Compare commits

..

12 Commits

Author SHA1 Message Date
Jonas Jenwald
50c573d16d
Merge pull request #19563 from Snuffleupagus/loadType3Data-once
Invoke `TranslatedFont.prototype.loadType3Data` only *once* per font
2025-02-27 11:20:26 +01:00
Jonas Jenwald
56a683bc3b
Merge pull request #19562 from Snuffleupagus/issue-19550
Extend `getGlyphMapForStandardFonts` with some Cyrillic entries (issue 19550)
2025-02-27 11:17:42 +01:00
Jonas Jenwald
4a48a7ec0f
Merge pull request #19557 from Snuffleupagus/PDFViewer-cleanupTimeouts
Abort various timeouts, in `PDFViewer`, when closing the document (PR 19128 follow-up)
2025-02-27 11:17:02 +01:00
Jonas Jenwald
21829f4157
Merge pull request #19556 from Snuffleupagus/_initializeViewerComponents-more-local-vars
Use more local variables in `PDFViewerApplication._initializeViewerComponents`
2025-02-27 11:16:04 +01:00
Jonas Jenwald
4e76a78341
Merge pull request #19549 from Snuffleupagus/fewer-EXPORT_DATA_PROPERTIES-2
[api-minor] Stop exporting, by default, a few additional Font properties (PR 11777 follow-up)
2025-02-27 11:15:27 +01:00
calixteman
a4fea2dafd
Merge pull request #19565 from calixteman/signature_fix_telemetry
[Editor] Fix the telemetry for the signature stuff
2025-02-27 10:10:13 +01:00
Calixte Denizet
bbf9bfc3c2 [Editor] Fix the telemetry for the signature stuff 2025-02-27 09:34:31 +01:00
Jonas Jenwald
bdfa96878d Invoke TranslatedFont.prototype.loadType3Data only *once* per font
Currently we're first loading the font, and then for Type3 fonts we're invoking `loadType3Data` every time that the font is encountered.
That seems completely unnecessary, and it's probably connected to the age of this code, since the `loadType3Data`-method will only run once anyway (note the caching).
2025-02-26 15:17:11 +01:00
Jonas Jenwald
59cb9a064e Extend getGlyphMapForStandardFonts with some Cyrillic entries (issue 19550) 2025-02-26 10:16:06 +01:00
Jonas Jenwald
71ad9fd0b7 Abort various timeouts, in PDFViewer, when closing the document (PR 19128 follow-up)
Looking at recent integration-test logs there's occasionally warnings about trying to invoke `PDFViewer.prototype.update` too late, which probably started with PR 19128 (see log excerpt below).
Given that we already had another timeout before that PR the problem was pre-existing, but it seems to trigger more easily now.

```
JavaScript warning: http://127.0.0.1:42333/build/generic/web/viewer.mjs, line 12668: Script terminated by timeout at:
update@http://127.0.0.1:42333/build/generic/web/viewer.mjs:12668:9
@http://127.0.0.1:42333/build/generic/web/viewer.mjs:12373:12
setTimeout handler*_scrollUpdate@http://127.0.0.1:42333/build/generic/web/viewer.mjs:12371:29
viewAreaElementScrolled@http://127.0.0.1:42333/build/generic/web/viewer.mjs:154:15
FrameRequestCallback*debounceScroll@http://127.0.0.1:42333/build/generic/web/viewer.mjs:140:18
EventListener.handleEvent*watchScroll@http://127.0.0.1:42333/build/generic/web/viewer.mjs:165:19
PDFViewer@http://127.0.0.1:42333/build/generic/web/viewer.mjs:11777:19
_initializeViewerComponents@http://127.0.0.1:42333/build/generic/web/viewer.mjs:15081:23
initialize@http://127.0.0.1:42333/build/generic/web/viewer.mjs:14931:16
async*run@http://127.0.0.1:42333/build/generic/web/viewer.mjs:15227:16
webViewerLoad@http://127.0.0.1:42333/build/generic/web/viewer.mjs:17078:24
@http://127.0.0.1:42333/build/generic/web/viewer.mjs:17082:3
```
2025-02-25 13:11:51 +01:00
Jonas Jenwald
cec32c6177 Use more local variables in PDFViewerApplication._initializeViewerComponents
This, ever so slightly, shortens the code which can never hurt.
2025-02-25 12:33:13 +01:00
Jonas Jenwald
132ccf04db [api-minor] Stop exporting, by default, a few additional Font properties (PR 11777 follow-up)
None of the "composite", "subtype", or "type" properties are normally used on the main-thread and/or in the API, hence there's no need to include them in the exported font-data by default.
Given that these properties may still be useful when debugging, and that `debugger.mjs` actually relies on the "type" property, they will instead only be sent to the main-thread when the `fontExtraProperties` API-option is used.
2025-02-24 12:27:51 +01:00
10 changed files with 233 additions and 84 deletions

