diff --git a/src/core/annotation.js b/src/core/annotation.js index ec5d51869..08d1d670f 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -26,6 +26,7 @@ import { getModificationDate, isString, OPS, + shadow, stringToPDFString, unreachable, Util, @@ -281,6 +282,8 @@ class Annotation { rect: this.rectangle, subtype: params.subtype, }; + + this._fallbackFontDict = null; } /** @@ -576,6 +579,7 @@ class Annotation { task, resources, operatorList: opList, + fallbackFontDict: this._fallbackFontDict, }) .then(() => { opList.addOp(OPS.endAnnotation, []); @@ -1873,6 +1877,7 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { if (this.uncheckedAppearance) { this._streams.push(this.uncheckedAppearance); } + this._fallbackFontDict = this.fallbackFontDict; } _processRadioButton(params) { @@ -1912,6 +1917,7 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { if (this.uncheckedAppearance) { this._streams.push(this.uncheckedAppearance); } + this._fallbackFontDict = this.fallbackFontDict; } _processPushButton(params) { @@ -1954,6 +1960,16 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { type, }; } + + get fallbackFontDict() { + const dict = new Dict(); + dict.set("BaseFont", Name.get("ZapfDingbats")); + dict.set("Type", Name.get("FallbackType")); + dict.set("Subtype", Name.get("FallbackType")); + dict.set("Encoding", Name.get("ZapfDingbatsEncoding")); + + return shadow(this, "fallbackFontDict", dict); + } } class ChoiceWidgetAnnotation extends WidgetAnnotation { diff --git a/src/core/evaluator.js b/src/core/evaluator.js index d63b98757..23c087d81 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -777,7 +777,15 @@ class PartialEvaluator { }); } - handleSetFont(resources, fontArgs, fontRef, operatorList, task, state) { + handleSetFont( + resources, + fontArgs, + fontRef, + operatorList, + task, + state, + fallbackFontDict = null + ) { // TODO(mack): Not needed? var fontName, fontSize = 0; @@ -787,7 +795,7 @@ class PartialEvaluator { fontSize = fontArgs[1]; } - return this.loadFont(fontName, fontRef, resources) + return this.loadFont(fontName, fontRef, resources, fallbackFontDict) .then(translated => { if (!translated.font.isType3Font) { return translated; @@ -978,7 +986,7 @@ class PartialEvaluator { }); } - loadFont(fontName, font, resources) { + loadFont(fontName, font, resources, fallbackFontDict = null) { const errorFont = async () => { return new TranslatedFont({ loadedName: "g_font_error", @@ -1020,7 +1028,11 @@ class PartialEvaluator { // Falling back to a default font to avoid completely broken rendering, // but note that there're no guarantees that things will look "correct". - fontRef = PartialEvaluator.fallbackFontDict; + if (fallbackFontDict) { + fontRef = fallbackFontDict; + } else { + fontRef = PartialEvaluator.fallbackFontDict; + } } if (this.fontCache.has(fontRef)) { @@ -1353,6 +1365,7 @@ class PartialEvaluator { resources, operatorList, initialState = null, + fallbackFontDict = null, }) { // Ensure that `resources`/`initialState` is correctly initialized, // even if the provided parameter is e.g. `null`. @@ -1533,7 +1546,8 @@ class PartialEvaluator { null, operatorList, task, - stateManager.state + stateManager.state, + fallbackFontDict ) .then(function (loadedName) { operatorList.addDependency(loadedName); diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index cadb2e9f3..82ebee21d 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -374,6 +374,7 @@ !issue12418_reduced.pdf !annotation-freetext.pdf !annotation-line.pdf +!bug1669099.pdf !annotation-square-circle.pdf !annotation-stamp.pdf !annotation-fileattachment.pdf diff --git a/test/pdfs/bug1669099.pdf b/test/pdfs/bug1669099.pdf new file mode 100644 index 000000000..6c4554be1 Binary files /dev/null and b/test/pdfs/bug1669099.pdf differ diff --git a/test/test_manifest.json b/test/test_manifest.json index 9a566ca30..549b8cd1e 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -4153,6 +4153,20 @@ "rounds": 1, "type": "eq" }, + { "id": "bug1669099", + "file": "pdfs/bug1669099.pdf", + "md5": "34421549d58e2b6eeddc674759381f7d", + "rounds": 1, + "type": "eq", + "print": true, + "annotationStorage": { + "29R": true, + "33R": true, + "37R": true, + "65R": true, + "69R": true + } + }, { "id": "issue1171.pdf", "file": "pdfs/issue1171.pdf", "md5": "2a6188a42a5874c7874b88eebd4acaf0", diff --git a/test/unit/annotation_spec.js b/test/unit/annotation_spec.js index 584d89b51..26659ffe5 100644 --- a/test/unit/annotation_spec.js +++ b/test/unit/annotation_spec.js @@ -2053,6 +2053,65 @@ describe("annotation", function () { }, done.fail); }); + it("should render checkbox with fallback font for printing", function (done) { + const appearanceStatesDict = new Dict(); + const normalAppearanceDict = new Dict(); + const checkedAppearanceDict = new Dict(); + const uncheckedAppearanceDict = new Dict(); + + const checkedStream = new StringStream("/ 12 Tf (4) Tj"); + checkedStream.dict = checkedAppearanceDict; + + const uncheckedStream = new StringStream(""); + uncheckedStream.dict = uncheckedAppearanceDict; + + checkedAppearanceDict.set("BBox", [0, 0, 8, 8]); + checkedAppearanceDict.set("FormType", 1); + checkedAppearanceDict.set("Matrix", [1, 0, 0, 1, 0, 0]); + normalAppearanceDict.set("Checked", checkedStream); + normalAppearanceDict.set("Off", uncheckedStream); + appearanceStatesDict.set("N", normalAppearanceDict); + + buttonWidgetDict.set("AP", appearanceStatesDict); + + const buttonWidgetRef = Ref.get(124, 0); + const xref = new XRefMock([ + { ref: buttonWidgetRef, data: buttonWidgetDict }, + ]); + const task = new WorkerTask("test print"); + partialEvaluator.options = { ignoreErrors: true }; + + AnnotationFactory.create( + xref, + buttonWidgetRef, + pdfManagerMock, + idFactoryMock + ) + .then(annotation => { + const annotationStorage = {}; + annotationStorage[annotation.data.id] = true; + return annotation.getOperatorList( + partialEvaluator, + task, + false, + annotationStorage + ); + }) + .then(opList => { + expect(opList.argsArray.length).toEqual(5); + expect(opList.fnArray).toEqual([ + OPS.beginAnnotation, + OPS.dependency, + OPS.setFont, + OPS.showText, + OPS.endAnnotation, + ]); + expect(opList.argsArray[3][0][0].fontChar).toEqual("✔"); + done(); + }) + .catch(done.fail); + }); + it("should render checkboxes for printing", function (done) { const appearanceStatesDict = new Dict(); const normalAppearanceDict = new Dict();