diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 55fb31669f30e..536a5682a140e 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -3588,7 +3588,7 @@ class PartialEvaluator { if (properties.composite) { // CIDSystemInfo helps to match CID to glyphs const cidSystemInfo = dict.get("CIDSystemInfo"); - if (cidSystemInfo instanceof Dict) { + if (cidSystemInfo instanceof Dict && !properties.cidSystemInfo) { properties.cidSystemInfo = { registry: stringToPDFString(cidSystemInfo.get("Registry")), ordering: stringToPDFString(cidSystemInfo.get("Ordering")), @@ -3670,6 +3670,51 @@ class PartialEvaluator { baseEncodingName = null; } + // Ignore incorrectly specified WinAnsiEncoding for non-embedded CJK fonts + // (fixes issue20489). Some chinese fonts often have WinAnsiEncoding in the + // PDF even though they should use Identity-H or GB-EUC-H encoding. + if ( + baseEncodingName === "WinAnsiEncoding" && + nonEmbeddedFont && + properties.name?.charCodeAt(0) >= 0xb7 + ) { + const fontName = properties.name; + // This list is built from some names from Pdfium and mupdf: + // - https://pdfium.googlesource.com/pdfium/+/master/core/fpdfapi/font/cpdf_font.cpp#41 + // - https://fossies.org/linux/mupdf/source/pdf/pdf-font.c#l_820 + const chineseFontNames = [ + "\xCB\xCE\xCC\xE5", // SimSun + "\xBA\xDA\xCC\xE5", // SimHei + "\xBF\xAC\xCC\xE5", // SimKai + "\xB7\xC2\xCB\xCE", // SimFang + "\xBF\xAC\xCC\xE5_GB2312", // SimKai + "\xB7\xC2\xCB\xCE_GB2312", // SimFang + "\xC1\xA5\xCA\xE9", // SimLi + "\xD0\xC2\xCB\xCE", // SimSun + ]; + + // Check for common Chinese font names and their GBK-encoded equivalents + // (which may appear as Latin-1 when incorrectly decoded). + if (chineseFontNames.includes(fontName)) { + baseEncodingName = null; + properties.defaultEncoding = "Adobe-GB1-UCS2"; + properties.composite = true; + properties.cidEncoding = Name.get("GBK-EUC-H"); + const cMap = await CMapFactory.create({ + encoding: properties.cidEncoding, + fetchBuiltInCMap: this._fetchBuiltInCMapBound, + useCMap: null, + }); + properties.cMap = cMap; + properties.vertical = properties.cMap.vertical; + properties.cidSystemInfo = { + registry: "Adobe", + ordering: "GB1", + supplement: 0, + }; + } + } + if (baseEncodingName) { properties.defaultEncoding = getEncoding(baseEncodingName); } else { diff --git a/src/core/font_substitutions.js b/src/core/font_substitutions.js index 1e87fabac5a3a..7325087b3a935 100644 --- a/src/core/font_substitutions.js +++ b/src/core/font_substitutions.js @@ -306,6 +306,64 @@ const substitutionMap = new Map([ alias: "Wingdings", }, ], + [ + "\xCB\xCE\xCC\xE5", + { + local: ["SimSun", "SimSun Regular", "NSimSun"], + style: NORMAL, + ultimate: "serif", + }, + ], + [ + "\xBA\xDA\xCC\xE5", + { + local: ["SimHei", "SimHei Regular"], + style: NORMAL, + ultimate: "sans-serif", + }, + ], + [ + "\xBF\xAC\xCC\xE5", + { + local: ["KaiTi", "SimKai", "SimKai Regular"], + style: NORMAL, + ultimate: "sans-serif", + }, + ], + [ + "\xB7\xC2\xCB\xCE", + { + local: ["FangSong", "SimFang", "SimFang Regular"], + style: NORMAL, + ultimate: "serif", + }, + ], + [ + "\xBF\xAC\xCC\xE5_GB2312", + { + alias: "\xBF\xAC\xCC\xE5", + }, + ], + [ + "\xB7\xC2\xCB\xCE_GB2312", + { + alias: "\xB7\xC2\xCB\xCE", + }, + ], + [ + "\xC1\xA5\xCA\xE9", + { + local: ["SimLi", "SimLi Regular"], + style: NORMAL, + ultimate: "serif", + }, + ], + [ + "\xD0\xC2\xCB\xCE", + { + alias: "\xCB\xCE\xCC\xE5", + }, + ], ]); const fontAliases = new Map([["Arial-Black", "ArialBlack"]]); diff --git a/src/display/pattern_helper.js b/src/display/pattern_helper.js index 6884eca9c7b6b..d460de365637e 100644 --- a/src/display/pattern_helper.js +++ b/src/display/pattern_helper.js @@ -71,23 +71,51 @@ class RadialAxialShadingPattern extends BaseShadingPattern { this.matrix = null; } - _createGradient(ctx) { + isOriginBased() { + return ( + this._p0[0] === 0 && + this._p0[1] === 0 && + (!this.isRadial() || (this._p1[0] === 0 && this._p1[1] === 0)) + ); + } + + isRadial() { + return this._type === "radial"; + } + + _createGradient(ctx, transform = null) { let grad; + let firstPoint = this._p0; + let secondPoint = this._p1; + if (transform) { + firstPoint = firstPoint.slice(); + secondPoint = secondPoint.slice(); + Util.applyTransform(firstPoint, transform); + Util.applyTransform(secondPoint, transform); + } if (this._type === "axial") { grad = ctx.createLinearGradient( - this._p0[0], - this._p0[1], - this._p1[0], - this._p1[1] + firstPoint[0], + firstPoint[1], + secondPoint[0], + secondPoint[1] ); } else if (this._type === "radial") { + let r0 = this._r0; + let r1 = this._r1; + if (transform) { + const scale = new Float32Array(2); + Util.singularValueDecompose2dScale(transform, scale); + r0 *= scale[0]; + r1 *= scale[0]; + } grad = ctx.createRadialGradient( - this._p0[0], - this._p0[1], - this._r0, - this._p1[0], - this._p1[1], - this._r1 + firstPoint[0], + firstPoint[1], + r0, + secondPoint[0], + secondPoint[1], + r1 ); } @@ -100,6 +128,32 @@ class RadialAxialShadingPattern extends BaseShadingPattern { getPattern(ctx, owner, inverse, pathType) { let pattern; if (pathType === PathType.STROKE || pathType === PathType.FILL) { + if (this.isOriginBased()) { + let transf = Util.transform(inverse, owner.baseTransform); + if (this.matrix) { + transf = Util.transform(transf, this.matrix); + } + const precision = 1e-3; + const n1 = Math.hypot(transf[0], transf[1]); + const n2 = Math.hypot(transf[2], transf[3]); + const ps = (transf[0] * transf[2] + transf[1] * transf[3]) / (n1 * n2); + if (Math.abs(ps) < precision) { + // The images of the basis vectors are orthogonal. + if (this.isRadial()) { + // If the images of the basis vectors are a square then the + // circles are transformed to circles and we can use a gradient + // directly. + if (Math.abs(n1 - n2) < precision) { + return this._createGradient(ctx, transf); + } + } else { + // The rectangles are transformed to rectangles and we can use a + // gradient directly. + return this._createGradient(ctx, transf); + } + } + } + const ownerBBox = owner.current.getClippedPathBoundingBox( pathType, getCurrentTransform(ctx) diff --git a/test/integration/ink_editor_spec.mjs b/test/integration/ink_editor_spec.mjs index c9977d93787a4..f9b8a0a2c54a0 100644 --- a/test/integration/ink_editor_spec.mjs +++ b/test/integration/ink_editor_spec.mjs @@ -52,6 +52,15 @@ const commit = async page => { const switchToInk = switchToEditor.bind(null, "Ink"); +const drawLine = async (page, x0, y0, x1, y1) => { + const clickHandle = await waitForPointerUp(page); + await page.mouse.move(x0, y0); + await page.mouse.down(); + await page.mouse.move(x1, y1); + await page.mouse.up(); + await awaitPromise(clickHandle); +}; + describe("Ink Editor", () => { describe("Basic operations", () => { let pages; @@ -74,13 +83,7 @@ describe("Ink Editor", () => { for (let i = 0; i < 3; i++) { const x = rect.x + 100 + i * 100; const y = rect.y + 100 + i * 100; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(x, y); - await page.mouse.down(); - await page.mouse.move(x + 50, y + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); - + await drawLine(page, x, y, x + 50, y + 50); await commit(page); } @@ -107,15 +110,9 @@ describe("Ink Editor", () => { const rect = await getRect(page, ".annotationEditorLayer"); - const xStart = rect.x + 300; - const yStart = rect.y + 300; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(xStart, yStart); - await page.mouse.down(); - await page.mouse.move(xStart + 50, yStart + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); - + const x = rect.x + 300; + const y = rect.y + 300; + await drawLine(page, x, y, x + 50, y + 50); await commit(page); const rectBefore = await getRect(page, ".canvasWrapper .draw"); @@ -149,13 +146,7 @@ describe("Ink Editor", () => { const x = rect.x + 100; const y = rect.y + 100; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(x, y); - await page.mouse.down(); - await page.mouse.move(x + 50, y + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); - + await drawLine(page, x, y, x + 50, y + 50); await commit(page); const editorSelector = getEditorSelector(0); @@ -205,13 +196,7 @@ describe("Ink Editor", () => { const x = rect.x + 20; const y = rect.y + 20; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(x, y); - await page.mouse.down(); - await page.mouse.move(x + 50, y + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); - + await drawLine(page, x, y, x + 50, y + 50); await commit(page); await selectAll(page); @@ -244,13 +229,7 @@ describe("Ink Editor", () => { const x = rect.x + 20; const y = rect.y + 20; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(x, y); - await page.mouse.down(); - await page.mouse.move(x + 50, y + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); - + await drawLine(page, x, y, x + 50, y + 50); await commit(page); const oneToFourteen = Array.from(new Array(13).keys(), n => n + 2); @@ -299,12 +278,7 @@ describe("Ink Editor", () => { const x = rect.x + 20; const y = rect.y + 20; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(x, y); - await page.mouse.down(); - await page.mouse.move(x + 50, y + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); + await drawLine(page, x, y, x + 50, y + 50); await page.mouse.click(rect.x - 10, rect.y + 10); await page.waitForSelector(`${getEditorSelector(0)}.disabled`); @@ -331,14 +305,9 @@ describe("Ink Editor", () => { const rect = await getRect(page, ".annotationEditorLayer"); - const xStart = rect.x + 300; - const yStart = rect.y + 300; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(xStart, yStart); - await page.mouse.down(); - await page.mouse.move(xStart + 50, yStart + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); + const x = rect.x + 300; + const y = rect.y + 300; + await drawLine(page, x, y, x + 50, y + 50); await commit(page); const editorSelector = getEditorSelector(0); @@ -375,14 +344,9 @@ describe("Ink Editor", () => { const rect = await getRect(page, ".annotationEditorLayer"); - const xStart = rect.x + 300; - const yStart = rect.y + 300; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(xStart, yStart); - await page.mouse.down(); - await page.mouse.move(xStart + 50, yStart + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); + const x = rect.x + 300; + const y = rect.y + 300; + await drawLine(page, x, y, x + 50, y + 50); await commit(page); const editorSelector = getEditorSelector(0); @@ -432,14 +396,9 @@ describe("Ink Editor", () => { const rect = await getRect(page, ".annotationEditorLayer"); - const xStart = rect.x + 300; - const yStart = rect.y + 300; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(xStart, yStart); - await page.mouse.down(); - await page.mouse.move(xStart + 50, yStart + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); + const x = rect.x + 300; + const y = rect.y + 300; + await drawLine(page, x, y, x + 50, y + 50); await commit(page); const editorSelector = getEditorSelector(0); @@ -483,16 +442,11 @@ describe("Ink Editor", () => { await switchToInk(page); const rect = await getRect(page, ".annotationEditorLayer"); - let xStart = rect.x + 10; - const yStart = rect.y + 10; + let x = rect.x + 10; + const y = rect.y + 10; for (let i = 0; i < 5; i++) { - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(xStart, yStart); - await page.mouse.down(); - await page.mouse.move(xStart + 50, yStart + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); - xStart += 70; + await drawLine(page, x, y, x + 50, y + 50); + x += 70; } await commit(page); @@ -561,13 +515,7 @@ describe("Ink Editor", () => { const x = rect.x + 20; const y = rect.y + 20; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(x, y); - await page.mouse.down(); - await page.mouse.move(x + 50, y + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); - + await drawLine(page, x, y, x + 50, y + 50); await commit(page); const drawSelector = `.page[data-page-number = "1"] .canvasWrapper .draw`; @@ -636,12 +584,7 @@ describe("Ink Editor", () => { const x = rect.x + 20; const y = rect.y + 20; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(x, y); - await page.mouse.down(); - await page.mouse.move(x + 50, y + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); + await drawLine(page, x, y, x + 50, y + 50); const drawSelector = `.canvasWrapper svg.draw path[d]:not([d=""])`; await page.waitForSelector(drawSelector); @@ -680,12 +623,7 @@ describe("Ink Editor", () => { const x = rect.x + 20; const y = rect.y + 20; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(x, y); - await page.mouse.down(); - await page.mouse.move(x + 50, y + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); + await drawLine(page, x, y, x + 50, y + 50); await page.evaluate(() => { window.focusedIds = []; @@ -884,14 +822,9 @@ describe("Ink Editor", () => { await switchToInk(page); const rect = await getRect(page, ".annotationEditorLayer"); - const xStart = rect.x + 300; - const yStart = rect.y + 300; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(xStart, yStart); - await page.mouse.down(); - await page.mouse.move(xStart + 50, yStart + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); + const x = rect.x + 300; + const y = rect.y + 300; + await drawLine(page, x, y, x + 50, y + 50); await commit(page); await page.waitForSelector(editorSelector); @@ -918,14 +851,9 @@ describe("Ink Editor", () => { await switchToInk(page); const rect = await getRect(page, ".annotationEditorLayer"); - const xStart = rect.x + 300; - const yStart = rect.y + 300; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(xStart, yStart); - await page.mouse.down(); - await page.mouse.move(xStart + 50, yStart + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); + const x = rect.x + 300; + const y = rect.y + 300; + await drawLine(page, x, y, x + 50, y + 50); await commit(page); await page.waitForSelector(editorSelector); @@ -957,14 +885,9 @@ describe("Ink Editor", () => { await switchToInk(page); const rect = await getRect(page, ".annotationEditorLayer"); - const xStart = rect.x + 300; - const yStart = rect.y + 300; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(xStart, yStart); - await page.mouse.down(); - await page.mouse.move(xStart + 50, yStart + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); + const x = rect.x + 300; + const y = rect.y + 300; + await drawLine(page, x, y, x + 50, y + 50); await commit(page); await page.waitForSelector(editorSelector); @@ -976,14 +899,9 @@ describe("Ink Editor", () => { await page.waitForSelector("#editorUndoBar", { visible: true }); const newRect = await getRect(page, ".annotationEditorLayer"); - const newXStart = newRect.x + 300; - const newYStart = newRect.y + 300; - const newClickHandle = await waitForPointerUp(page); - await page.mouse.move(newXStart, newYStart); - await page.mouse.down(); - await page.mouse.move(newXStart + 50, newYStart + 50); - await page.mouse.up(); - await awaitPromise(newClickHandle); + const newX = newRect.x + 300; + const newY = newRect.y + 300; + await drawLine(page, newX, newY, newX + 50, newY + 50); await commit(page); await page.waitForSelector(getEditorSelector(1)); @@ -1014,12 +932,7 @@ describe("Ink Editor", () => { const x = rect.x + 20; const y = rect.y + 20; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(x, y); - await page.mouse.down(); - await page.mouse.move(x + 50, y + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); + await drawLine(page, x, y, x + 50, y + 50); const svgSelector = ".canvasWrapper svg.draw"; const strokeWidth = await page.$eval(svgSelector, el => @@ -1082,14 +995,9 @@ describe("Ink Editor", () => { page, `${pageSelector} .annotationEditorLayer` ); - const xStart = rect.x + 10; - const yStart = rect.y + 10; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(xStart, yStart); - await page.mouse.down(); - await page.mouse.move(xStart + 10, yStart + 10); - await page.mouse.up(); - await awaitPromise(clickHandle); + const x = rect.x + 10; + const y = rect.y + 10; + await drawLine(page, x, y, x + 10, y + 10); await commit(page); } @@ -1148,15 +1056,10 @@ describe("Ink Editor", () => { await switchToInk(page); const editorLayerRect = await getRect(page, ".annotationEditorLayer"); - const drawStartX = editorLayerRect.x + 100; - const drawStartY = editorLayerRect.y + 100; - - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(drawStartX, drawStartY); - await page.mouse.down(); - await page.mouse.move(drawStartX + 50, drawStartY + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); + + const x = editorLayerRect.x + 100; + const y = editorLayerRect.y + 100; + await drawLine(page, x, y, x + 50, y + 50); await commit(page); const pageFinalPosition = await getRect( @@ -1257,16 +1160,10 @@ describe("Should switch from an editor and mode to others by double clicking", ( await switchToInk(page); const editorLayerRect = await getRect(page, ".annotationEditorLayer"); - const drawStartX = editorLayerRect.x + 100; - const drawStartY = editorLayerRect.y + 100; - const inkSelector = getEditorSelector(0); - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(drawStartX, drawStartY); - await page.mouse.down(); - await page.mouse.move(drawStartX + 50, drawStartY + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); + const x = editorLayerRect.x + 100; + const y = editorLayerRect.y + 100; + await drawLine(page, x, y, x + 50, y + 50); await commit(page); await switchToEditor("FreeText", page); @@ -1286,6 +1183,7 @@ describe("Should switch from an editor and mode to others by double clicking", ( await page.waitForSelector("#editorInkButton:not(.toggled)"); let modeChangedHandle = await waitForAnnotationModeChanged(page); + const inkSelector = getEditorSelector(0); await selectEditor(page, inkSelector, 2); await awaitPromise(modeChangedHandle); await page.waitForSelector("#editorInkButton.toggled"); @@ -1322,12 +1220,7 @@ describe("Ink must update its color", () => { const x = rect.x + 20; const y = rect.y + 20; - const clickHandle = await waitForPointerUp(page); - await page.mouse.move(x, y); - await page.mouse.down(); - await page.mouse.move(x + 50, y + 50); - await page.mouse.up(); - await awaitPromise(clickHandle); + await drawLine(page, x, y, x + 50, y + 50); await commit(page); const editorSelector = getEditorSelector(0); diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 352366971bffa..6080768c93f9e 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -767,3 +767,4 @@ !issue20225.pdf !issue20513.pdf !issue20516.pdf +!issue20489.pdf diff --git a/test/pdfs/issue20489.pdf b/test/pdfs/issue20489.pdf new file mode 100644 index 0000000000000..1261d4d0b8723 Binary files /dev/null and b/test/pdfs/issue20489.pdf differ diff --git a/test/test_manifest.json b/test/test_manifest.json index c9b9a5400c321..4ea18bc5421fc 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -654,6 +654,7 @@ "md5": "8f9347b0d5620537850b24b8385b0982", "rounds": 1, "link": true, + "talos": false, "firstPage": 4, "lastPage": 4, "type": "eq" @@ -664,6 +665,7 @@ "md5": "8f9347b0d5620537850b24b8385b0982", "rounds": 1, "link": true, + "talos": false, "firstPage": 4, "lastPage": 4, "type": "eq", @@ -13106,5 +13108,12 @@ "md5": "19a3a347773518242fa3cf1c04a9a1e4", "rounds": 1, "type": "eq" + }, + { + "id": "issue20489", + "file": "pdfs/issue20489.pdf", + "md5": "b85c798b9a4cc2cd4337d335321cc612", + "rounds": 1, + "type": "eq" } ]