Compare commits

...
23 Commits
Author SHA1 Message Date
Jonas Jenwald ef62f31f24 Merge pull request #22035 from Snuffleupagus/FontInfo-shadow
Shadow the getters in `FontInfo`, `CssFontInfo`, and `SystemFontInfo` (PR 20197 follow-up)
2026-10-02 16:41:09 +02:00
Tim van der Meij c7e2fe4d29 Merge pull request #22012 from calixteman/tiling-pattern-tiny-steps-dos
Bound tiling pattern folding work for tiny steps
2026-10-02 15:31:33 +02:00
Tim van der Meij 8496056b9a Merge pull request #22056 from Snuffleupagus/charCodeToGlyphId-cidToGidMap-skip-has
Directly lookup entries in the `cidToGidMap` when building the `charCodeToGlyphId` map, for embedded composite fonts
2026-10-02 15:26:48 +02:00
Jonas Jenwald db561fecc6 Shadow the getters in FontInfo, CssFontInfo, and SystemFontInfo (PR 20197 follow-up)
Prior to PR 20197 each font-instance had just a single copy of all its relevant font-properties on the main-thread, however that's unfortunately no longer the case.
Given how the compilation was implemented, every single time that a font-property is accessed on the main-thread it'll now be re-parsed. Not only does this seem inefficient, especially for properties needed e.g. during text-rendering, but it'll lead to (potentially) a lot of duplicated object creation.

Consider what currently happens when rendering all pages of the following PDFs:
 - `tracemonkey.pdf` contains `24` separate fonts, but the `fontMatrix`-property is re-parsed a whopping `1009` times (once per `showText` operator).

 - `standard_fonts.pdf` contains `14` separate fonts, however we create no less than `186` separate `StandardFontInfo` instances.

 - `xfa_bug1716816.pdf` contains `4` separate fonts, however we create no less than `45` separate `CssFontInfo` instances.

This is obviously not limited to just the font-properties listed above, but those are mere examples to illustrate the problem.
By shadowing the font-property getters on the main-thread, obviously with the exception of `FontInfo.prototype.data`, we only need to parse each font-property *once* per font-instance.

*Note:* Unfortunately this *increases* the size of the `gulp mozcentral` bundle by `792` bytes, but that cannot really be helped since this seems like the correct thing to do regardless.
2026-10-02 15:25:24 +02:00
Jonas Jenwald a00ad131fd Introduce a helper to reduce duplication when reading compiled buffer-data from fonts
Currently the `FontInfo.prototype.{data, cssFontInfo, systemFontInfo}` getters duplicate virtually the same code when reading buffer-data, which seems completely unnecessary.