View File

@ -1177,9 +1177,7 @@ class Catalog {
this.pageDictCache.clear();
this.nonBlendModesSet.clear();
const translatedFonts = await Promise.all(this.fontCache);
for (const { dict } of translatedFonts) {
for (const { dict } of await Promise.all(this.fontCache)) {
delete dict.cacheKey;
}
this.fontCache.clear();

View File

@ -1046,27 +1046,19 @@ class PartialEvaluator {
) {
const fontName = fontArgs?.[0] instanceof Name ? fontArgs[0].name : null;
let translated = await this.loadFont(
const translated = await this.loadFont(
fontName,
fontRef,
resources,
task,
fallbackFontDict,
cssFontInfo
);
if (translated.font.isType3Font) {
try {
await translated.loadType3Data(this, resources, task);
// Add the dependencies to the parent operatorList so they are
// resolved before Type3 operatorLists are executed synchronously.
operatorList.addDependencies(translated.type3Dependencies);
} catch (reason) {
translated = new TranslatedFont({
loadedName: "g_font_error",
font: new ErrorFont(`Type3 font load error: ${reason}`),
dict: translated.font,
});
}
// Add the dependencies to the parent operatorList so they are
// resolved before Type3 operatorLists are executed synchronously.
operatorList.addDependencies(translated.type3Dependencies);
}
state.font = translated.font;
@ -1228,6 +1220,7 @@ class PartialEvaluator {
fontName,
font,
resources,
task,
fallbackFontDict = null,
cssFontInfo = null
) {
@ -1356,14 +1349,21 @@ class PartialEvaluator {
font.loadedName = `${this.idFactory.getDocId()}_${fontID}`;
this.translateFont(preEvaluatedFont)
.then(translatedFont => {
resolve(
new TranslatedFont({
loadedName: font.loadedName,
font: translatedFont,
dict: font,
})
);
.then(async translatedFont => {
const translated = new TranslatedFont({
loadedName: font.loadedName,
font: translatedFont,
dict: font,
});
if (translatedFont.isType3Font) {
try {
await translated.loadType3Data(this, resources, task);
} catch (reason) {
throw new Error(`Type3 font load error: ${reason}`);
}
}
resolve(translated);
})
.catch(reason => {
// TODO reject?
@ -1372,9 +1372,7 @@ class PartialEvaluator {
resolve(
new TranslatedFont({
loadedName: font.loadedName,
font: new ErrorFont(
reason instanceof Error ? reason.message : reason
),
font: new ErrorFont(reason?.message),
dict: font,
})
);
@ -2616,16 +2614,12 @@ class PartialEvaluator {
}
async function handleSetFont(fontName, fontRef) {
const translated = await self.loadFont(fontName, fontRef, resources);
if (translated.font.isType3Font) {
try {
await translated.loadType3Data(self, resources, task);
} catch {
// Ignore Type3-parsing errors, since we only use `loadType3Data`
// here to ensure that we'll always obtain a useful /FontBBox.
}
}
const translated = await self.loadFont(
fontName,
fontRef,
resources,
task
);
textState.loadedName = translated.loadedName;
textState.font = translated.font;
@ -4602,20 +4596,22 @@ class PartialEvaluator {
}
class TranslatedFont {
#sent = false;
#type3Loaded = null;
constructor({ loadedName, font, dict }) {
this.loadedName = loadedName;
this.font = font;
this.dict = dict;
this.type3Loaded = null;
this.type3Dependencies = font.isType3Font ? new Set() : null;
this.sent = false;
}
send(handler) {
if (this.sent) {
if (this.#sent) {
return;
}
this.sent = true;
this.#sent = true;
handler.send("commonobj", [
this.loadedName,
@ -4645,12 +4641,12 @@ class TranslatedFont {
}
loadType3Data(evaluator, resources, task) {
if (this.type3Loaded) {
return this.type3Loaded;
}
if (!this.font.isType3Font) {
throw new Error("Must be a Type3 font.");
if (this.#type3Loaded) {
return this.#type3Loaded;
}
const { font, type3Dependencies } = this;
assert(font.isType3Font, "Must be a Type3 font.");
// When parsing Type3 glyphs, always ignore them if there are errors.
// Compared to the parsing of e.g. an entire page, it doesn't really
// make sense to only be able to render a Type3 glyph partially.
@ -4662,14 +4658,12 @@ class TranslatedFont {
}
type3Evaluator.type3FontRefs = type3FontRefs;
const translatedFont = this.font,
type3Dependencies = this.type3Dependencies;
let loadCharProcsPromise = Promise.resolve();
const charProcs = this.dict.get("CharProcs");
const fontResources = this.dict.get("Resources") || resources;
const charProcOperatorList = Object.create(null);
const fontBBox = Util.normalizeRect(translatedFont.bbox || [0, 0, 0, 0]),
const fontBBox = Util.normalizeRect(font.bbox || [0, 0, 0, 0]),
width = fontBBox[2] - fontBBox[0],
height = fontBBox[3] - fontBBox[1];
const fontBBoxSize = Math.hypot(width, height);
@ -4693,7 +4687,7 @@ class TranslatedFont {
// colour-related parameters) in the graphics state;
// any use of such operators shall be ignored."
if (operatorList.fnArray[0] === OPS.setCharWidthAndBounds) {
this._removeType3ColorOperators(operatorList, fontBBoxSize);
this.#removeType3ColorOperators(operatorList, fontBBoxSize);
}
charProcOperatorList[key] = operatorList.getIR();
@ -4708,20 +4702,17 @@ class TranslatedFont {
});
});
}
this.type3Loaded = loadCharProcsPromise.then(() => {
translatedFont.charProcOperatorList = charProcOperatorList;
this.#type3Loaded = loadCharProcsPromise.then(() => {
font.charProcOperatorList = charProcOperatorList;
if (this._bbox) {
translatedFont.isCharBBox = true;
translatedFont.bbox = this._bbox;
font.isCharBBox = true;
font.bbox = this._bbox;
}
});
return this.type3Loaded;
return this.#type3Loaded;
}
/**
* @private
*/
_removeType3ColorOperators(operatorList, fontBBoxSize = NaN) {
#removeType3ColorOperators(operatorList, fontBBoxSize = NaN) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert(
operatorList.fnArray[0] === OPS.setCharWidthAndBounds,

View File

@ -82,7 +82,6 @@ const EXPORT_DATA_PROPERTIES = [
"black",
"bold",
"charProcOperatorList",
"composite",
"cssFontInfo",
"data",
"defaultVMetrics",
@ -100,22 +99,23 @@ const EXPORT_DATA_PROPERTIES = [
"missingFile",
"name",
"remeasure",
"subtype",
"systemFontInfo",
"type",
"vertical",
];
const EXPORT_DATA_EXTRA_PROPERTIES = [
"cMap",
"composite",
"defaultEncoding",
"differences",
"isMonospace",
"isSerifFont",
"isSymbolicFont",
"seacMap",
"subtype",
"toFontChar",
"toUnicode",
"type",
"vmetrics",
"widths",
];

View File

@ -543,6 +543,37 @@ const getGlyphMapForStandardFonts = getLookupTableFactory(function (t) {
t[337] = 9552;
t[493] = 1039;
t[494] = 1040;
t[570] = 1040;
t[571] = 1041;
t[572] = 1042;
t[573] = 1043;
t[574] = 1044;
t[575] = 1045;
t[576] = 1046;
t[577] = 1047;
t[578] = 1048;
t[579] = 1049;
t[580] = 1050;
t[581] = 1051;
t[582] = 1052;
t[583] = 1053;
t[584] = 1054;
t[585] = 1055;
t[586] = 1056;
t[587] = 1057;
t[588] = 1058;
t[589] = 1059;
t[590] = 1060;
t[591] = 1061;
t[592] = 1062;
t[593] = 1063;
t[594] = 1064;
t[595] = 1065;
t[596] = 1066;
t[597] = 1067;
t[598] = 1068;
t[599] = 1069;
t[600] = 1070;
t[672] = 1488;
t[673] = 1489;
t[674] = 1490;

View File

@ -661,6 +661,7 @@
!bug1868759.pdf
!issue17730.pdf
!bug1883609.pdf
!issue19550.pdf
!issue17808.pdf
!issue17871_bottom_right.pdf
!issue17871_top_right.pdf

117
test/pdfs/issue19550.pdf Normal file

File diff suppressed because one or more lines are too long

View File

@ -3906,6 +3906,13 @@
"7R": false
}
},
{
"id": "issue19550",
"file": "pdfs/issue19550.pdf",
"md5": "4c978bd99348789e05dad1d2a919ddf0",
"rounds": 1,
"type": "eq"
},
{
"id": "issue1127-text",
"file": "pdfs/issue1127.pdf",

View File

@ -380,7 +380,8 @@ const PDFViewerApplication = {
* @private
*/
async _initializeViewerComponents() {
const { appConfig, externalServices, l10n } = this;
const { appConfig, externalServices, l10n, mlManager } = this;
const abortSignal = this._globalAbortController.signal;
const eventBus =
typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")
@ -391,7 +392,7 @@ const PDFViewerApplication = {
)
: new EventBus();
this.eventBus = AppOptions.eventBus = eventBus;
this.mlManager?.setEventBus(eventBus, this._globalAbortController.signal);
mlManager?.setEventBus(eventBus, abortSignal);
this.overlayManager = new OverlayManager();
@ -470,10 +471,7 @@ const PDFViewerApplication = {
null,
this.overlayManager,
l10n,
externalServices.createSignatureStorage(
eventBus,
this._globalAbortController.signal
),
externalServices.createSignatureStorage(eventBus, abortSignal),
eventBus
)
: null;
@ -510,8 +508,8 @@ const PDFViewerApplication = {
enableDetailCanvas: AppOptions.get("enableDetailCanvas"),
enablePermissions: AppOptions.get("enablePermissions"),
pageColors,
mlManager: this.mlManager,
abortSignal: this._globalAbortController.signal,
mlManager,
abortSignal,
enableHWA,
supportsPinchToZoom: this.supportsPinchToZoom,
enableAutoLinking: AppOptions.get("enableAutoLinking"),
@ -529,7 +527,7 @@ const PDFViewerApplication = {
renderingQueue: pdfRenderingQueue,
linkService: pdfLinkService,
pageColors,
abortSignal: this._globalAbortController.signal,
abortSignal,
enableHWA,
});
pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer);
@ -574,15 +572,12 @@ const PDFViewerApplication = {
}
}
if (
this.mlManager &&
appConfig.secondaryToolbar?.imageAltTextSettingsButton
) {
if (mlManager && appConfig.secondaryToolbar?.imageAltTextSettingsButton) {
this.imageAltTextSettings = new ImageAltTextSettings(
appConfig.altTextSettingsDialog,
this.overlayManager,
eventBus,
this.mlManager
mlManager
);
}

View File

@ -1173,6 +1173,7 @@ class PDFViewer {
this.#hiddenCopyElement?.remove();
this.#hiddenCopyElement = null;
this.#cleanupTimeouts();
this.#cleanupSwitchAnnotationEditorMode();
}
@ -2340,6 +2341,17 @@ class PDFViewer {
]);
}
#cleanupTimeouts() {
if (this.#scaleTimeoutId !== null) {
clearTimeout(this.#scaleTimeoutId);
this.#scaleTimeoutId = null;
}
if (this.#scrollTimeoutId !== null) {
clearTimeout(this.#scrollTimeoutId);
this.#scrollTimeoutId = null;
}
}
#cleanupSwitchAnnotationEditorMode() {
this.#switchAnnotationEditorModeAC?.abort();
this.#switchAnnotationEditorModeAC = null;
@ -2466,14 +2478,8 @@ class PDFViewer {
for (const pageView of this._pages) {
pageView.update(updateArgs);
}
if (this.#scaleTimeoutId !== null) {
clearTimeout(this.#scaleTimeoutId);
this.#scaleTimeoutId = null;
}
if (this.#scrollTimeoutId !== null) {
clearTimeout(this.#scrollTimeoutId);
this.#scrollTimeoutId = null;
}
this.#cleanupTimeouts();
if (!noUpdate) {
this.update();
}

View File

@ -177,6 +177,7 @@ class SignatureManager {
"click",
() => {
this.#reportTelemetry({
type: "signature",
action: "pdfjs.signature.clear",
data: {
type: this.#currentTab,
@ -738,6 +739,7 @@ class SignatureManager {
if (await this.#signatureStorage.delete(uuid)) {
div.remove();
this.#reportTelemetry({
type: "signature",
action: "pdfjs.signature.delete_saved",
data: {
savedCount: await this.#signatureStorage.size(),
@ -924,6 +926,7 @@ class SignatureManager {
const altText = this.#tabsToAltText.get(type);
this.#reportTelemetry({
type: "signature",
action: "pdfjs.signature.created",
data: {
type,