Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 39 additions & 8 deletions src/core/cff_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ const CFFStandardStrings = [

const NUM_STANDARD_CFF_STRINGS = 391;

const DEFAULT_BLUE_SCALE = 0.039625;
const DEFAULT_BLUE_SHIFT = 7;
const DEFAULT_BLUE_FUZZ = 1;
const DEFAULT_EXPANSION_FACTOR = 0.06;

const CharstringValidationData = [
/* 0 */ null,
/* 1 */ { id: "hstem", min: 2, stackClearing: true, stem: true },
Expand Down Expand Up @@ -262,8 +267,16 @@ class CFFParser {
properties.fontMatrix = fontMatrix;
}

const fontBBox = topDict.getByName("FontBBox");
if (fontBBox) {
let fontBBox = topDict.getByName("FontBBox");
if (fontBBox?.every(coord => coord === 0) && properties.bbox) {
fontBBox = Util.normalizeRect(
properties.bbox.map(coord =>
coord > 0x7fff && coord <= 0xffff ? coord - 0x10000 : coord
)
);
topDict.setByName("FontBBox", fontBBox);
}
if (fontBBox?.some(coord => coord !== 0)) {
// adjusting ascent/descent
properties.ascent = Math.max(fontBBox[3], fontBBox[1]);
properties.descent = Math.min(fontBBox[1], fontBBox[3]);
Expand Down Expand Up @@ -785,10 +798,28 @@ class CFFParser {
);
parentDict.privateDict = privateDict;

if (privateDict.getByName("ExpansionFactor") === 0) {
const blueScale = privateDict.getByName("BlueScale");
const blueShift = privateDict.getByName("BlueShift");
const blueFuzz = privateDict.getByName("BlueFuzz");
const expansionFactor = privateDict.getByName("ExpansionFactor");
if (
blueScale === 0 &&
blueShift === 0 &&
blueFuzz === 0 &&
expansionFactor === 0
) {
// Ghostscript can fail to initialize Private DICT defaults before
// writing them, which leaves omitted blue zone values as explicit
// zeroes. This has been seen in FDArray entries.
privateDict.setByName("BlueScale", DEFAULT_BLUE_SCALE);
privateDict.setByName("BlueShift", DEFAULT_BLUE_SHIFT);
privateDict.setByName("BlueFuzz", DEFAULT_BLUE_FUZZ);
}

if (expansionFactor === 0) {
// Firefox doesn't render correctly such a font on Windows (see issue
// 15289), hence we just reset it to its default value.
privateDict.setByName("ExpansionFactor", 0.06);
privateDict.setByName("ExpansionFactor", DEFAULT_EXPANSION_FACTOR);
}

// Parse the Subrs index also since it's relative to the private dict.
Expand Down Expand Up @@ -1247,16 +1278,16 @@ const CFFPrivateDictLayout = [
[7, "OtherBlues", "delta", null],
[8, "FamilyBlues", "delta", null],
[9, "FamilyOtherBlues", "delta", null],
[[12, 9], "BlueScale", "num", 0.039625],
[[12, 10], "BlueShift", "num", 7],
[[12, 11], "BlueFuzz", "num", 1],
[[12, 9], "BlueScale", "num", DEFAULT_BLUE_SCALE],
[[12, 10], "BlueShift", "num", DEFAULT_BLUE_SHIFT],
[[12, 11], "BlueFuzz", "num", DEFAULT_BLUE_FUZZ],
[10, "StdHW", "num", null],
[11, "StdVW", "num", null],
[[12, 12], "StemSnapH", "delta", null],
[[12, 13], "StemSnapV", "delta", null],
[[12, 14], "ForceBold", "num", 0],
[[12, 17], "LanguageGroup", "num", 0],
[[12, 18], "ExpansionFactor", "num", 0.06],
[[12, 18], "ExpansionFactor", "num", DEFAULT_EXPANSION_FACTOR],
[[12, 19], "initialRandomSeed", "num", 0],
[20, "defaultWidthX", "num", 0],
[21, "nominalWidthX", "num", 0],
Expand Down
65 changes: 65 additions & 0 deletions test/integration/document_properties_spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*/

import { closePages, FSI, loadAndWait, PDI } from "./test_utils.mjs";
import fs from "fs/promises";

const FIELDS = [
"fileName",
Expand Down Expand Up @@ -155,4 +156,68 @@ describe("PDFDocumentProperties", () => {
);
});
});

describe("Document without contentLength", () => {
let pages;

beforeEach(async () => {
pages = await loadAndWait("empty.pdf", ".textLayer .endOfContent");
});

afterEach(async () => {
await closePages(pages);
});

it("must check that the document properties dialog has the correct information", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
// Open a binary PDF document, such that `contentLength` is undefined.
const base64 = await fs.readFile("./pdfs/clippath.pdf", {
encoding: "base64",
});

await page.evaluate(async b64 => {
await window.PDFViewerApplication.open({
data: Uint8Array.fromBase64(b64),
});
}, base64);

await page.click("#secondaryToolbarToggleButton");
await page.waitForSelector("#secondaryToolbar", { hidden: false });

await page.click("#documentProperties");
await page.waitForSelector("#documentPropertiesDialog", {
hidden: false,
});

await page.waitForFunction(
`document.getElementById("fileSizeField").textContent !== "-"`
);
const props = await getFieldProperties(page);

expect(props).toEqual({
fileName: "document.pdf",
fileSize: `${FSI}0.448${PDI} KB (${FSI}459${PDI} bytes)`,
title: "-",
author: "-",
subject: "-",
keywords: "-",
creationDate: "-",
modificationDate: "-",
creator: "-",
producer: "-",
version: "1.1",
pageCount: "1",
pageSize: `${FSI}2.78${PDI} × ${FSI}1.39${PDI} ${FSI}in${PDI} (${FSI}landscape${PDI})`,
linearized: "No",
});

await page.click("#documentPropertiesClose");
await page.waitForSelector("#documentPropertiesDialog", {
hidden: true,
});
})
);
});
});
});
4 changes: 0 additions & 4 deletions test/integration/highlight_editor_spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1726,10 +1726,6 @@ describe("Highlight Editor", () => {
it("must check that an existing highlight is ignored on hovering", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
if (navigator.platform.includes("Win")) {
pending("Fails consistently on Windows (issue #20136).");
}

await switchToHighlight(page);

const rect = await getSpanRectFromText(
Expand Down
7 changes: 1 addition & 6 deletions test/integration/test_utils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -268,13 +268,8 @@ function getSelector(id) {
}

async function getRect(page, selector) {
// In Chrome something is wrong when serializing a `DomRect`,
// so we extract the values and return them ourselves.
await page.waitForSelector(selector, { visible: true });
return page.$eval(selector, el => {
const { x, y, width, height } = el.getBoundingClientRect();
return { x, y, width, height };
});
return (await page.$(selector)).boundingBox();
}

function getQuerySelector(id) {
Expand Down
8 changes: 8 additions & 0 deletions test/integration/viewer_spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,10 @@ describe("PDF viewer", () => {
);
});

afterEach(async () => {
await closePages(pages);
});

it("keeps the content under the pinch centre fixed on the screen", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
Expand Down Expand Up @@ -1610,6 +1614,10 @@ describe("PDF viewer", () => {
);
});

afterEach(async () => {
await closePages(pages);
});

it("Check that the top right corner of the annotation is centered vertically", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
Expand Down
2 changes: 1 addition & 1 deletion test/test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,7 @@ async function startBrowser({
// calls can run before events triggered by the previous protocol calls had
// a chance to be processed (essentially causing events to get lost). This
// value gives Chrome a more similar execution speed as Firefox.
options.slowMo = 5;
options.slowMo = 3;

// avoid crash
options.args = ["--no-sandbox", "--disable-setuid-sandbox"];
Expand Down
73 changes: 73 additions & 0 deletions test/unit/cff_parser_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import {
CFFCompiler,
CFFFDSelect,
CFFParser,
CFFPrivateDict,
CFFStrings,
CFFTopDict,
} from "../../src/core/cff_parser.js";
import { SEAC_ANALYSIS_ENABLED } from "../../src/core/fonts_utils.js";
import { Stream } from "../../src/core/stream.js";
Expand Down Expand Up @@ -112,6 +114,77 @@ describe("CFFParser", function () {
expect(topDict.getByName("Private")).toEqual([45, 102]);
});

it("ignores an empty FontBBox when adjusting ascent/descent", function () {
cff.topDict.setByName("FontBBox", [0, 0, 0, 0]);
const fontDataWithEmptyBBox = new CFFCompiler(cff).compile();

const properties = {
ascent: 800,
descent: -200,
};
new CFFParser(
new Stream(fontDataWithEmptyBBox),
properties,
SEAC_ANALYSIS_ENABLED
).parse();

expect(properties.ascent).toEqual(800);
expect(properties.descent).toEqual(-200);
expect(properties.ascentScaled).toBeUndefined();
});

it("repairs an empty FontBBox from font descriptor data", function () {
cff.topDict.setByName("FontBBox", [0, 0, 0, 0]);
const fontDataWithEmptyBBox = new CFFCompiler(cff).compile();

const properties = {
bbox: [2974, -300, 64236, 900],
};
const reparsedCff = new CFFParser(
new Stream(fontDataWithEmptyBBox),
properties,
SEAC_ANALYSIS_ENABLED
).parse();

expect(reparsedCff.topDict.getByName("FontBBox")).toEqual([
-1300, -300, 2974, 900,
]);
expect(properties.ascent).toEqual(900);
expect(properties.descent).toEqual(-300);
expect(properties.ascentScaled).toEqual(true);
});

it("repairs likely Ghostscript-zeroed FDArray private defaults", function () {
cff.isCIDFont = true;
cff.topDict.setByName("ROS", [0, 0, 0]);
cff.topDict.setByName("FDSelect", 0);
cff.topDict.setByName("FDArray", 0);

const fdDict = new CFFTopDict(cff.strings);
fdDict.setByName("Private", [0, 0]);
fdDict.privateDict = new CFFPrivateDict(cff.strings);
fdDict.privateDict.setByName("BlueScale", 0);
fdDict.privateDict.setByName("BlueShift", 0);
fdDict.privateDict.setByName("BlueFuzz", 0);
fdDict.privateDict.setByName("ExpansionFactor", 0);

cff.fdArray = [fdDict];
cff.fdSelect = new CFFFDSelect(0, Array(cff.charStrings.count).fill(0));
const fontDataWithBrokenFDPrivate = new CFFCompiler(cff).compile();

const reparsedCff = new CFFParser(
new Stream(fontDataWithBrokenFDPrivate),
{},
SEAC_ANALYSIS_ENABLED
).parse();
const privateDict = reparsedCff.fdArray[0].privateDict;

expect(privateDict.getByName("BlueScale")).toEqual(0.039625);
expect(privateDict.getByName("BlueShift")).toEqual(7);
expect(privateDict.getByName("BlueFuzz")).toEqual(1);
expect(privateDict.getByName("ExpansionFactor")).toEqual(0.06);
});

it("refuses to add topDict key with invalid value (bug 1068432)", function () {
const topDict = cff.topDict;
const defaultValue = topDict.getByName("UnderlinePosition");
Expand Down
Loading