Merge pull request #13867 from Snuffleupagus/RenderingIntentFlag

[api-minor] Re-factor the *internal* renderingIntent, and change the default `intent` value in the `PDFPageProxy.getAnnotations` method
This commit is contained in:
Tim van der Meij 2021-08-07 19:25:51 +02:00 committed by GitHub
commit 952f6366bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 96 additions and 43 deletions

View File

@ -25,6 +25,7 @@ import {
isString, isString,
OPS, OPS,
PageActionEventType, PageActionEventType,
RenderingIntentFlag,
shadow, shadow,
stringToBytes, stringToBytes,
stringToPDFString, stringToPDFString,
@ -319,14 +320,7 @@ class Page {
}); });
} }
getOperatorList({ getOperatorList({ handler, sink, task, intent, annotationStorage }) {
handler,
sink,
task,
intent,
renderInteractiveForms,
annotationStorage,
}) {
const contentStreamPromise = this.getContentStream(handler); const contentStreamPromise = this.getContentStream(handler);
const resourcesPromise = this.loadResources([ const resourcesPromise = this.loadResources([
"ColorSpace", "ColorSpace",
@ -383,26 +377,26 @@ class Page {
pageOpList.flush(true); pageOpList.flush(true);
return { length: pageOpList.totalLength }; return { length: pageOpList.totalLength };
} }
const renderForms = !!(intent & RenderingIntentFlag.ANNOTATION_FORMS),
intentAny = !!(intent & RenderingIntentFlag.ANY),
intentDisplay = !!(intent & RenderingIntentFlag.DISPLAY),
intentPrint = !!(intent & RenderingIntentFlag.PRINT);
// Collect the operator list promises for the annotations. Each promise // Collect the operator list promises for the annotations. Each promise
// is resolved with the complete operator list for a single annotation. // is resolved with the complete operator list for a single annotation.
const annotationIntent = intent.startsWith("oplist-")
? intent.split("-")[1]
: intent;
const opListPromises = []; const opListPromises = [];
for (const annotation of annotations) { for (const annotation of annotations) {
if ( if (
(annotationIntent === "display" && intentAny ||
annotation.mustBeViewed(annotationStorage)) || (intentDisplay && annotation.mustBeViewed(annotationStorage)) ||
(annotationIntent === "print" && (intentPrint && annotation.mustBePrinted(annotationStorage))
annotation.mustBePrinted(annotationStorage))
) { ) {
opListPromises.push( opListPromises.push(
annotation annotation
.getOperatorList( .getOperatorList(
partialEvaluator, partialEvaluator,
task, task,
renderInteractiveForms, renderForms,
annotationStorage annotationStorage
) )
.catch(function (reason) { .catch(function (reason) {
@ -496,15 +490,23 @@ class Page {
getAnnotationsData(intent) { getAnnotationsData(intent) {
return this._parsedAnnotations.then(function (annotations) { return this._parsedAnnotations.then(function (annotations) {
const annotationsData = []; const annotationsData = [];
for (let i = 0, ii = annotations.length; i < ii; i++) {
if (annotations.length === 0) {
return annotationsData;
}
const intentAny = !!(intent & RenderingIntentFlag.ANY),
intentDisplay = !!(intent & RenderingIntentFlag.DISPLAY),
intentPrint = !!(intent & RenderingIntentFlag.PRINT);
for (const annotation of annotations) {
// Get the annotation even if it's hidden because // Get the annotation even if it's hidden because
// JS can change its display. // JS can change its display.
if ( if (
!intent || intentAny ||
(intent === "display" && annotations[i].viewable) || (intentDisplay && annotation.viewable) ||
(intent === "print" && annotations[i].printable) (intentPrint && annotation.printable)
) { ) {
annotationsData.push(annotations[i].data); annotationsData.push(annotation.data);
} }
} }
return annotationsData; return annotationsData;

View File

@ -13,7 +13,14 @@
* limitations under the License. * limitations under the License.
*/ */
import { assert, ImageKind, OPS, shadow, warn } from "../shared/util.js"; import {
assert,
ImageKind,
OPS,
RenderingIntentFlag,
shadow,
warn,
} from "../shared/util.js";
function addState(parentState, pattern, checkFn, iterateFn, processFn) { function addState(parentState, pattern, checkFn, iterateFn, processFn) {
let state = parentState; let state = parentState;
@ -597,11 +604,11 @@ class OperatorList {
return shadow(this, "CHUNK_SIZE_ABOUT", this.CHUNK_SIZE - 5); return shadow(this, "CHUNK_SIZE_ABOUT", this.CHUNK_SIZE - 5);
} }
constructor(intent, streamSink) { constructor(intent = 0, streamSink) {
this._streamSink = streamSink; this._streamSink = streamSink;
this.fnArray = []; this.fnArray = [];
this.argsArray = []; this.argsArray = [];
if (streamSink && !(intent && intent.startsWith("oplist-"))) { if (streamSink && !(intent & RenderingIntentFlag.OPLIST)) {
this.optimizer = new QueueOptimizer(this); this.optimizer = new QueueOptimizer(this);
} else { } else {
this.optimizer = new NullOptimizer(this); this.optimizer = new NullOptimizer(this);

View File

@ -688,7 +688,6 @@ class WorkerMessageHandler {
sink, sink,
task, task,
intent: data.intent, intent: data.intent,
renderInteractiveForms: data.renderInteractiveForms,
annotationStorage: data.annotationStorage, annotationStorage: data.annotationStorage,
}) })
.then( .then(

View File

@ -28,6 +28,7 @@ import {
isSameOrigin, isSameOrigin,
MissingPDFException, MissingPDFException,
PasswordException, PasswordException,
RenderingIntentFlag,
setVerbosityLevel, setVerbosityLevel,
shadow, shadow,
stringToBytes, stringToBytes,
@ -514,6 +515,29 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
}); });
} }
function getRenderingIntent(intent, { renderForms = false, isOpList = false }) {
let renderingIntent = RenderingIntentFlag.DISPLAY; // Default value.
switch (intent) {
case "any":
renderingIntent = RenderingIntentFlag.ANY;
break;
case "display":
break;
case "print":
renderingIntent = RenderingIntentFlag.PRINT;
break;
default:
warn(`getRenderingIntent - invalid intent: ${intent}`);
}
if (renderForms) {
renderingIntent += RenderingIntentFlag.ANNOTATION_FORMS;
}
if (isOpList) {
renderingIntent += RenderingIntentFlag.OPLIST;
}
return renderingIntent;
}
/** /**
* @typedef {Object} OnProgressParameters * @typedef {Object} OnProgressParameters
* @property {number} loaded - Currently loaded number of bytes. * @property {number} loaded - Currently loaded number of bytes.
@ -1120,8 +1144,8 @@ class PDFDocumentProxy {
* *
* @typedef {Object} GetAnnotationsParameters * @typedef {Object} GetAnnotationsParameters
* @property {string} [intent] - Determines the annotations that are fetched, * @property {string} [intent] - Determines the annotations that are fetched,
* can be either 'display' (viewable annotations) or 'print' (printable * can be 'display' (viewable annotations), 'print' (printable annotations),
* annotations). If the parameter is omitted, all annotations are fetched. * or 'any' (all annotations). The default value is 'display'.
*/ */
/** /**
@ -1131,8 +1155,8 @@ class PDFDocumentProxy {
* @property {Object} canvasContext - A 2D context of a DOM Canvas object. * @property {Object} canvasContext - A 2D context of a DOM Canvas object.
* @property {PageViewport} viewport - Rendering viewport obtained by calling * @property {PageViewport} viewport - Rendering viewport obtained by calling
* the `PDFPageProxy.getViewport` method. * the `PDFPageProxy.getViewport` method.
* @property {string} [intent] - Rendering intent, can be 'display' or 'print'. * @property {string} [intent] - Rendering intent, can be 'display', 'print',
* The default value is 'display'. * or 'any'. The default value is 'display'.
* @property {boolean} [renderInteractiveForms] - Whether or not interactive * @property {boolean} [renderInteractiveForms] - Whether or not interactive
* form elements are rendered in the display layer. If so, we do not render * form elements are rendered in the display layer. If so, we do not render
* them on the canvas as well. The default value is `false`. * them on the canvas as well. The default value is `false`.
@ -1161,8 +1185,8 @@ class PDFDocumentProxy {
* Page getOperatorList parameters. * Page getOperatorList parameters.
* *
* @typedef {Object} GetOperatorListParameters * @typedef {Object} GetOperatorListParameters
* @property {string} [intent] - Rendering intent, can be 'display' or 'print'. * @property {string} [intent] - Rendering intent, can be 'display', 'print',
* The default value is 'display'. * or 'any'. The default value is 'display'.
*/ */
/** /**
@ -1276,9 +1300,8 @@ class PDFPageProxy {
* @returns {Promise<Array<any>>} A promise that is resolved with an * @returns {Promise<Array<any>>} A promise that is resolved with an
* {Array} of the annotation objects. * {Array} of the annotation objects.
*/ */
getAnnotations({ intent = null } = {}) { getAnnotations({ intent = "display" } = {}) {
const renderingIntent = const renderingIntent = getRenderingIntent(intent, {});
intent === "display" || intent === "print" ? intent : null;
if ( if (
!this._annotationsPromise || !this._annotationsPromise ||
@ -1336,7 +1359,9 @@ class PDFPageProxy {
this._stats.time("Overall"); this._stats.time("Overall");
} }
const renderingIntent = intent === "print" ? "print" : "display"; const renderingIntent = getRenderingIntent(intent, {
renderForms: renderInteractiveForms === true,
});
// If there was a pending destroy, cancel it so no cleanup happens during // If there was a pending destroy, cancel it so no cleanup happens during
// this call to render. // this call to render.
this.pendingCleanup = false; this.pendingCleanup = false;
@ -1380,7 +1405,6 @@ class PDFPageProxy {
this._pumpOperatorList({ this._pumpOperatorList({
pageIndex: this._pageIndex, pageIndex: this._pageIndex,
intent: renderingIntent, intent: renderingIntent,
renderInteractiveForms: renderInteractiveForms === true,
annotationStorage, annotationStorage,
}); });
} }
@ -1390,7 +1414,10 @@ class PDFPageProxy {
// Attempt to reduce memory usage during *printing*, by always running // Attempt to reduce memory usage during *printing*, by always running
// cleanup once rendering has finished (regardless of cleanupAfterRender). // cleanup once rendering has finished (regardless of cleanupAfterRender).
if (this.cleanupAfterRender || renderingIntent === "print") { if (
this.cleanupAfterRender ||
renderingIntent & RenderingIntentFlag.PRINT
) {
this.pendingCleanup = true; this.pendingCleanup = true;
} }
this._tryCleanup(); this._tryCleanup();
@ -1426,7 +1453,7 @@ class PDFPageProxy {
operatorList: intentState.operatorList, operatorList: intentState.operatorList,
pageIndex: this._pageIndex, pageIndex: this._pageIndex,
canvasFactory: canvasFactoryInstance, canvasFactory: canvasFactoryInstance,
useRequestAnimationFrame: renderingIntent !== "print", useRequestAnimationFrame: !(renderingIntent & RenderingIntentFlag.PRINT),
pdfBug: this._pdfBug, pdfBug: this._pdfBug,
}); });
@ -1471,9 +1498,7 @@ class PDFPageProxy {
} }
} }
const renderingIntent = `oplist-${ const renderingIntent = getRenderingIntent(intent, { isOpList: true });
intent === "print" ? "print" : "display"
}`;
let intentState = this._intentStates.get(renderingIntent); let intentState = this._intentStates.get(renderingIntent);
if (!intentState) { if (!intentState) {
intentState = Object.create(null); intentState = Object.create(null);
@ -1588,7 +1613,7 @@ class PDFPageProxy {
force: true, force: true,
}); });
if (intent.startsWith("oplist-")) { if (intent & RenderingIntentFlag.OPLIST) {
// Avoid errors below, since the renderTasks are just stubs. // Avoid errors below, since the renderTasks are just stubs.
continue; continue;
} }

View File

@ -18,6 +18,14 @@ import "./compatibility.js";
const IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; const IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
const FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; const FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
const RenderingIntentFlag = {
ANY: 0x01,
DISPLAY: 0x02,
PRINT: 0x04,
ANNOTATION_FORMS: 0x20,
OPLIST: 0x100,
};
// Permission flags from Table 22, Section 7.6.3.2 of the PDF specification. // Permission flags from Table 22, Section 7.6.3.2 of the PDF specification.
const PermissionFlag = { const PermissionFlag = {
PRINT: 0x04, PRINT: 0x04,
@ -1033,6 +1041,7 @@ export {
PasswordResponses, PasswordResponses,
PermissionFlag, PermissionFlag,
removeNullCharacters, removeNullCharacters,
RenderingIntentFlag,
setVerbosityLevel, setVerbosityLevel,
shadow, shadow,
StreamType, StreamType,

View File

@ -1443,6 +1443,12 @@ describe("api", function () {
expect(data.length).toEqual(4); expect(data.length).toEqual(4);
}); });
const anyPromise = page
.getAnnotations({ intent: "any" })
.then(function (data) {
expect(data.length).toEqual(4);
});
const displayPromise = page const displayPromise = page
.getAnnotations({ intent: "display" }) .getAnnotations({ intent: "display" })
.then(function (data) { .then(function (data) {
@ -1455,7 +1461,12 @@ describe("api", function () {
expect(data.length).toEqual(4); expect(data.length).toEqual(4);
}); });
await Promise.all([defaultPromise, displayPromise, printPromise]); await Promise.all([
defaultPromise,
anyPromise,
displayPromise,
printPromise,
]);
}); });
it("gets annotations containing relative URLs (bug 766086)", async function () { it("gets annotations containing relative URLs (bug 766086)", async function () {