Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for optional marked content. #12095

Merged
merged 1 commit into from
Aug 4, 2020
Merged

Conversation

brendandahl
Copy link
Contributor

Add a new method to the API to get the optional content configuration. Add
a new render task param that accepts the above configuration.
For now, the optional content is not controllable by the user in
the viewer, but renders with the default configuration in the PDF.

All of the test files added exhibit different uses of optional content.

Fixes #269.

@brendandahl
Copy link
Contributor Author

/botio test

@pdfjsbot
Copy link

From: Bot.io (Windows)


Received

Command cmd_test from @brendandahl received. Current queue size: 0

Live output at: http://54.215.176.217:8877/6ff05971f6f1fd5/output.txt

@pdfjsbot
Copy link

From: Bot.io (Linux m4)


Received

Command cmd_test from @brendandahl received. Current queue size: 0

Live output at: http://54.67.70.0:8877/48e5f33dc64066f/output.txt

@pdfjsbot
Copy link

From: Bot.io (Linux m4)


Failed

Full output at http://54.67.70.0:8877/48e5f33dc64066f/output.txt

Total script time: 26.89 mins

  • Font tests: Passed
  • Unit tests: FAILED
  • Regression tests: FAILED

Image differences available at: http://54.67.70.0:8877/48e5f33dc64066f/reftest-analyzer.html#web=eq.log

@pdfjsbot
Copy link

From: Bot.io (Windows)


Failed

Full output at http://54.215.176.217:8877/6ff05971f6f1fd5/output.txt

Total script time: 31.32 mins

  • Font tests: Passed
  • Unit tests: FAILED
  • Regression tests: FAILED

Image differences available at: http://54.215.176.217:8877/6ff05971f6f1fd5/reftest-analyzer.html#web=eq.log

@brendandahl
Copy link
Contributor Author

/botio test

@pdfjsbot
Copy link

From: Bot.io (Windows)


Received

Command cmd_test from @brendandahl received. Current queue size: 0

Live output at: http://54.215.176.217:8877/ef23cf7e4380623/output.txt

@pdfjsbot
Copy link

From: Bot.io (Linux m4)


Received

Command cmd_test from @brendandahl received. Current queue size: 0

Live output at: http://54.67.70.0:8877/d7647d4e6988ad1/output.txt

} else if (isDict(contentProperties)) {
optionalContent = contentProperties;
} else {
throw new Error("Optional content properties malformed.");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be throw new FormatError(...), since that's used for errors with the PDF data itself.

@@ -1224,6 +1232,61 @@ class PartialEvaluator {
throw new FormatError(`Unknown PatternName: ${patternName}`);
}

parseMarkedContentProps(contentProperties, resources) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make this method async, since the data fetching below (i.e. the Dict.get(...) calls) could throw which would thus break the OperatorList parsing, and that way you'll be able to (easier) deal with any errors gracefully (relying on the existing ignoreErrors functionality).

warn(`Expected name for beginMarkedContentProps arg0=${args[0]}`);
continue;
}
fn = OPS.beginMarkedContentProps;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line looks superfluous, since fn === OPS.beginMarkedContentProps must hold in order for this block to have been entered!?

src/core/obj.js Outdated
Comment on lines 258 to 287
const properties = this.catDict.get("OCProperties");
if (!properties) {
return null;
}
const groupsData = properties.get("OCGs");
if (!Array.isArray(groupsData)) {
return 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 = properties.xref.fetchIfRef(groupRef);
groups.push({
id: groupRef.toString(),
name: group.get("Name"),
intent: group.get("Intent"), // TODO validate this.
});
}
const defaultConfig = properties.get("D");
if (!defaultConfig) {
return null;
}
const config = this._readOptionalContentConfig(defaultConfig, groupRefs);
config.groups = groups;
return config;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to all other methods here, see e.g. get permissions() above, please wrap this in try...catch to prevent breaking errors if data fetching fails (since Dict.get(...) calls could throw).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will get optionalContentConfig() always return the same data for a document with Optional Content groups?
If so, please shadow the return values to avoid having to repeatedly re-parsing this for every single page. (Or at least, please shadow the return null; cases.)

src/core/obj.js Outdated
}

