Merge pull request #12095 from brendandahl/oc
Add support for optional marked content.
This commit is contained in:
commit
9989879458
@ -392,6 +392,14 @@ class PartialEvaluator {
|
|||||||
} else {
|
} else {
|
||||||
bbox = null;
|
bbox = null;
|
||||||
}
|
}
|
||||||
|
let optionalContent = null;
|
||||||
|
if (dict.has("OC")) {
|
||||||
|
optionalContent = await this.parseMarkedContentProps(
|
||||||
|
dict.get("OC"),
|
||||||
|
resources
|
||||||
|
);
|
||||||
|
operatorList.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]);
|
||||||
|
}
|
||||||
var group = dict.get("Group");
|
var group = dict.get("Group");
|
||||||
if (group) {
|
if (group) {
|
||||||
var groupOptions = {
|
var groupOptions = {
|
||||||
@ -449,6 +457,10 @@ class PartialEvaluator {
|
|||||||
if (group) {
|
if (group) {
|
||||||
operatorList.addOp(OPS.endGroup, [groupOptions]);
|
operatorList.addOp(OPS.endGroup, [groupOptions]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (optionalContent) {
|
||||||
|
operatorList.addOp(OPS.endMarkedContent, []);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1202,6 +1214,63 @@ class PartialEvaluator {
|
|||||||
throw new FormatError(`Unknown PatternName: ${patternName}`);
|
throw new FormatError(`Unknown PatternName: ${patternName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async parseMarkedContentProps(contentProperties, resources) {
|
||||||
|
let optionalContent;
|
||||||
|
if (isName(contentProperties)) {
|
||||||
|
const properties = resources.get("Properties");
|
||||||
|
optionalContent = properties.get(contentProperties.name);
|
||||||
|
} else if (isDict(contentProperties)) {
|
||||||
|
optionalContent = contentProperties;
|
||||||
|
} else {
|
||||||
|
throw new FormatError("Optional content properties malformed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const optionalContentType = optionalContent.get("Type").name;
|
||||||
|
if (optionalContentType === "OCG") {
|
||||||
|
return {
|
||||||
|
type: optionalContentType,
|
||||||
|
id: optionalContent.objId,
|
||||||
|
};
|
||||||
|
} else if (optionalContentType === "OCMD") {
|
||||||
|
const optionalContentGroups = optionalContent.get("OCGs");
|
||||||
|
if (
|
||||||
|
Array.isArray(optionalContentGroups) ||
|
||||||
|
isDict(optionalContentGroups)
|
||||||
|
) {
|
||||||
|
const groupIds = [];
|
||||||
|
if (Array.isArray(optionalContentGroups)) {
|
||||||
|
optionalContent.get("OCGs").forEach(ocg => {
|
||||||
|
groupIds.push(ocg.toString());
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Dictionary, just use the obj id.
|
||||||
|
groupIds.push(optionalContentGroups.objId);
|
||||||
|
}
|
||||||
|
|
||||||
|
let expression = null;
|
||||||
|
if (optionalContent.get("VE")) {
|
||||||
|
// TODO support visibility expression.
|
||||||
|
expression = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: optionalContentType,
|
||||||
|
ids: groupIds,
|
||||||
|
policy: isName(optionalContent.get("P"))
|
||||||
|
? optionalContent.get("P").name
|
||||||
|
: null,
|
||||||
|
expression,
|
||||||
|
};
|
||||||
|
} else if (isRef(optionalContentGroups)) {
|
||||||
|
return {
|
||||||
|
type: optionalContentType,
|
||||||
|
id: optionalContentGroups.toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
getOperatorList({
|
getOperatorList({
|
||||||
stream,
|
stream,
|
||||||
task,
|
task,
|
||||||
@ -1704,9 +1773,6 @@ class PartialEvaluator {
|
|||||||
continue;
|
continue;
|
||||||
case OPS.markPoint:
|
case OPS.markPoint:
|
||||||
case OPS.markPointProps:
|
case OPS.markPointProps:
|
||||||
case OPS.beginMarkedContent:
|
|
||||||
case OPS.beginMarkedContentProps:
|
|
||||||
case OPS.endMarkedContent:
|
|
||||||
case OPS.beginCompat:
|
case OPS.beginCompat:
|
||||||
case OPS.endCompat:
|
case OPS.endCompat:
|
||||||
// Ignore operators where the corresponding handlers are known to
|
// Ignore operators where the corresponding handlers are known to
|
||||||
@ -1716,6 +1782,45 @@ class PartialEvaluator {
|
|||||||
// e.g. as done in https://github.com/mozilla/pdf.js/pull/6266,
|
// e.g. as done in https://github.com/mozilla/pdf.js/pull/6266,
|
||||||
// but doing so is meaningless without knowing the semantics.
|
// but doing so is meaningless without knowing the semantics.
|
||||||
continue;
|
continue;
|
||||||
|
case OPS.beginMarkedContentProps:
|
||||||
|
if (!isName(args[0])) {
|
||||||
|
warn(`Expected name for beginMarkedContentProps arg0=${args[0]}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[0].name === "OC") {
|
||||||
|
next(
|
||||||
|
self
|
||||||
|
.parseMarkedContentProps(args[1], resources)
|
||||||
|
.then(data => {
|
||||||
|
operatorList.addOp(OPS.beginMarkedContentProps, [
|
||||||
|
"OC",
|
||||||
|
data,
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
.catch(reason => {
|
||||||
|
if (reason instanceof AbortException) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (self.options.ignoreErrors) {
|
||||||
|
self.handler.send("UnsupportedFeature", {
|
||||||
|
featureId: UNSUPPORTED_FEATURES.errorMarkedContent,
|
||||||
|
});
|
||||||
|
warn(
|
||||||
|
`getOperatorList - ignoring beginMarkedContentProps: "${reason}".`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw reason;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Other marked content types aren't supported yet.
|
||||||
|
args = [args[0].name];
|
||||||
|
|
||||||
|
break;
|
||||||
|
case OPS.beginMarkedContent:
|
||||||
|
case OPS.endMarkedContent:
|
||||||
default:
|
default:
|
||||||
// Note: Ignore the operator if it has `Dict` arguments, since
|
// Note: Ignore the operator if it has `Dict` arguments, since
|
||||||
// those are non-serializable, otherwise postMessage will throw
|
// those are non-serializable, otherwise postMessage will throw
|
||||||
|
@ -254,6 +254,82 @@ class Catalog {
|
|||||||
return permissions;
|
return permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get optionalContentConfig() {
|
||||||
|
let config = null;
|
||||||
|
try {
|
||||||
|
const properties = this.catDict.get("OCProperties");
|
||||||
|
if (!properties) {
|
||||||
|
return shadow(this, "optionalContentConfig", null);
|
||||||
|
}
|
||||||
|
const defaultConfig = properties.get("D");
|
||||||
|
if (!defaultConfig) {
|
||||||
|
return shadow(this, "optionalContentConfig", null);
|
||||||
|
}
|
||||||
|
const groupsData = properties.get("OCGs");
|
||||||
|
if (!Array.isArray(groupsData)) {
|
||||||
|
return shadow(this, "optionalContentConfig", null);
|
||||||
|
}
|
||||||
|
const groups = [];
|
||||||
|
const groupRefs = [];
|
||||||
|
// Ensure all the optional content groups are valid.
|
||||||
|
for (const groupRef of groupsData) {
|
||||||
|
if (!isRef(groupRef)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
groupRefs.push(groupRef);
|
||||||
|
const group = this.xref.fetchIfRef(groupRef);
|
||||||
|
groups.push({
|
||||||
|
id: groupRef.toString(),
|
||||||
|
name: isString(group.get("Name"))
|
||||||
|
? stringToPDFString(group.get("Name"))
|
||||||
|
: null,
|
||||||
|
intent: isString(group.get("Intent"))
|
||||||
|
? stringToPDFString(group.get("Intent"))
|
||||||
|
: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
config = this._readOptionalContentConfig(defaultConfig, groupRefs);
|
||||||
|
config.groups = groups;
|
||||||
|
} catch (ex) {
|
||||||
|
if (ex instanceof MissingDataException) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
warn(`Unable to read optional content config: ${ex}`);
|
||||||
|
}
|
||||||
|
return shadow(this, "optionalContentConfig", config);
|
||||||
|
}
|
||||||
|
|
||||||
|
_readOptionalContentConfig(config, contentGroupRefs) {
|
||||||
|
function parseOnOff(refs) {
|
||||||
|
const onParsed = [];
|
||||||
|
if (Array.isArray(refs)) {
|
||||||
|
for (const value of refs) {
|
||||||
|
if (!isRef(value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (contentGroupRefs.includes(value)) {
|
||||||
|
onParsed.push(value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return onParsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: isString(config.get("Name"))
|
||||||
|
? stringToPDFString(config.get("Name"))
|
||||||
|
: null,
|
||||||
|
creator: isString(config.get("Creator"))
|
||||||
|
? stringToPDFString(config.get("Creator"))
|
||||||
|
: null,
|
||||||
|
baseState: isName(config.get("BaseState"))
|
||||||
|
? config.get("BaseState").name
|
||||||
|
: null,
|
||||||
|
on: parseOnOff(config.get("ON")),
|
||||||
|
off: parseOnOff(config.get("OFF")),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
get numPages() {
|
get numPages() {
|
||||||
const obj = this.toplevelPagesDict.get("Count");
|
const obj = this.toplevelPagesDict.get("Count");
|
||||||
if (!Number.isInteger(obj)) {
|
if (!Number.isInteger(obj)) {
|
||||||
|
@ -481,6 +481,10 @@ class WorkerMessageHandler {
|
|||||||
return pdfManager.ensureCatalog("documentOutline");
|
return pdfManager.ensureCatalog("documentOutline");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
handler.on("GetOptionalContentConfig", function (data) {
|
||||||
|
return pdfManager.ensureCatalog("optionalContentConfig");
|
||||||
|
});
|
||||||
|
|
||||||
handler.on("GetPermissions", function (data) {
|
handler.on("GetPermissions", function (data) {
|
||||||
return pdfManager.ensureCatalog("permissions");
|
return pdfManager.ensureCatalog("permissions");
|
||||||
});
|
});
|
||||||
|
@ -55,6 +55,7 @@ import { GlobalWorkerOptions } from "./worker_options.js";
|
|||||||
import { isNodeJS } from "../shared/is_node.js";
|
import { isNodeJS } from "../shared/is_node.js";
|
||||||
import { MessageHandler } from "../shared/message_handler.js";
|
import { MessageHandler } from "../shared/message_handler.js";
|
||||||
import { Metadata } from "./metadata.js";
|
import { Metadata } from "./metadata.js";
|
||||||
|
import { OptionalContentConfig } from "./optional_content_config.js";
|
||||||
import { PDFDataTransportStream } from "./transport_stream.js";
|
import { PDFDataTransportStream } from "./transport_stream.js";
|
||||||
import { WebGLContext } from "./webgl.js";
|
import { WebGLContext } from "./webgl.js";
|
||||||
|
|
||||||
@ -788,6 +789,15 @@ class PDFDocumentProxy {
|
|||||||
return this._transport.getOutline();
|
return this._transport.getOutline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Promise<OptionalContentConfig | null>} A promise that is resolved
|
||||||
|
* with an {@link OptionalContentConfig} that will have all the optional
|
||||||
|
* content groups (if the document has any).
|
||||||
|
*/
|
||||||
|
getOptionalContentConfig() {
|
||||||
|
return this._transport.getOptionalContentConfig();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {Promise<Array<string | null>>} A promise that is resolved with
|
* @returns {Promise<Array<string | null>>} A promise that is resolved with
|
||||||
* an {Array} that contains the permission flags for the PDF document, or
|
* an {Array} that contains the permission flags for the PDF document, or
|
||||||
@ -965,6 +975,11 @@ class PDFDocumentProxy {
|
|||||||
* image). The default value is 'rgb(255,255,255)'.
|
* image). The default value is 'rgb(255,255,255)'.
|
||||||
* @property {Object} [annotationStorage] - Storage for annotation data in
|
* @property {Object} [annotationStorage] - Storage for annotation data in
|
||||||
* forms.
|
* forms.
|
||||||
|
* @property {Promise} [optionalContentConfigPromise] - A promise that should
|
||||||
|
* resolve with an {OptionalContentConfig} created from
|
||||||
|
* PDFDocumentProxy.getOptionalContentConfig. If null, the
|
||||||
|
* config will be automatically fetched with the default
|
||||||
|
* visibility states set.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1088,6 +1103,7 @@ class PDFPageProxy {
|
|||||||
canvasFactory = null,
|
canvasFactory = null,
|
||||||
background = null,
|
background = null,
|
||||||
annotationStorage = null,
|
annotationStorage = null,
|
||||||
|
optionalContentConfigPromise = null,
|
||||||
}) {
|
}) {
|
||||||
if (this._stats) {
|
if (this._stats) {
|
||||||
this._stats.time("Overall");
|
this._stats.time("Overall");
|
||||||
@ -1098,6 +1114,10 @@ class PDFPageProxy {
|
|||||||
// this call to render.
|
// this call to render.
|
||||||
this.pendingCleanup = false;
|
this.pendingCleanup = false;
|
||||||
|
|
||||||
|
if (!optionalContentConfigPromise) {
|
||||||
|
optionalContentConfigPromise = this._transport.getOptionalContentConfig();
|
||||||
|
}
|
||||||
|
|
||||||
let intentState = this._intentStates.get(renderingIntent);
|
let intentState = this._intentStates.get(renderingIntent);
|
||||||
if (!intentState) {
|
if (!intentState) {
|
||||||
intentState = Object.create(null);
|
intentState = Object.create(null);
|
||||||
@ -1191,8 +1211,11 @@ class PDFPageProxy {
|
|||||||
intentState.renderTasks.push(internalRenderTask);
|
intentState.renderTasks.push(internalRenderTask);
|
||||||
const renderTask = internalRenderTask.task;
|
const renderTask = internalRenderTask.task;
|
||||||
|
|
||||||
intentState.displayReadyCapability.promise
|
Promise.all([
|
||||||
.then(transparency => {
|
intentState.displayReadyCapability.promise,
|
||||||
|
optionalContentConfigPromise,
|
||||||
|
])
|
||||||
|
.then(([transparency, optionalContentConfig]) => {
|
||||||
if (this.pendingCleanup) {
|
if (this.pendingCleanup) {
|
||||||
complete();
|
complete();
|
||||||
return;
|
return;
|
||||||
@ -1200,7 +1223,10 @@ class PDFPageProxy {
|
|||||||
if (this._stats) {
|
if (this._stats) {
|
||||||
this._stats.time("Rendering");
|
this._stats.time("Rendering");
|
||||||
}
|
}
|
||||||
internalRenderTask.initializeGraphics(transparency);
|
internalRenderTask.initializeGraphics({
|
||||||
|
transparency,
|
||||||
|
optionalContentConfig,
|
||||||
|
});
|
||||||
internalRenderTask.operatorListChanged();
|
internalRenderTask.operatorListChanged();
|
||||||
})
|
})
|
||||||
.catch(complete);
|
.catch(complete);
|
||||||
@ -2546,6 +2572,14 @@ class WorkerTransport {
|
|||||||
return this.messageHandler.sendWithPromise("GetOutline", null);
|
return this.messageHandler.sendWithPromise("GetOutline", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOptionalContentConfig() {
|
||||||
|
return this.messageHandler
|
||||||
|
.sendWithPromise("GetOptionalContentConfig", null)
|
||||||
|
.then(results => {
|
||||||
|
return new OptionalContentConfig(results);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getPermissions() {
|
getPermissions() {
|
||||||
return this.messageHandler.sendWithPromise("GetPermissions", null);
|
return this.messageHandler.sendWithPromise("GetPermissions", null);
|
||||||
}
|
}
|
||||||
@ -2759,7 +2793,7 @@ const InternalRenderTask = (function InternalRenderTaskClosure() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeGraphics(transparency = false) {
|
initializeGraphics({ transparency = false, optionalContentConfig }) {
|
||||||
if (this.cancelled) {
|
if (this.cancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2797,7 +2831,8 @@ const InternalRenderTask = (function InternalRenderTaskClosure() {
|
|||||||
this.objs,
|
this.objs,
|
||||||
this.canvasFactory,
|
this.canvasFactory,
|
||||||
this.webGLContext,
|
this.webGLContext,
|
||||||
imageLayer
|
imageLayer,
|
||||||
|
optionalContentConfig
|
||||||
);
|
);
|
||||||
this.gfx.beginDrawing({
|
this.gfx.beginDrawing({
|
||||||
transform,
|
transform,
|
||||||
|
@ -447,7 +447,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
objs,
|
objs,
|
||||||
canvasFactory,
|
canvasFactory,
|
||||||
webGLContext,
|
webGLContext,
|
||||||
imageLayer
|
imageLayer,
|
||||||
|
optionalContentConfig
|
||||||
) {
|
) {
|
||||||
this.ctx = canvasCtx;
|
this.ctx = canvasCtx;
|
||||||
this.current = new CanvasExtraState();
|
this.current = new CanvasExtraState();
|
||||||
@ -471,6 +472,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
this.smaskStack = [];
|
this.smaskStack = [];
|
||||||
this.smaskCounter = 0;
|
this.smaskCounter = 0;
|
||||||
this.tempSMask = null;
|
this.tempSMask = null;
|
||||||
|
this.contentVisible = true;
|
||||||
|
this.markedContentStack = [];
|
||||||
|
this.optionalContentConfig = optionalContentConfig;
|
||||||
this.cachedCanvases = new CachedCanvases(this.canvasFactory);
|
this.cachedCanvases = new CachedCanvases(this.canvasFactory);
|
||||||
if (canvasCtx) {
|
if (canvasCtx) {
|
||||||
// NOTE: if mozCurrentTransform is polyfilled, then the current state of
|
// NOTE: if mozCurrentTransform is polyfilled, then the current state of
|
||||||
@ -1262,6 +1266,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
// For stroke we want to temporarily change the global alpha to the
|
// For stroke we want to temporarily change the global alpha to the
|
||||||
// stroking alpha.
|
// stroking alpha.
|
||||||
ctx.globalAlpha = this.current.strokeAlpha;
|
ctx.globalAlpha = this.current.strokeAlpha;
|
||||||
|
if (this.contentVisible) {
|
||||||
if (
|
if (
|
||||||
strokeColor &&
|
strokeColor &&
|
||||||
strokeColor.hasOwnProperty("type") &&
|
strokeColor.hasOwnProperty("type") &&
|
||||||
@ -1271,9 +1276,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
// the pattern, call stroke, and restore to user space
|
// the pattern, call stroke, and restore to user space
|
||||||
ctx.save();
|
ctx.save();
|
||||||
// The current transform will be replaced while building the pattern,
|
// The current transform will be replaced while building the pattern,
|
||||||
// but the line width needs to be adjusted by the current transform, so
|
// but the line width needs to be adjusted by the current transform,
|
||||||
// we must scale it. To properly fix this we should be using a pattern
|
// so we must scale it. To properly fix this we should be using a
|
||||||
// transform instead (see #10955).
|
// pattern transform instead (see #10955).
|
||||||
const transform = ctx.mozCurrentTransform;
|
const transform = ctx.mozCurrentTransform;
|
||||||
const scale = Util.singularValueDecompose2dScale(transform)[0];
|
const scale = Util.singularValueDecompose2dScale(transform)[0];
|
||||||
ctx.strokeStyle = strokeColor.getPattern(ctx, this);
|
ctx.strokeStyle = strokeColor.getPattern(ctx, this);
|
||||||
@ -1291,6 +1296,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
);
|
);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (consumePath) {
|
if (consumePath) {
|
||||||
this.consumePath();
|
this.consumePath();
|
||||||
}
|
}
|
||||||
@ -1317,12 +1323,14 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
needRestore = true;
|
needRestore = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.contentVisible) {
|
||||||
if (this.pendingEOFill) {
|
if (this.pendingEOFill) {
|
||||||
ctx.fill("evenodd");
|
ctx.fill("evenodd");
|
||||||
this.pendingEOFill = false;
|
this.pendingEOFill = false;
|
||||||
} else {
|
} else {
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (needRestore) {
|
if (needRestore) {
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
@ -1700,7 +1708,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
|
|
||||||
// Only attempt to draw the glyph if it is actually in the embedded font
|
// Only attempt to draw the glyph if it is actually in the embedded font
|
||||||
// file or if there isn't a font file so the fallback font is shown.
|
// file or if there isn't a font file so the fallback font is shown.
|
||||||
if (glyph.isInFont || font.missingFile) {
|
if (this.contentVisible && (glyph.isInFont || font.missingFile)) {
|
||||||
if (simpleFillText && !accent) {
|
if (simpleFillText && !accent) {
|
||||||
// common case
|
// common case
|
||||||
ctx.fillText(character, scaledX, scaledY);
|
ctx.fillText(character, scaledX, scaledY);
|
||||||
@ -1782,12 +1790,14 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
warn(`Type3 character "${glyph.operatorListId}" is not available.`);
|
warn(`Type3 character "${glyph.operatorListId}" is not available.`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (this.contentVisible) {
|
||||||
this.processingType3 = glyph;
|
this.processingType3 = glyph;
|
||||||
this.save();
|
this.save();
|
||||||
ctx.scale(fontSize, fontSize);
|
ctx.scale(fontSize, fontSize);
|
||||||
ctx.transform.apply(ctx, fontMatrix);
|
ctx.transform.apply(ctx, fontMatrix);
|
||||||
this.executeOperatorList(operatorList);
|
this.executeOperatorList(operatorList);
|
||||||
this.restore();
|
this.restore();
|
||||||
|
}
|
||||||
|
|
||||||
var transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
|
var transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
|
||||||
width = transformed[0] * fontSize + spacing;
|
width = transformed[0] * fontSize + spacing;
|
||||||
@ -1869,6 +1879,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
shadingFill: function CanvasGraphics_shadingFill(patternIR) {
|
shadingFill: function CanvasGraphics_shadingFill(patternIR) {
|
||||||
|
if (!this.contentVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var ctx = this.ctx;
|
var ctx = this.ctx;
|
||||||
|
|
||||||
this.save();
|
this.save();
|
||||||
@ -1917,6 +1930,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
matrix,
|
matrix,
|
||||||
bbox
|
bbox
|
||||||
) {
|
) {
|
||||||
|
if (!this.contentVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.save();
|
this.save();
|
||||||
this.baseTransformStack.push(this.baseTransform);
|
this.baseTransformStack.push(this.baseTransform);
|
||||||
|
|
||||||
@ -1936,11 +1952,18 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
paintFormXObjectEnd: function CanvasGraphics_paintFormXObjectEnd() {
|
paintFormXObjectEnd: function CanvasGraphics_paintFormXObjectEnd() {
|
||||||
|
if (!this.contentVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.restore();
|
this.restore();
|
||||||
this.baseTransform = this.baseTransformStack.pop();
|
this.baseTransform = this.baseTransformStack.pop();
|
||||||
},
|
},
|
||||||
|
|
||||||
beginGroup: function CanvasGraphics_beginGroup(group) {
|
beginGroup: function CanvasGraphics_beginGroup(group) {
|
||||||
|
if (!this.contentVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.save();
|
this.save();
|
||||||
var currentCtx = this.ctx;
|
var currentCtx = this.ctx;
|
||||||
// TODO non-isolated groups - according to Rik at adobe non-isolated
|
// TODO non-isolated groups - according to Rik at adobe non-isolated
|
||||||
@ -2062,6 +2085,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
endGroup: function CanvasGraphics_endGroup(group) {
|
endGroup: function CanvasGraphics_endGroup(group) {
|
||||||
|
if (!this.contentVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.groupLevel--;
|
this.groupLevel--;
|
||||||
var groupCtx = this.ctx;
|
var groupCtx = this.ctx;
|
||||||
this.ctx = this.groupStack.pop();
|
this.ctx = this.groupStack.pop();
|
||||||
@ -2117,6 +2143,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
paintImageMaskXObject: function CanvasGraphics_paintImageMaskXObject(img) {
|
paintImageMaskXObject: function CanvasGraphics_paintImageMaskXObject(img) {
|
||||||
|
if (!this.contentVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var ctx = this.ctx;
|
var ctx = this.ctx;
|
||||||
var width = img.width,
|
var width = img.width,
|
||||||
height = img.height;
|
height = img.height;
|
||||||
@ -2168,6 +2197,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
scaleY,
|
scaleY,
|
||||||
positions
|
positions
|
||||||
) {
|
) {
|
||||||
|
if (!this.contentVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var width = imgData.width;
|
var width = imgData.width;
|
||||||
var height = imgData.height;
|
var height = imgData.height;
|
||||||
var fillColor = this.current.fillColor;
|
var fillColor = this.current.fillColor;
|
||||||
@ -2212,6 +2244,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
paintImageMaskXObjectGroup: function CanvasGraphics_paintImageMaskXObjectGroup(
|
paintImageMaskXObjectGroup: function CanvasGraphics_paintImageMaskXObjectGroup(
|
||||||
images
|
images
|
||||||
) {
|
) {
|
||||||
|
if (!this.contentVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var ctx = this.ctx;
|
var ctx = this.ctx;
|
||||||
|
|
||||||
var fillColor = this.current.fillColor;
|
var fillColor = this.current.fillColor;
|
||||||
@ -2249,6 +2284,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
paintImageXObject: function CanvasGraphics_paintImageXObject(objId) {
|
paintImageXObject: function CanvasGraphics_paintImageXObject(objId) {
|
||||||
|
if (!this.contentVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const imgData = objId.startsWith("g_")
|
const imgData = objId.startsWith("g_")
|
||||||
? this.commonObjs.get(objId)
|
? this.commonObjs.get(objId)
|
||||||
: this.objs.get(objId);
|
: this.objs.get(objId);
|
||||||
@ -2266,6 +2304,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
scaleY,
|
scaleY,
|
||||||
positions
|
positions
|
||||||
) {
|
) {
|
||||||
|
if (!this.contentVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const imgData = objId.startsWith("g_")
|
const imgData = objId.startsWith("g_")
|
||||||
? this.commonObjs.get(objId)
|
? this.commonObjs.get(objId)
|
||||||
: this.objs.get(objId);
|
: this.objs.get(objId);
|
||||||
@ -2292,6 +2333,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
paintInlineImageXObject: function CanvasGraphics_paintInlineImageXObject(
|
paintInlineImageXObject: function CanvasGraphics_paintInlineImageXObject(
|
||||||
imgData
|
imgData
|
||||||
) {
|
) {
|
||||||
|
if (!this.contentVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var width = imgData.width;
|
var width = imgData.width;
|
||||||
var height = imgData.height;
|
var height = imgData.height;
|
||||||
var ctx = this.ctx;
|
var ctx = this.ctx;
|
||||||
@ -2394,6 +2438,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
imgData,
|
imgData,
|
||||||
map
|
map
|
||||||
) {
|
) {
|
||||||
|
if (!this.contentVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var ctx = this.ctx;
|
var ctx = this.ctx;
|
||||||
var w = imgData.width;
|
var w = imgData.width;
|
||||||
var h = imgData.height;
|
var h = imgData.height;
|
||||||
@ -2433,6 +2480,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
paintSolidColorImageMask: function CanvasGraphics_paintSolidColorImageMask() {
|
paintSolidColorImageMask: function CanvasGraphics_paintSolidColorImageMask() {
|
||||||
|
if (!this.contentVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.ctx.fillRect(0, 0, 1, 1);
|
this.ctx.fillRect(0, 0, 1, 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2445,16 +2495,28 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
// TODO Marked content.
|
// TODO Marked content.
|
||||||
},
|
},
|
||||||
beginMarkedContent: function CanvasGraphics_beginMarkedContent(tag) {
|
beginMarkedContent: function CanvasGraphics_beginMarkedContent(tag) {
|
||||||
// TODO Marked content.
|
this.markedContentStack.push({
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
beginMarkedContentProps: function CanvasGraphics_beginMarkedContentProps(
|
beginMarkedContentProps: function CanvasGraphics_beginMarkedContentProps(
|
||||||
tag,
|
tag,
|
||||||
properties
|
properties
|
||||||
) {
|
) {
|
||||||
// TODO Marked content.
|
if (tag === "OC") {
|
||||||
|
this.markedContentStack.push({
|
||||||
|
visible: this.optionalContentConfig.isVisible(properties),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.markedContentStack.push({
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.contentVisible = this.isContentVisible();
|
||||||
},
|
},
|
||||||
endMarkedContent: function CanvasGraphics_endMarkedContent() {
|
endMarkedContent: function CanvasGraphics_endMarkedContent() {
|
||||||
// TODO Marked content.
|
this.markedContentStack.pop();
|
||||||
|
this.contentVisible = this.isContentVisible();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Compatibility
|
// Compatibility
|
||||||
@ -2500,6 +2562,15 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
transform[1] * x + transform[3] * y + transform[5],
|
transform[1] * x + transform[3] * y + transform[5],
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isContentVisible: function CanvasGraphics_isContentVisible() {
|
||||||
|
for (let i = this.markedContentStack.length - 1; i >= 0; i--) {
|
||||||
|
if (!this.markedContentStack[i].visible) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var op in OPS) {
|
for (var op in OPS) {
|
||||||
|
125
src/display/optional_content_config.js
Normal file
125
src/display/optional_content_config.js
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/* Copyright 2020 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import { warn } from "../shared/util.js";
|
||||||
|
|
||||||
|
class OptionalContentGroup {
|
||||||
|
constructor(name, intent) {
|
||||||
|
this.visible = true;
|
||||||
|
this.name = name;
|
||||||
|
this.intent = intent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OptionalContentConfig {
|
||||||
|
constructor(data) {
|
||||||
|
this.name = null;
|
||||||
|
this.creator = null;
|
||||||
|
this.groups = new Map();
|
||||||
|
|
||||||
|
if (data === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.name = data.name;
|
||||||
|
this.creator = data.creator;
|
||||||
|
for (const group of data.groups) {
|
||||||
|
this.groups.set(
|
||||||
|
group.id,
|
||||||
|
new OptionalContentGroup(group.name, group.intent)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.baseState === "OFF") {
|
||||||
|
for (const group of this.groups) {
|
||||||
|
group.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const on of data.on) {
|
||||||
|
this.groups.get(on).visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const off of data.off) {
|
||||||
|
this.groups.get(off).visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isVisible(group) {
|
||||||
|
if (group.type === "OCG") {
|
||||||
|
if (!this.groups.has(group.id)) {
|
||||||
|
warn(`Optional content group not found: ${group.id}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return this.groups.get(group.id).visible;
|
||||||
|
} else if (group.type === "OCMD") {
|
||||||
|
// Per the spec, the expression should be preferred if available. Until
|
||||||
|
// we implement this, just fallback to using the group policy for now.
|
||||||
|
if (group.expression) {
|
||||||
|
warn("Visibility expression not supported yet.");
|
||||||
|
}
|
||||||
|
if (!group.policy || group.policy === "AnyOn") {
|
||||||
|
// Default
|
||||||
|
for (const id of group.ids) {
|
||||||
|
if (!this.groups.has(id)) {
|
||||||
|
warn(`Optional content group not found: ${id}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this.groups.get(id).visible) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else if (group.policy === "AllOn") {
|
||||||
|
for (const id of group.ids) {
|
||||||
|
if (!this.groups.has(id)) {
|
||||||
|
warn(`Optional content group not found: ${id}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!this.groups.get(id).visible) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (group.policy === "AnyOff") {
|
||||||
|
for (const id of group.ids) {
|
||||||
|
if (!this.groups.has(id)) {
|
||||||
|
warn(`Optional content group not found: ${id}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!this.groups.get(id).visible) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else if (group.policy === "AllOff") {
|
||||||
|
for (const id of group.ids) {
|
||||||
|
if (!this.groups.has(id)) {
|
||||||
|
warn(`Optional content group not found: ${id}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this.groups.get(id).visible) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
warn(`Unknown optional content policy ${group.policy}.`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
warn(`Unknown group type ${group.type}.`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { OptionalContentConfig };
|
@ -302,6 +302,7 @@ const UNSUPPORTED_FEATURES = {
|
|||||||
errorFontToUnicode: "errorFontToUnicode",
|
errorFontToUnicode: "errorFontToUnicode",
|
||||||
errorFontLoadNative: "errorFontLoadNative",
|
errorFontLoadNative: "errorFontLoadNative",
|
||||||
errorFontGetPath: "errorFontGetPath",
|
errorFontGetPath: "errorFontGetPath",
|
||||||
|
errorMarkedContent: "errorMarkedContent",
|
||||||
};
|
};
|
||||||
|
|
||||||
const PasswordResponses = {
|
const PasswordResponses = {
|
||||||
|
4
test/pdfs/.gitignore
vendored
4
test/pdfs/.gitignore
vendored
@ -52,6 +52,7 @@
|
|||||||
!issue7835.pdf
|
!issue7835.pdf
|
||||||
!issue11922_reduced.pdf
|
!issue11922_reduced.pdf
|
||||||
!issue7855.pdf
|
!issue7855.pdf
|
||||||
|
!issue11144_reduced.pdf
|
||||||
!issue7872.pdf
|
!issue7872.pdf
|
||||||
!issue7901.pdf
|
!issue7901.pdf
|
||||||
!issue8061.pdf
|
!issue8061.pdf
|
||||||
@ -296,6 +297,7 @@
|
|||||||
!issue3371.pdf
|
!issue3371.pdf
|
||||||
!issue2956.pdf
|
!issue2956.pdf
|
||||||
!issue2537r.pdf
|
!issue2537r.pdf
|
||||||
|
!issue269_1.pdf
|
||||||
!bug946506.pdf
|
!bug946506.pdf
|
||||||
!issue3885.pdf
|
!issue3885.pdf
|
||||||
!issue11697_reduced.pdf
|
!issue11697_reduced.pdf
|
||||||
@ -331,6 +333,7 @@
|
|||||||
!issue5481.pdf
|
!issue5481.pdf
|
||||||
!issue5567.pdf
|
!issue5567.pdf
|
||||||
!issue5701.pdf
|
!issue5701.pdf
|
||||||
|
!issue12007_reduced.pdf
|
||||||
!issue5896.pdf
|
!issue5896.pdf
|
||||||
!issue6010_1.pdf
|
!issue6010_1.pdf
|
||||||
!issue6010_2.pdf
|
!issue6010_2.pdf
|
||||||
@ -352,6 +355,7 @@
|
|||||||
!issue9278.pdf
|
!issue9278.pdf
|
||||||
!annotation-text-without-popup.pdf
|
!annotation-text-without-popup.pdf
|
||||||
!annotation-underline.pdf
|
!annotation-underline.pdf
|
||||||
|
!issue269_2.pdf
|
||||||
!annotation-strikeout.pdf
|
!annotation-strikeout.pdf
|
||||||
!annotation-squiggly.pdf
|
!annotation-squiggly.pdf
|
||||||
!annotation-highlight.pdf
|
!annotation-highlight.pdf
|
||||||
|
BIN
test/pdfs/issue11144_reduced.pdf
Normal file
BIN
test/pdfs/issue11144_reduced.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/issue12007_reduced.pdf
Normal file
BIN
test/pdfs/issue12007_reduced.pdf
Normal file
Binary file not shown.
676
test/pdfs/issue269_1.pdf
Normal file
676
test/pdfs/issue269_1.pdf
Normal file
File diff suppressed because one or more lines are too long
BIN
test/pdfs/issue269_2.pdf
Normal file
BIN
test/pdfs/issue269_2.pdf
Normal file
Binary file not shown.
@ -926,6 +926,34 @@
|
|||||||
"link": false,
|
"link": false,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{ "id": "issue269_1",
|
||||||
|
"file": "pdfs/issue269_1.pdf",
|
||||||
|
"md5": "ab932f697b4d2e2bf700de15a8efea9c",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"about": "Optional marked content."
|
||||||
|
},
|
||||||
|
{ "id": "issue269_2",
|
||||||
|
"file": "pdfs/issue269_2.pdf",
|
||||||
|
"md5": "0f553510850ee17c87fbab3fac564165",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"about": "Optional marked content."
|
||||||
|
},
|
||||||
|
{ "id": "issue11144_reduced",
|
||||||
|
"file": "pdfs/issue11144_reduced.pdf",
|
||||||
|
"md5": "09e3e771ebd6867558074e900adb54b9",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"about": "Optional marked content."
|
||||||
|
},
|
||||||
|
{ "id": "issue12007_reduced",
|
||||||
|
"file": "pdfs/issue12007_reduced.pdf",
|
||||||
|
"md5": "3aa9d8a0c5ff8594245149f9c7379613",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"about": "Optional marked content."
|
||||||
|
},
|
||||||
{ "id": "issue10438",
|
{ "id": "issue10438",
|
||||||
"file": "pdfs/issue10438_reduced.pdf",
|
"file": "pdfs/issue10438_reduced.pdf",
|
||||||
"md5": "bb26f68493e33af17b256a6ffe777a24",
|
"md5": "bb26f68493e33af17b256a6ffe777a24",
|
||||||
|
@ -1636,8 +1636,8 @@ describe("api", function () {
|
|||||||
const result1 = loadingTask1.promise.then(pdfDoc => {
|
const result1 = loadingTask1.promise.then(pdfDoc => {
|
||||||
return pdfDoc.getPage(1).then(pdfPage => {
|
return pdfDoc.getPage(1).then(pdfPage => {
|
||||||
return pdfPage.getOperatorList().then(opList => {
|
return pdfPage.getOperatorList().then(opList => {
|
||||||
expect(opList.fnArray.length).toEqual(722);
|
expect(opList.fnArray.length).toBeGreaterThan(100);
|
||||||
expect(opList.argsArray.length).toEqual(722);
|
expect(opList.argsArray.length).toBeGreaterThan(100);
|
||||||
expect(opList.lastChunk).toEqual(true);
|
expect(opList.lastChunk).toEqual(true);
|
||||||
|
|
||||||
return loadingTask1.destroy();
|
return loadingTask1.destroy();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user