*Note:* This reduces the size of the `gulp mozcentral` bundle by `738` bytes, and with the upcoming worker-rendering this saving will be doubled.
2026-10-02 15:25:09 +02:00
Tim van der Meij 20b2e11213 Merge pull request #22038 from Snuffleupagus/rm-mimetype-export
Stop exporting the Font `mimetype` property
2026-10-02 15:12:44 +02:00
Tim van der Meij 0f116c95da Merge pull request #22033 from mozilla/dependabot/github_actions/github/codeql-action/init-4.38.1
Bump github/codeql-action/init from 4.38.0 to 4.38.1
2026-10-02 15:10:12 +02:00
Tim van der Meij f576c25bcd Merge pull request #22032 from mozilla/dependabot/github_actions/github/codeql-action/autobuild-4.38.1
Bump github/codeql-action/autobuild from 4.38.0 to 4.38.1
2026-10-02 15:09:47 +02:00
Tim van der Meij fecddae585 Merge pull request #22030 from mozilla/dependabot/github_actions/github/codeql-action/analyze-4.38.1
Bump github/codeql-action/analyze from 4.38.0 to 4.38.1
2026-10-02 15:09:17 +02:00
Tim van der Meij 637ed4cfe3 Merge pull request #22031 from mozilla/dependabot/github_actions/codecov/codecov-action-7.1.1
Bump codecov/codecov-action from 7.1.0 to 7.1.1
2026-10-02 14:53:14 +02:00
Tim van der Meij 5baa62329e Merge pull request #22054 from mozilla/dependabot/npm_and_yarn/multi-581aeff57e
Bump brace-expansion
2026-10-02 14:50:44 +02:00
Tim van der Meij fe0e9afdb1 Merge pull request #22053 from mozilla/dependabot/npm_and_yarn/undici-7.30.0
Bump undici from 7.29.0 to 7.30.0
2026-10-02 14:45:47 +02:00
Tim van der Meij 90448db27e Merge pull request #22055 from mozilla/dependabot/npm_and_yarn/markdown-it-14.3.2
Bump markdown-it from 14.2.0 to 14.3.2
2026-10-02 14:45:15 +02:00
Jonas Jenwald dc7ac33fcd Directly lookup entries in the cidToGidMap when building the charCodeToGlyphId map, for embedded composite fonts
Rather than first checking if the entry exists in the `cidToGidMap`, we can lookup it directly and instead fallback to `-1` when it doesn't exist.
2026-10-02 14:14:09 +02:00
dependabot[bot] b6753b1bf3 Bump markdown-it from 14.2.0 to 14.3.2
Bumps [markdown-it](https://github.com/markdown-it/markdown-it) from 14.2.0 to 14.3.2.
- [Changelog](https://github.com/markdown-it/markdown-it/blob/14.3.2/CHANGELOG.md)
- [Commits](https://github.com/markdown-it/markdown-it/compare/14.2.0...14.3.2)

---
updated-dependencies:
- dependency-name: markdown-it
  dependency-version: 14.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-02 10:34:54 +00:00
dependabot[bot] 1d43a5a956 Bump brace-expansion
Bumps  and [brace-expansion](https://github.com/juliangruber/brace-expansion). These dependencies needed to be updated together.

Updates `brace-expansion` from 2.1.4 to 2.1.7
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v2.1.4...v2.1.7)

Updates `brace-expansion` from 5.0.9 to 5.0.12
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v2.1.4...v2.1.7)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 2.1.7
  dependency-type: indirect
- dependency-name: brace-expansion
  dependency-version: 5.0.12
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-02 10:34:39 +00:00
dependabot[bot] c58ed927af Bump undici from 7.29.0 to 7.30.0
Bumps [undici](https://github.com/nodejs/undici) from 7.29.0 to 7.30.0.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v7.29.0...v7.30.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 7.30.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-02 10:34:39 +00:00
Jonas Jenwald 05566bd110 Stop exporting the Font mimetype property
- All embedded font data, regardless of how it's specified in the PDF, is always converted into OpenType in the worker-thread. This has been the case since "forever" in the PDF.js project, hence the value of `Font.prototype.mimetype` never varies (when actually set).

 - With the introduction of the CSS Font Loading API, in the font-loading code, the `mimetype` property is no longer used *by default* in the main-thread.

 - Given that `Font.prototype.mimetype` is either a string or `null`, the way that PR 20197 implemented the serialization/deserialization isn't actually correct since an explicit `null` value is being converted into a `"null"` string.
2026-10-01 14:14:26 +02:00
calixteman cf2db3a426 Bound tiling pattern folding work for tiny steps
Fold rows, then columns, and cap offsets by the rendered tile dimensions.
For the 10x10 fixture with 0.001 steps at scale 1, this reduces folding
from 100,020,001 drawImage calls to 22. Descending offsets draw the origin
tile last.

Add rendering tests for tiny steps, far-corner content, and overlap order.
2026-09-30 16:16:38 +02:00
dependabot[bot] f1c5c1513d Bump github/codeql-action/init from 4.38.0 to 4.38.1
Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.38.0 to 4.38.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-29 12:15:05 +00:00
dependabot[bot] 9a6b3808ba Bump github/codeql-action/autobuild from 4.38.0 to 4.38.1
Bumps [github/codeql-action/autobuild](https://github.com/github/codeql-action) from 4.38.0 to 4.38.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd)

---
updated-dependencies:
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-29 12:14:18 +00:00
dependabot[bot] 08526f78df Bump codecov/codecov-action from 7.1.0 to 7.1.1
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 7.1.0 to 7.1.1.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/0b35c9ecc4f0529d0eb674914510c22f85b196b4...303a32d7a59b442fa8d48b6a1cc6825c09c847a5)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: 7.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-29 12:13:25 +00:00
dependabot[bot] f1907720a8 Bump github/codeql-action/analyze from 4.38.0 to 4.38.1
Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.38.0 to 4.38.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd)

---
updated-dependencies:
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-29 12:13:19 +00:00
18 changed files with 337 additions and 128 deletions
+1 -1
View File
@@ -55,7 +55,7 @@ jobs:
enableCrossOsArchive: true enableCrossOsArchive: true
- name: Upload results to Codecov - name: Upload results to Codecov
uses: codecov/codecov-action@0b35c9ecc4f0529d0eb674914510c22f85b196b4 # v7.1.0 uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true fail_ci_if_error: true
+3 -3
View File
@@ -24,13 +24,13 @@ jobs:
persist-credentials: false persist-credentials: false
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 uses: github/codeql-action/init@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
queries: security-and-quality queries: security-and-quality
- name: Autobuild CodeQL - name: Autobuild CodeQL
uses: github/codeql-action/autobuild@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 uses: github/codeql-action/autobuild@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
- name: Perform CodeQL analysis - name: Perform CodeQL analysis
uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 uses: github/codeql-action/analyze@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
+1 -1
View File
@@ -91,7 +91,7 @@ jobs:
enableCrossOsArchive: true enableCrossOsArchive: true
- name: Upload results to Codecov - name: Upload results to Codecov
uses: codecov/codecov-action@0b35c9ecc4f0529d0eb674914510c22f85b196b4 # v7.1.0 uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true fail_ci_if_error: true
+1 -1
View File
@@ -79,7 +79,7 @@ jobs:
run: npx gulp fonttest --headless --coverage --coverage-output build/coverage/font ${{ matrix.skip }} run: npx gulp fonttest --headless --coverage --coverage-output build/coverage/font ${{ matrix.skip }}
- name: Upload results to Codecov - name: Upload results to Codecov
uses: codecov/codecov-action@0b35c9ecc4f0529d0eb674914510c22f85b196b4 # v7.1.0 uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true fail_ci_if_error: true
+1 -1
View File
@@ -98,7 +98,7 @@ jobs:
enableCrossOsArchive: true enableCrossOsArchive: true
- name: Upload results to Codecov - name: Upload results to Codecov
uses: codecov/codecov-action@0b35c9ecc4f0529d0eb674914510c22f85b196b4 # v7.1.0 uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true fail_ci_if_error: true
+1 -1
View File
@@ -85,7 +85,7 @@ jobs:
enableCrossOsArchive: true enableCrossOsArchive: true
- name: Upload results to Codecov - name: Upload results to Codecov
uses: codecov/codecov-action@0b35c9ecc4f0529d0eb674914510c22f85b196b4 # v7.1.0 uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true fail_ci_if_error: true
+14 -14
View File
@@ -5325,9 +5325,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "5.0.9", "version": "5.0.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz",
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -9141,9 +9141,9 @@
} }
}, },
"node_modules/markdown-it": { "node_modules/markdown-it": {
"version": "14.2.0", "version": "14.3.2",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.2.tgz",
"integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==", "integrity": "sha512-sHHjZ5fJKlgrG4qns2YwVcdNep35h5fERrfkD2YNsb9UFk0UIHarbiTaHKVMlPuWAoiilyK8Fv/jAm11slsY7Q==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -9158,8 +9158,8 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"argparse": "^2.0.1", "argparse": "^2.0.1",
"entities": "^4.4.0", "entities": "^4.5.0",
"linkify-it": "^5.0.1", "linkify-it": "^5.0.2",
"mdurl": "^2.0.0", "mdurl": "^2.0.0",
"punycode.js": "^2.3.1", "punycode.js": "^2.3.1",
"uc.micro": "^2.1.0" "uc.micro": "^2.1.0"
@@ -11951,9 +11951,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/test-exclude/node_modules/brace-expansion": { "node_modules/test-exclude/node_modules/brace-expansion": {
"version": "2.1.4", "version": "2.1.7",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz",
"integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -12422,9 +12422,9 @@
} }
}, },
"node_modules/undici": { "node_modules/undici": {
"version": "7.29.0", "version": "7.30.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.0.tgz",
"integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", "integrity": "sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
+1 -12
View File
@@ -96,7 +96,6 @@ const EXPORT_DATA_PROPERTIES = [
"isType3Font", "isType3Font",
"italic", "italic",
"loadedName", "loadedName",
"mimetype",
"missingFile", "missingFile",
"name", "name",
"remeasure", "remeasure",
@@ -1050,7 +1049,6 @@ class Font {
constructor(name, file, properties, evaluatorOptions) { constructor(name, file, properties, evaluatorOptions) {
this.name = name; this.name = name;
this.psName = null; this.psName = null;
this.mimetype = null;
this.disableFontFace = evaluatorOptions.disableFontFace; this.disableFontFace = evaluatorOptions.disableFontFace;
this.fontExtraProperties = evaluatorOptions.fontExtraProperties; this.fontExtraProperties = evaluatorOptions.fontExtraProperties;
@@ -1165,8 +1163,6 @@ class Font {
/* falls through */ /* falls through */
case "Type1": case "Type1":
case "CIDFontType0": case "CIDFontType0":
this.mimetype = "font/opentype";
const cff = const cff =
subtype === "Type1C" || subtype === "CIDFontType0C" subtype === "Type1C" || subtype === "CIDFontType0C"
? new CFFFont(file, properties) ? new CFFFont(file, properties)
@@ -1181,8 +1177,6 @@ class Font {
case "OpenType": case "OpenType":
case "TrueType": case "TrueType":
case "CIDFontType2": case "CIDFontType2":
this.mimetype = "font/opentype";
// Repair the TrueType file. It is can be damaged in the point of // Repair the TrueType file. It is can be damaged in the point of
// view of the sanitizer // view of the sanitizer
data = this.checkAndRepair(name, file, properties); data = this.checkAndRepair(name, file, properties);
@@ -3038,12 +3032,7 @@ class Font {
if (cid > 0xffff) { if (cid > 0xffff) {
throw new FormatError("Max size of CID is 65,535"); throw new FormatError("Max size of CID is 65,535");
} }
let glyphId = -1; const glyphId = isCidToGidMapEmpty ? cid : (cidToGidMap.get(cid) ?? -1);
if (isCidToGidMapEmpty) {
glyphId = cid;
} else if (cidToGidMap.has(cid)) {
glyphId = cidToGidMap.get(cid);
}
if (glyphId >= 0 && glyphId < numGlyphs && hasGlyph(glyphId)) { if (glyphId >= 0 && glyphId < numGlyphs && hasGlyph(glyphId)) {
charCodeToGlyphId.set(charCode, glyphId); charCodeToGlyphId.set(charCode, glyphId);
+8 -6
View File
@@ -423,12 +423,13 @@ class FontFaceObject {
} }
createNativeFontFace() { createNativeFontFace() {
if (!this.data || this.disableFontFace) { const { data } = this; // Get `data` just once, since it's not shadowed.
if (!data || this.disableFontFace) {
return null; return null;
} }
let nativeFontFace; let nativeFontFace;
if (!this.cssFontInfo) { if (!this.cssFontInfo) {
nativeFontFace = new FontFace(this.loadedName, this.data, {}); nativeFontFace = new FontFace(this.loadedName, data, {});
} else { } else {
const css = { const css = {
weight: this.cssFontInfo.fontWeight, weight: this.cssFontInfo.fontWeight,
@@ -438,7 +439,7 @@ class FontFaceObject {
} }
nativeFontFace = new FontFace( nativeFontFace = new FontFace(
serializeFontFamily(this.cssFontInfo.fontFamily), serializeFontFamily(this.cssFontInfo.fontFamily),
this.data, data,
css css
); );
} }
@@ -452,11 +453,12 @@ class FontFaceObject {
throw new Error("Not implemented: createFontFaceRule"); throw new Error("Not implemented: createFontFaceRule");
} }
if (!this.data || this.disableFontFace) { const { data } = this; // Get `data` just once, since it's not shadowed.
if (!data || this.disableFontFace) {
return null; return null;
} }
// Add the @font-face rule to the document. // Add the @font-face rule to the document.
const url = `url(data:${this.mimetype};base64,${this.data.toBase64()});`; const url = `url(data:${this.mimetype};base64,${data.toBase64()});`;
let rule; let rule;
if (!this.cssFontInfo) { if (!this.cssFontInfo) {
rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`; rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`;
@@ -567,7 +569,7 @@ class FontFaceObject {
} }
get mimetype() { get mimetype() {
return this.#fontData.mimetype; return this.missingFile ? null : "font/opentype";
} }
get name() { get name() {
+71 -75
View File
@@ -13,7 +13,13 @@
* limitations under the License. * limitations under the License.
*/ */
import { assert, BBOX_INIT, FeatureTest, Util } from "../shared/util.js"; import {
assert,
BBOX_INIT,
FeatureTest,
shadow,
Util,
} from "../shared/util.js";
import { import {
CSS_FONT_INFO, CSS_FONT_INFO,
FONT_INFO, FONT_INFO,
@@ -47,15 +53,15 @@ class CssFontInfo {
} }
get fontFamily() { get fontFamily() {
return this.#readString(0); return shadow(this, "fontFamily", this.#readString(0));
} }
get fontWeight() { get fontWeight() {
return this.#readString(1); return shadow(this, "fontWeight", this.#readString(1));
} }
get italicAngle() { get italicAngle() {
return this.#readString(2); return shadow(this, "italicAngle", this.#readString(2));
} }
} }
@@ -75,19 +81,19 @@ class SystemFontInfo {
} }
get css() { get css() {
return this.#readString(0); return shadow(this, "css", this.#readString(0));
} }
get loadedName() { get loadedName() {
return this.#readString(1); return shadow(this, "loadedName", this.#readString(1));
} }
get baseFontName() { get baseFontName() {
return this.#readString(2); return shadow(this, "baseFontName", this.#readString(2));
} }
get src() { get src() {
return this.#readString(3); return shadow(this, "src", this.#readString(3));
} }
get style() { get style() {
@@ -95,7 +101,7 @@ class SystemFontInfo {
offset += 4 + this.#view.getUint32(offset); offset += 4 + this.#view.getUint32(offset);
const style = readString(this.#buffer, this.#view, /* index = */ 0, offset), const style = readString(this.#buffer, this.#view, /* index = */ 0, offset),
weight = readString(this.#buffer, this.#view, /* index = */ 1, offset); weight = readString(this.#buffer, this.#view, /* index = */ 1, offset);
return { style, weight }; return shadow(this, "style", { style, weight });
} }
} }
@@ -121,43 +127,43 @@ class FontInfo {
} }
get black() { get black() {
return this.#readBoolean(0); return shadow(this, "black", this.#readBoolean(0));
} }
get bold() { get bold() {
return this.#readBoolean(1); return shadow(this, "bold", this.#readBoolean(1));
} }
get disableFontFace() { get disableFontFace() {
return this.#readBoolean(2); return shadow(this, "disableFontFace", this.#readBoolean(2));
} }
get fontExtraProperties() { get fontExtraProperties() {
return this.#readBoolean(3); return shadow(this, "fontExtraProperties", this.#readBoolean(3));
} }
get isInvalidPDFjsFont() { get isInvalidPDFjsFont() {
return this.#readBoolean(4); return shadow(this, "isInvalidPDFjsFont", this.#readBoolean(4));
} }
get isType3Font() { get isType3Font() {
return this.#readBoolean(5); return shadow(this, "isType3Font", this.#readBoolean(5));
} }
get italic() { get italic() {
return this.#readBoolean(6); return shadow(this, "italic", this.#readBoolean(6));
} }
get missingFile() { get missingFile() {
return this.#readBoolean(7); return shadow(this, "missingFile", this.#readBoolean(7));
} }
get remeasure() { get remeasure() {
return this.#readBoolean(8); return shadow(this, "remeasure", this.#readBoolean(8));
} }
get vertical() { get vertical() {
return this.#readBoolean(9); return shadow(this, "vertical", this.#readBoolean(9));
} }
#readNumber(index) { #readNumber(index) {
@@ -166,20 +172,19 @@ class FontInfo {
} }
get ascent() { get ascent() {
return this.#readNumber(0); return shadow(this, "ascent", this.#readNumber(0));
} }
get descent() { get descent() {
return this.#readNumber(1); return shadow(this, "descent", this.#readNumber(1));
} }
#readArray(offset, arrLen, lookupName, increment) { #readArray(offset, arrLen, lookupName, increment) {
const len = this.#view.getUint8(offset); const len = this.#view.getUint8(offset++);
if (len === 0) { if (len === 0) {
return undefined; return undefined;
} }
assert(len === arrLen, "Invalid array length."); assert(len === arrLen, "Invalid array length.");
offset += 1;
const arr = new Array(len); const arr = new Array(len);
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
arr[i] = this.#view[lookupName](offset, true); arr[i] = this.#view[lookupName](offset, true);
@@ -189,20 +194,28 @@ class FontInfo {
} }
get bbox() { get bbox() {
return this.#readArray( return shadow(
/* offset = */ FONT_INFO.OFFSET_BBOX, this,
/* arrLen = */ 4, "bbox",
/* lookup = */ "getInt16", this.#readArray(
/* increment = */ 2 /* offset = */ FONT_INFO.OFFSET_BBOX,
/* arrLen = */ 4,
/* lookup = */ "getInt16",
/* increment = */ 2
)
); );
} }
get fontMatrix() { get fontMatrix() {
return this.#readArray( return shadow(
/* offset = */ FONT_INFO.OFFSET_FONT_MATRIX, this,
/* arrLen = */ 6, "fontMatrix",
/* lookup = */ "getFloat64", this.#readArray(
/* increment = */ 8 /* offset = */ FONT_INFO.OFFSET_FONT_MATRIX,
/* arrLen = */ 6,
/* lookup = */ "getFloat64",
/* increment = */ 8
)
); );
} }
@@ -217,44 +230,39 @@ class FontInfo {
} }
get fallbackName() { get fallbackName() {
return this.#readString(0); return shadow(this, "fallbackName", this.#readString(0));
} }
get loadedName() { get loadedName() {
return this.#readString(1); return shadow(this, "loadedName", this.#readString(1));
}
get mimetype() {
return this.#readString(2);
} }
get name() { get name() {
return this.#readString(3); return shadow(this, "name", this.#readString(2));
} }
#getDataOffsets() { #getBufferOffset(index) {
let offset = FONT_INFO.OFFSET_STRINGS; let offset = FONT_INFO.OFFSET_STRINGS;
const stringsLength = this.#view.getUint32(offset); // We need to move past any string-data first, hence the `<=` condition.
offset += 4 + stringsLength; for (let i = 0; i <= index; i++) {
const systemFontInfoLength = this.#view.getUint32(offset); offset += 4 + this.#view.getUint32(offset);
offset += 4 + systemFontInfoLength; }
const cssFontInfoLength = this.#view.getUint32(offset);
offset += 4 + cssFontInfoLength;
const length = this.#view.getUint32(offset); const length = this.#view.getUint32(offset);
return { offset, length }; return { offset, length };
} }
// NOTE: This cannot be shadowed, since it may be cleared.
get data() { get data() {
const { offset, length } = this.#getDataOffsets(); const { offset, length } = this.#getBufferOffset(2);
return length === 0 return !length
? undefined ? undefined
: new Uint8Array(this.#buffer, offset + 4, length); : new Uint8Array(this.#buffer, offset + 4, length);
} }
clearData() { clearData() {
const { offset, length } = this.#getDataOffsets(); const { offset, length } = this.#getBufferOffset(2);
if (length === 0) { if (!length) {
return; // The data is either not present, or it was previously cleared. return; // The data is either not present, or it was previously cleared.
} }
this.#view.setUint32(offset, 0); // Zero the data-length. this.#view.setUint32(offset, 0); // Zero the data-length.
@@ -265,35 +273,23 @@ class FontInfo {
} }
get cssFontInfo() { get cssFontInfo() {
let offset = FONT_INFO.OFFSET_STRINGS; const { offset, length } = this.#getBufferOffset(1);
const stringsLength = this.#view.getUint32(offset); let info = null;
offset += 4 + stringsLength; if (length) {
const systemFontInfoLength = this.#view.getUint32(offset); const data = new Uint8Array(this.#buffer, offset + 4, length).slice();
offset += 4 + systemFontInfoLength; info = new CssFontInfo(data.buffer);
const cssFontInfoLength = this.#view.getUint32(offset);
if (cssFontInfoLength === 0) {
return null;
} }
const cssFontInfoData = new Uint8Array(cssFontInfoLength); return shadow(this, "cssFontInfo", info);
cssFontInfoData.set(
new Uint8Array(this.#buffer, offset + 4, cssFontInfoLength)
);
return new CssFontInfo(cssFontInfoData.buffer);
} }
get systemFontInfo() { get systemFontInfo() {
let offset = FONT_INFO.OFFSET_STRINGS; const { offset, length } = this.#getBufferOffset(0);
const stringsLength = this.#view.getUint32(offset); let info = null;
offset += 4 + stringsLength; if (length) {
const systemFontInfoLength = this.#view.getUint32(offset); const data = new Uint8Array(this.#buffer, offset + 4, length).slice();
if (systemFontInfoLength === 0) { info = new SystemFontInfo(data.buffer);
return null;
} }
const systemFontInfoData = new Uint8Array(systemFontInfoLength); return shadow(this, "systemFontInfo", info);
systemFontInfoData.set(
new Uint8Array(this.#buffer, offset + 4, systemFontInfoLength)
);
return new SystemFontInfo(systemFontInfoData.buffer);
} }
} }
+37 -9
View File
@@ -836,24 +836,52 @@ class TilingPattern {
const ySize = dimy2.size; const ySize = dimy2.size;
const tmpCanvas2 = owner.canvasFactory.create(xSize, ySize); const tmpCanvas2 = owner.canvasFactory.create(xSize, ySize);
const tmpCtx2 = tmpCanvas2.context; const tmpCtx2 = tmpCanvas2.context;
const ii = redrawHorizontally ? Math.floor(width / xstep) : 0; // Source rectangles outside the image contribute no pixels.
const jj = redrawVertically ? Math.floor(height / ystep) : 0; const ii = redrawHorizontally
? Math.min(Math.floor(width / xstep), Math.ceil(image.width / xSize))
: 0;
const jj = redrawVertically
? Math.min(Math.floor(height / ystep), Math.ceil(image.height / ySize))
: 0;
// Draw the overlapping parts of the original tile on the new tile. // Fold rows, then columns, to avoid drawing every offset pair.
for (let i = 0; i <= ii; i++) { // Descending indices draw the origin tile last.
for (let j = 0; j <= jj; j++) { let rowSource = image;
tmpCtx2.drawImage( let bandCanvas = null;
if (redrawVertically) {
bandCanvas = owner.canvasFactory.create(image.width, ySize);
const bandCtx = bandCanvas.context;
for (let j = jj; j >= 0; j--) {
bandCtx.drawImage(
image, image,
xSize * i, 0,
ySize * j, ySize * j,
xSize, image.width,
ySize, ySize,
0, 0,
0, 0,
xSize, image.width,
ySize ySize
); );
} }
rowSource = bandCanvas.canvas;
}
for (let i = ii; i >= 0; i--) {
tmpCtx2.drawImage(
rowSource,
xSize * i,
0,
xSize,
ySize,
0,
0,
xSize,
ySize
);
}
if (bandCanvas) {
owner.canvasFactory.destroy(bandCanvas);
} }
owner.canvasFactory.destroy(tmpCanvas); owner.canvasFactory.destroy(tmpCanvas);
return { return {
+1 -1
View File
@@ -39,7 +39,7 @@ class FONT_INFO {
static numbers = ["ascent", "descent"]; static numbers = ["ascent", "descent"];
static strings = ["fallbackName", "loadedName", "mimetype", "name"]; static strings = ["fallbackName", "loadedName", "name"];
static OFFSET_NUMBERS = Math.ceil((this.bools.length * 2) / 8); static OFFSET_NUMBERS = Math.ceil((this.bools.length * 2) / 8);
+3
View File
@@ -959,3 +959,6 @@
!nonembedded_type1_tounicode.pdf !nonembedded_type1_tounicode.pdf
!transfer_maps.pdf !transfer_maps.pdf
!resetform_parent.pdf !resetform_parent.pdf
!tiling-pattern-tiny-steps.pdf
!tiling-pattern-tiny-steps-large.pdf
!tiling-pattern-fold-order.pdf
+36
View File
@@ -0,0 +1,36 @@
%PDF-1.7
%âãÏÓ
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 20 20] /Resources << /Pattern << /P1 4 0 R >> >> /Contents 5 0 R >>
endobj
4 0 obj
<< /PatternType 1 /PaintType 1 /TilingType 1 /BBox [0 0 20 20] /XStep 10 /YStep 10 /Resources << >> /Length 49 >>
stream
1 0 0 rg 0 0 10 10 re f 0 0 1 rg 10 10 10 10 re f
endstream
endobj
5 0 obj
<< /Length 38 >>
stream
q /Pattern cs /P1 scn 0 0 20 20 re f Q
endstream
endobj
xref
0 6
0000000000 65535 f
0000000015 00000 n
0000000064 00000 n
0000000121 00000 n
0000000248 00000 n
0000000444 00000 n
trailer
<< /Size 6 /Root 1 0 R >>
startxref
532
%%EOF
@@ -0,0 +1,36 @@
%PDF-1.7
%âãÏÓ
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 1200 1200] /Resources << /Pattern << /P1 4 0 R >> >> /Contents 5 0 R >>
endobj
4 0 obj
<< /PatternType 1 /PaintType 1 /TilingType 1 /BBox [0 0 1200 1200] /XStep 0.001 /YStep 0.001 /Resources << >> /Length 26 >>
stream
q 1100 1100 100 100 re f Q
endstream
endobj
5 0 obj
<< /Length 42 >>
stream
q /Pattern cs /P1 scn 0 0 1200 1200 re f Q
endstream
endobj
xref
0 6
0000000000 65535 f
0000000015 00000 n
0000000064 00000 n
0000000121 00000 n
0000000252 00000 n
0000000435 00000 n
trailer
<< /Size 6 /Root 1 0 R >>
startxref
527
%%EOF
+35
View File
@@ -0,0 +1,35 @@
%PDF-1.7
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] /Resources << /Pattern << /P1 4 0 R >> >> /Contents 5 0 R >>
endobj
4 0 obj
<< /PatternType 1 /PaintType 1 /TilingType 1 /BBox [0 0 10 10] /XStep 0.001 /YStep 0.001 /Resources << >> /Length 19 >>
stream
q 0 0 10 10 re f Q
endstream
endobj
5 0 obj
<< /Length 41 >>
stream
q /Pattern cs /P1 scn 0 0 200 200 re f Q
endstream
endobj
xref
0 6
0000000000 65535 f
0000000009 00000 n
0000000058 00000 n
0000000115 00000 n
0000000244 00000 n
0000000415 00000 n
trailer
<< /Size 6 /Root 1 0 R >>
startxref
505
%%EOF
+86
View File
@@ -239,3 +239,89 @@ describe("custom ownerDocument", function () {
expect(ownerDocument.adoptedStyleSheets.length).toBe(0); expect(ownerDocument.adoptedStyleSheets.length).toBe(0);
}); });
}); });
describe("tiling pattern rendering", function () {
// The old folding loops required (10 / 0.001 + 1)^2 = 100,020,001 draws.
it("does not hang on tiny XStep/YStep steps", async function () {
const loadingTask = getDocument(
buildGetDocumentParams("tiling-pattern-tiny-steps.pdf")
);
const doc = await loadingTask.promise;
const page = await doc.getPage(1);
const viewport = page.getViewport({ scale: 1 });
const { canvasFactory } = doc;
const canvasAndCtx = canvasFactory.create(viewport.width, viewport.height);
await page.render({
canvas: canvasAndCtx.canvas,
viewport,
}).promise;
expect(getTopLeftPixel(canvasAndCtx.context)).toEqual({
r: 0,
g: 0,
b: 0,
a: 255,
});
canvasFactory.destroy(canvasAndCtx);
await loadingTask.destroy();
});
// Only the tile's far 100x100 corner is painted.
it("does not hang and keeps far-corner content on a large BBox with tiny steps", async function () {
const loadingTask = getDocument(
buildGetDocumentParams("tiling-pattern-tiny-steps-large.pdf")
);
const doc = await loadingTask.promise;
const page = await doc.getPage(1);
const viewport = page.getViewport({ scale: 1 });
const { canvasFactory } = doc;
const canvasAndCtx = canvasFactory.create(viewport.width, viewport.height);
await page.render({
canvas: canvasAndCtx.canvas,
viewport,
}).promise;
expect(getTopLeftPixel(canvasAndCtx.context)).toEqual({
r: 0,
g: 0,
b: 0,
a: 255,
});
canvasFactory.destroy(canvasAndCtx);
await loadingTask.destroy();
});
// The red and blue squares coincide after folding, exposing draw order.
it("places the origin tile on top when tiles overlap", async function () {
const loadingTask = getDocument(
buildGetDocumentParams("tiling-pattern-fold-order.pdf")
);
const doc = await loadingTask.promise;
const page = await doc.getPage(1);
const viewport = page.getViewport({ scale: 1 });
const { canvasFactory } = doc;
const canvasAndCtx = canvasFactory.create(viewport.width, viewport.height);
await page.render({
canvas: canvasAndCtx.canvas,
viewport,
}).promise;
expect(getTopLeftPixel(canvasAndCtx.context)).toEqual({
r: 255,
g: 0,
b: 0,
a: 255,
});
canvasFactory.destroy(canvasAndCtx);
await loadingTask.destroy();
});
});
+1 -3
View File
@@ -70,7 +70,6 @@ describe("obj_bin_transform", function () {
fontMatrix: [1, 1, 1, 1, 1, 1], fontMatrix: [1, 1, 1, 1, 1, 1],
fallbackName: "string", fallbackName: "string",
loadedName: "string", loadedName: "string",
mimetype: "string",
name: "string", name: "string",
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
uselessProp: "something", uselessProp: "something",
@@ -124,7 +123,7 @@ describe("obj_bin_transform", function () {
describe("FontInfo", function () { describe("FontInfo", function () {
it("must roundtrip correctly for FontInfo", function () { it("must roundtrip correctly for FontInfo", function () {
let sizeEstimate = 77; // fixed offset until the strings let sizeEstimate = 67; // fixed offset until the strings
const { encoder } = InfoUtils; const { encoder } = InfoUtils;
sizeEstimate += 4 + 4 * (4 + encoder.encode("string").length); sizeEstimate += 4 + 4 * (4 + encoder.encode("string").length);
sizeEstimate += 4 + 4; // cssFontInfo and systemFontInfo sizeEstimate += 4 + 4; // cssFontInfo and systemFontInfo
@@ -148,7 +147,6 @@ describe("obj_bin_transform", function () {
expect(deserialized.fontMatrix).toEqual([1, 1, 1, 1, 1, 1]); expect(deserialized.fontMatrix).toEqual([1, 1, 1, 1, 1, 1]);
expect(deserialized.fallbackName).toEqual("string"); expect(deserialized.fallbackName).toEqual("string");
expect(deserialized.loadedName).toEqual("string"); expect(deserialized.loadedName).toEqual("string");
expect(deserialized.mimetype).toEqual("string");
expect(deserialized.name).toEqual("string"); expect(deserialized.name).toEqual("string");
expect(Array.from(deserialized.data)).toEqual([ expect(Array.from(deserialized.data)).toEqual([
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,