_readOptionalContentConfig(config, contentGroupRefs) {
// console.log(contentGroupRefs);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left-over debugging statement?

}
fn = OPS.beginMarkedContentProps;
if (args[0].name === "OC") {
args = ["OC", self.parseMarkedContentProps(args[1], resources)];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With parseMarkedContentProps being asynchronous, as suggested above, this would need to become something like this (which thus handles any errors gracefully):

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: /* Some new UNSUPPORTED_FEATURES id */,
        });
        warn(`getOperatorList - ignoring beginMarkedContentProps: "${reason}".`);
        return;
      }
      throw reason;
    })
);
return;

};

if (dict.has("OC")) {
groupOptions.optionalContent = this.parseMarkedContentProps(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: With parseMarkedContentProps being asynchronous, as suggested below, this would need to be groupOptions.optionalContent = await this.parseMarkedContentProps( instead.

Comment on lines 1718 to 1721
if (simpleFillText && !accent && this.contentVisible) {
// common case
ctx.fillText(character, scaledX, scaledY);
} else {
} else if (this.contentVisible) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would in my opinion be, a little bit, clearer if you simply enclosed this entire code-block

pdf.js/src/display/canvas.js

Lines 1698 to 1717 in 6c39aff

// 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.
if (glyph.isInFont || font.missingFile) {
if (simpleFillText && !accent) {
// common case
ctx.fillText(character, scaledX, scaledY);
} else {
this.paintChar(character, scaledX, scaledY, patternTransform);
if (accent) {
scaledAccentX = scaledX + accent.offset.x / fontSizeScale;
scaledAccentY = scaledY - accent.offset.y / fontSizeScale;
this.paintChar(
accent.fontChar,
scaledAccentX,
scaledAccentY,
patternTransform
);
}
}
}

in if (this.contentVisible) { ... } rather than "inlining" these checks.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, unless I'm mistaken there's no similar checks in the showType3Text below. Don't you need to account for this.contentVisible there as well?

Comment on lines 2689 to 2701
if (this._canvas) {
if (canvasInRendering.has(this._canvas)) {
this.capability.reject(
new Error(
"Cannot use the same canvas during multiple render() operations. " +
"Use different canvas or ensure previous operations were " +
"cancelled or completed."
)
);
} else {
canvasInRendering.add(this._canvas);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which test-case required moving this code, and is there another way to deal with that?
(Since the commit message says: - Reject the InternalRenderTask earlier now that InitializeGraphics is waits for another promise.)

I've not actually tested it, but based on reading the code I'm not entirely convinced that moving this code will actually do the right thing in general.
Currently initializeGraphics will throw

pdf.js/src/display/api.js

Lines 2668 to 2672 in 6c39aff

throw new Error(
"Cannot use the same canvas during multiple render() operations. " +
"Use different canvas or ensure previous operations were " +
"cancelled or completed."
);
which thus means that we're reaching this catch

pdf.js/src/display/api.js

Lines 1118 to 1121 in 6c39aff

internalRenderTask.initializeGraphics(transparency);
internalRenderTask.operatorListChanged();
})
.catch(complete);
which thus calls

pdf.js/src/display/api.js

Lines 1054 to 1081 in 6c39aff

const complete = error => {
const i = intentState.renderTasks.indexOf(internalRenderTask);
if (i >= 0) {
intentState.renderTasks.splice(i, 1);
}
// Attempt to reduce memory usage during *printing*, by always running
// cleanup once rendering has finished (regardless of cleanupAfterRender).
if (this.cleanupAfterRender || renderingIntent === "print") {
this.pendingCleanup = true;
}
this._tryCleanup();
if (error) {
internalRenderTask.capability.reject(error);
this._abortOperatorList({
intentState,
reason: error,
});
} else {
internalRenderTask.capability.resolve();
}
if (this._stats) {
this._stats.timeEnd("Rendering");
this._stats.timeEnd("Overall");
}
};
as expected and all of the required clean-up will thus run.

With your changes here, it doesn't seem like you're actually guaranteed that complete is ever called correctly!?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was the test that calls render twice. It doesn't seem to be failing anymore. This code is quite the mix of callbacks and old style promises.

@pdfjsbot
Copy link

From: Bot.io (Linux m4)


Failed

Full output at http://54.67.70.0:8877/d7647d4e6988ad1/output.txt

Total script time: 26.87 mins

  • Font tests: Passed
  • Unit tests: Passed
  • Regression tests: FAILED

Image differences available at: http://54.67.70.0:8877/d7647d4e6988ad1/reftest-analyzer.html#web=eq.log

@pdfjsbot
Copy link

From: Bot.io (Windows)


Failed

Full output at http://54.215.176.217:8877/ef23cf7e4380623/output.txt

Total script time: 30.57 mins

  • Font tests: Passed
  • Unit tests: Passed
  • Regression tests: FAILED

Image differences available at: http://54.215.176.217:8877/ef23cf7e4380623/reftest-analyzer.html#web=eq.log

src/core/obj.js Outdated
continue;
}
groupRefs.push(groupRef);
const group = properties.xref.fetchIfRef(groupRef);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed this previously: You want this.xref.fetchIfRef(groupRef); instead here, since this.xref is guaranteed to be defined whereas a Dict instance may not always have an xref property.

src/core/obj.js Outdated
if (ex instanceof MissingDataException) {
throw ex;
}
warn("Unable to optional content config. " + ex);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing e.g. the word "read" here?

warn(`Unable to read optional content config: ${ex}`);

src/core/obj.js Outdated
Comment on lines 279 to 280
name: group.get("Name"),
intent: group.get("Intent"), // TODO validate this.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this data is being sent between threads, whatever data this contains much be compatible with the "structured clone algorithm". Hence you may want to add at least some basic validation of these two properties since otherwise a corrupt PDF document might refuse to render altogether.

return {
type: optionalContentType,
ids: groupIds,
policy: optionalContent.get("P"),
Copy link
Collaborator

@Snuffleupagus Snuffleupagus Jul 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this data is being sent between threads, whatever data this contains much be compatible with the "structured clone algorithm". Hence you may want to add at least some basic validation of the P property since otherwise a corrupt PDF document might refuse to render altogether.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the specification, https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PDF32000_2008.pdf#G7.3883825, this should be a Name. In the OptionalContentConfig class you're accessing them as if they were mere strings, which I don't think will work!?

I believe that you'll need to either validate that it's a name and (essentially) return optionalContent.get("P").name, here, or possibly return the name as-is a and fix the lookup in OptionalContentConfig instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought there was a test PDF for this, but appears there aren't any with a policy attribute. If I have some time I'll try to make some more tests.

src/core/obj.js Outdated
Comment on lines 312 to 329
name: config.get("Name"),
creator: config.get("Creator"),
baseState: config.get("BaseState"),
on: parseOnOff(config.get("ON")),
off: parseOnOff(config.get("OFF")),
Copy link
Collaborator

@Snuffleupagus Snuffleupagus Jul 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this data is being sent between threads, whatever data this contains much be compatible with the "structured clone algorithm". Hence you may want to add at least some basic validation of the Name/Creator/BaseState properties since otherwise a corrupt PDF document might refuse to render altogether.

return this.messageHandler
.sendWithPromise("GetOptionalContentConfig", null)
.then(results => {
const config = new OptionalContentConfig();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering: Any particular reason to not simply pass the results when creating the OptionalContentConfig instance, and thus essentially inline the initialize method in the constructor?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had plans of doing some unit testing this differently. Not going to do that now, so I'll merge it.

src/core/obj.js Outdated
const group = properties.xref.fetchIfRef(groupRef);
groups.push({
id: groupRef.toString(),
name: group.get("Name"),
Copy link
Collaborator

@Snuffleupagus Snuffleupagus Jul 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is, I believe, a string; don't you need to wrap it in stringToPDFString() to avoid issues?

Edit: Issue #7283 is one example which requires this.

src/core/obj.js Outdated
Comment on lines 312 to 313
name: config.get("Name"),
creator: config.get("Creator"),
Copy link
Collaborator

@Snuffleupagus Snuffleupagus Jul 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these are, I believe, strings; don't you need to wrap them in stringToPDFString() to avoid issues?

Edit: Issue #7283 is one example which requires this.

src/core/obj.js Outdated
return {
name: config.get("Name"),
creator: config.get("Creator"),
baseState: config.get("BaseState"),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the specification, https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PDF32000_2008.pdf#G7.3883884, this should be a Name. In the OptionalContentConfig class you're accessing them as if they were mere strings, which I don't think will work!?

I believe that you'll need to either validate that it's a name and (essentially) return config.get("BaseState").name, here, or possibly return the name as-is a and fix the lookup in OptionalContentConfig instead.

return {
type: optionalContentType,
ids: groupIds,
policy: optionalContent.get("P"),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the specification, https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PDF32000_2008.pdf#G7.3883825, this should be a Name. In the OptionalContentConfig class you're accessing them as if they were mere strings, which I don't think will work!?

I believe that you'll need to either validate that it's a name and (essentially) return optionalContent.get("P").name, here, or possibly return the name as-is a and fix the lookup in OptionalContentConfig instead.

@@ -790,6 +794,15 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
ctx.drawImage(mask, 0, 0);
}

function isContentVisible(stack) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that every call-sites seem to pass in this.markedContentStack manually, any reason not to make this a "normal" method and simply handle the stack internally instead?
E.g. something like this

_computeContentVisible() {
  const state = this.markedContentStack;
  for (let i = stack.length - 1; i >= 0; i--) {
    if (!stack[i].visible) {
      return false;
    }
  }
  return true;
},

and the call-sites could then simply read this.contentVisible = this._computeContentVisible();

@@ -1695,6 +1755,45 @@ class PartialEvaluator {
// e.g. as done in https://github.com/mozilla/pdf.js/pull/6266,
// but doing so is meaningless without knowing the semantics.
continue;
case OPS.beginMarkedContentProps:
if (!isName(args[0])) {
warn(`Expected name for beginMarkedContentProps arg0=${args[0]}`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it actually make sense to simply move this check into the next handling below and throw a FormatError instead?
I.e. something similar to the next code that's used e.g. when parsing OPS.paintXObject earlier in this file.

Really sorry about the back-and-forth in some of these comments, but it's quite a lot of added code/functionality to "unpack" here and at first glance I unfortunately overlooked some of the finer details.
The overall functionality seem to work very nicely though :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started to do do this, but sometimes the marked content isn't an "OC" which then causes us to wait async when we really don't need to.

Copy link
Collaborator

@Snuffleupagus Snuffleupagus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r=me, with the commits squashed and all tests passing.

src/core/obj.js Outdated
Comment on lines 287 to 290
const defaultConfig = properties.get("D");
if (!defaultConfig) {
return shadow(this, "optionalContentConfig", null);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Would it make sense to move this to occur earlier in the method, since the groupsData parsing above seems pointless in the !defaultConfig case?

Copy link
Contributor

@timvandermeij timvandermeij left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comments, also looks good to me after this.


let expression = null;
if (optionalContent.get("VE")) {
// TODO support content epxressions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: expressions

@@ -485,6 +485,13 @@ class WorkerMessageHandler {
return pdfManager.ensureCatalog("documentOutline");
});

handler.on(
"GetOptionalContentConfig",
function wphSetupGetOptionalContentConfig(data) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This handler can be shortened a bit by using an anonymous function like the one below.

/**
* @returns {Promise} A promise that is resolved with an
* {OptionalContentConfig} that will have all the optional content groups (if
* the document has any). @see {OptionalContentConfig}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't @see be on a new line for JSDoc?

* @property {Promise} [optionalContentConfigPromise] - A promise that should
* resolve with an {OptionalContentConfig} created from
* PDFDocumentProxy.getOptionalContentConfig. If null, the
* config will automatically fetched with the default
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: automatically be fetched

@@ -0,0 +1,107 @@
/* Copyright 2012 Mozilla Foundation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: 2020

@brendandahl
Copy link
Contributor Author

/botio test

@pdfjsbot
Copy link

From: Bot.io (Linux m4)


Received

Command cmd_test from @brendandahl received. Current queue size: 0

Live output at: http://54.67.70.0:8877/871b55504c08464/output.txt

@pdfjsbot
Copy link

From: Bot.io (Windows)


Received

Command cmd_test from @brendandahl received. Current queue size: 0

Live output at: http://54.215.176.217:8877/d3650f17b24da81/output.txt

@pdfjsbot
Copy link

From: Bot.io (Linux m4)


Failed

Full output at http://54.67.70.0:8877/871b55504c08464/output.txt

Total script time: 26.85 mins

  • Font tests: Passed
  • Unit tests: Passed
  • Regression tests: FAILED

Image differences available at: http://54.67.70.0:8877/871b55504c08464/reftest-analyzer.html#web=eq.log

@pdfjsbot
Copy link

From: Bot.io (Windows)


Failed

Full output at http://54.215.176.217:8877/d3650f17b24da81/output.txt

Total script time: 30.81 mins

  • Font tests: Passed
  • Unit tests: Passed
  • Regression tests: FAILED

Image differences available at: http://54.215.176.217:8877/d3650f17b24da81/reftest-analyzer.html#web=eq.log

Copy link
Collaborator

@Snuffleupagus Snuffleupagus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the test logs, there's two actual errors thrown during rendering:

TEST-UNEXPECTED-FAIL | test failed issue7769 | in firefox | page1 round 1 | render : Error: Optional content group must be found.
TEST-UNEXPECTED-FAIL | test failed issue7769 | in chrome | page1 round 1 | render : Error: Optional content group must be found.

Unfortunately, it appears that this particular error occurs in such a way that the failures aren't displayed in the "Reftest analyzer" (and I thus missed it previously).

src/display/optional_content_config.js Outdated Show resolved Hide resolved
src/display/optional_content_config.js Show resolved Hide resolved
src/display/optional_content_config.js Show resolved Hide resolved
src/display/optional_content_config.js Show resolved Hide resolved
src/display/optional_content_config.js Show resolved Hide resolved
src/display/optional_content_config.js Show resolved Hide resolved
};

if (dict.has("OC")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All form XObjects can have an OC entry, not just transparency groups that have a Group entry. Then you may need to pass the parsed OC value also to paintFormXObjectBegin/End and handle it in canvas.js.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, I'll just insert a beginMarkedContentProps before the form object so I don't have to handle this for both.

@janpe2
Copy link
Contributor

janpe2 commented Jul 22, 2020

You should push a { visible: true } object also in beginMarkedContent ("BMC").

beginMarkedContent: function CanvasGraphics_beginMarkedContent(tag) {

"EMC" is the end of both "BMC" and "BDC" sections. "BMC" is not related to optional content but some PDFs may use it and the marked content stack might go wrong.

@Snuffleupagus
Copy link
Collaborator

@brendandahl Given that this PR seems really close to being done, since there's just a few more comments to fix (such that all tests pass), do you perhaps have time to fix the remaining things such that this can land?

I've been playing around with this patch a fair bit locally[1], and it seems to work really well in practice :-)
Also, the overall implementation is a lot simpler than I thought it would be when reading the specification.


[1] I've got, more-or-less, finished patches for the viewer integration.

@brendandahl
Copy link
Contributor Author

/botio test

@pdfjsbot
Copy link

pdfjsbot commented Aug 3, 2020

From: Bot.io (Linux m4)


Received

Command cmd_test from @brendandahl received. Current queue size: 0

Live output at: http://54.67.70.0:8877/48c34ec27dcef8c/output.txt

@pdfjsbot
Copy link

pdfjsbot commented Aug 3, 2020

From: Bot.io (Windows)


Received

Command cmd_test from @brendandahl received. Current queue size: 0

Live output at: http://54.215.176.217:8877/0f7ae373a4b37f7/output.txt

@pdfjsbot
Copy link

pdfjsbot commented Aug 3, 2020

From: Bot.io (Linux m4)


Failed

Full output at http://54.67.70.0:8877/48c34ec27dcef8c/output.txt

Total script time: 26.94 mins

  • Font tests: Passed
  • Unit tests: FAILED
  • Regression tests: FAILED

Image differences available at: http://54.67.70.0:8877/48c34ec27dcef8c/reftest-analyzer.html#web=eq.log

Copy link
Contributor

@timvandermeij timvandermeij left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm leaving the review for @Snuffleupagus, but I'm just checking the comments/types here. For context, we have been working a lot on that the last few days in #12102 and follow-up pull requests. The main priority was to get TypeScript definitions generated automatically from the source code and to get the comments in a consistent format, so this requires a small number of changes here to make sure that once #12156 lands everything is still consistent and the generated types are correct. Thanks!

@@ -788,6 +789,16 @@ class PDFDocumentProxy {
return this._transport.getOutline();
}

/**
* @see {OptionalContentConfig}
* @returns {Promise<OptionalContentConfig>} A promise that is resolved with
Copy link
Contributor

@timvandermeij timvandermeij Aug 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be @returns {Promise<OptionalContentConfig | null>} looking at the catalog code. Moreover, @link should be used to refer to other types/code and it should be formatted in the standard way. In summary, this is my suggestion based on the existing comments:

/**
  * @returns {Promise<OptionalContentConfig | null>} A promise that is resolved
  *   with an {@link OptionalContentConfig} that will have all the optional
  *   content groups, or `null` if the document does not have any.
  */

@@ -788,6 +789,16 @@ class PDFDocumentProxy {
return this._transport.getOutline();
}

/**
* @see {OptionalContentConfig}
Copy link
Contributor

@timvandermeij timvandermeij Aug 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line can be removed since the type is in the @returns already so JSDoc will pick it up (we don't have this elsewhere too).

@@ -965,6 +976,11 @@ class PDFDocumentProxy {
* image). The default value is 'rgb(255,255,255)'.
* @property {Object} [annotationStorage] - Storage for annotation data in
* forms.
* @property {Promise} [optionalContentConfigPromise] - A promise that should
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once #12156 lands, this should be changed to mention the promise type and to have each line start with two spaces as per the standard.

@pdfjsbot
Copy link

pdfjsbot commented Aug 3, 2020

From: Bot.io (Windows)


Failed

Full output at http://54.215.176.217:8877/0f7ae373a4b37f7/output.txt

Total script time: 31.48 mins

  • Font tests: Passed
  • Unit tests: FAILED
  • Regression tests: FAILED

Image differences available at: http://54.215.176.217:8877/0f7ae373a4b37f7/reftest-analyzer.html#web=eq.log

@timvandermeij
Copy link
Contributor

By the way, the unit test failures (and most likely the reference test failures as well) are known intermittents and are tracked in #12123 (the Windows one is a new one that I now mentioned there).

@Snuffleupagus
Copy link
Collaborator

/botio test

@pdfjsbot
Copy link

pdfjsbot commented Aug 4, 2020

From: Bot.io (Linux m4)


Received

Command cmd_test from @Snuffleupagus received. Current queue size: 0

Live output at: http://54.67.70.0:8877/fc2151d724bc8a7/output.txt

@pdfjsbot
Copy link

pdfjsbot commented Aug 4, 2020

From: Bot.io (Windows)


Received

Command cmd_test from @Snuffleupagus received. Current queue size: 0

Live output at: http://54.215.176.217:8877/eacea1f25df0912/output.txt

@pdfjsbot
Copy link

pdfjsbot commented Aug 4, 2020

From: Bot.io (Linux m4)


Failed

Full output at http://54.67.70.0:8877/fc2151d724bc8a7/output.txt

Total script time: 27.04 mins

  • Font tests: Passed
  • Unit tests: FAILED
  • Regression tests: FAILED

Image differences available at: http://54.67.70.0:8877/fc2151d724bc8a7/reftest-analyzer.html#web=eq.log

@pdfjsbot
Copy link

pdfjsbot commented Aug 4, 2020

From: Bot.io (Windows)


Failed

Full output at http://54.215.176.217:8877/eacea1f25df0912/output.txt

Total script time: 32.10 mins

  • Font tests: Passed
  • Unit tests: Passed
  • Regression tests: FAILED

Image differences available at: http://54.215.176.217:8877/eacea1f25df0912/reftest-analyzer.html#web=eq.log

Copy link
Collaborator

@Snuffleupagus Snuffleupagus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, thank you!

@@ -2446,15 +2496,28 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
},
beginMarkedContent: function CanvasGraphics_beginMarkedContent(tag) {
// TODO Marked content.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Remove this comment?

Add a new method to the API to get the optional content configuration. Add
a new render task param that accepts the above configuration.
For now, the optional content is not controllable by the user in
the viewer, but renders with the default configuration in the PDF.

All of the test files added exhibit different uses of optional content.

Fixes mozilla#269.

Fix test to work with optional content.

- Change the stopAtErrors test to ensure the operator list has something,
  instead of asserting the exact number of operators.
@brendandahl
Copy link
Contributor Author

/botio makeref

@pdfjsbot
Copy link

pdfjsbot commented Aug 4, 2020

From: Bot.io (Linux m4)


Received

Command cmd_makeref from @brendandahl received. Current queue size: 0

Live output at: http://54.67.70.0:8877/540ff90a3e49b5e/output.txt

@pdfjsbot
Copy link

pdfjsbot commented Aug 4, 2020

From: Bot.io (Windows)


Received

Command cmd_makeref from @brendandahl received. Current queue size: 0

Live output at: http://54.215.176.217:8877/20d3f22d0d860ed/output.txt

@pdfjsbot
Copy link

pdfjsbot commented Aug 4, 2020

From: Bot.io (Linux m4)


Success

Full output at http://54.67.70.0:8877/540ff90a3e49b5e/output.txt

Total script time: 25.47 mins

  • Lint: Passed
  • Make references: Passed
  • Check references: Passed

@pdfjsbot
Copy link

pdfjsbot commented Aug 4, 2020

From: Bot.io (Windows)


Success

Full output at http://54.215.176.217:8877/20d3f22d0d860ed/output.txt

Total script time: 27.44 mins

  • Lint: Passed
  • Make references: Passed
  • Check references: Passed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature request for the future: PDF layers
5 participants