Compare commits

..

10 Commits

Author SHA1 Message Date
Jonas Jenwald
aa70b28365
Merge pull request #19555 from rossj/master
Pad rev 4 encryption keys to be >= 16 bytes (issue 19484)
2025-02-25 10:03:56 +01:00
calixteman
e999f77959
Merge pull request #19554 from calixteman/signature_telemetry
[Editor] Add some telemetry for the signature editor (bug 1945827)
2025-02-24 22:53:49 +01:00
Ross Johnson
4f25d7f6cd Fix decryption of R=4, V=4 files with < 16-byte keys by 0-padding - undocumented but matches Acrobat behavior (issue #19484) 2025-02-24 15:36:37 -06:00
Calixte Denizet
9e672ff05e [Editor] Add some telemetry for the signature editor (bug 1945827) 2025-02-24 21:10:04 +01:00
calixteman
fef706233d
Merge pull request #19551 from Snuffleupagus/loadSystemFont-fix-assert
Fix the `assert` in `FontLoader.prototype.loadSystemFont`
2025-02-24 16:49:03 +01:00
calixteman
12d2f1b04a
Merge pull request #19552 from calixteman/add_generated_header_openjpeg
Add a comment 'THIS FILE IS GENERATED - DO NOT EDIT' in openjpeg files generated with emscripten
2025-02-24 16:17:48 +01:00
Jonas Jenwald
2966171a2b Fix the assert in FontLoader.prototype.loadSystemFont
Currently this `assert` isn't actually doing what it's supposed to, since the `FontLoader`-class doesn't have a `disableFontFace`-field.
The `FontFaceObject`-class on the other hand has such a field, hence we update the method-signature to be able to check the intended thing.
2025-02-24 16:09:07 +01:00
calixteman
f9aa8e1d15
Merge pull request #19539 from calixteman/paste_signature
[Editor] Shift the signature editor when pasted
2025-02-24 16:07:50 +01:00
Calixte Denizet
c099245daa Add a comment 'THIS FILE IS GENERATED - DO NOT EDIT' in openjpeg files generated with emscripten 2025-02-24 16:05:19 +01:00
Calixte Denizet
cc3d6ab539 [Editor] Shift the signature editor when pasted
The idea is to avoid to have the pasted editor hidding the copied one:
the user could think that nothing happened.
So the top-left corner of the pasted one is moved to the bottom-right corner of the copied one.
2025-02-23 21:35:01 +01:00
31 changed files with 315 additions and 7790 deletions

View File

@ -1,66 +0,0 @@
import fs from "fs";
import { getDocument } from "../../build/dist/build/pdf.mjs";
// Some PDFs need external cmaps.
const CMAP_URL = "../../../node_modules/pdfjs-dist/cmaps/";
const CMAP_PACKED = true;
// Where the standard fonts are located.
const STANDARD_FONT_DATA_URL =
"../../../node_modules/pdfjs-dist/standard_fonts/";
// Loading file from file system into typed array.
const pdfPath =
process.argv[2] || "C:\\Users\\kj131\\pdf-forge\\test_pdfs\\ISO_32000-2_2020(en).pdf";
const data = new Uint8Array(fs.readFileSync(pdfPath));
// Load the PDF file.
const loadingTask = getDocument({
data,
cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED,
standardFontDataUrl: STANDARD_FONT_DATA_URL,
});
test(loadingTask);
async function test(loading) {
try {
const pdfDocument = await loading.promise;
console.log("# PDF document loaded.");
const page = await pdfDocument.getPage(4);
printOpList(page);
console.time("contents");
const contents = await page.getContents();
console.timeEnd("contents");
console.time("oplist");
const opList = await page.getOperatorList();
console.timeEnd("oplist");
// console.log(opList);
let newContents = "";
for (let i = 0; i < 5; i++) {
const range = opList.rangeArray[i];
if (range) {
newContents += contents.slice(range[0], range[1]);
newContents += "\n";
}
}
console.log(newContents);
await page.updateContents(newContents);
await printOpList(page);
} catch (e) {
console.error(e);
}
}
async function printOpList(page) {
const contents = await page.getContents();
const opList = await page.getOperatorList();
// console.log(opList);
const ops = [];
for (let i = 0; i < opList.rangeArray.length; i++) {
const range = opList.rangeArray[i];
if (range) {
ops.push(contents.slice(range[0], range[1]));
}
}
console.log(ops.slice(0, 100));
}

View File

@ -1,160 +0,0 @@
import { EvaluatorPreprocessor } from "../../src/core/evaluator.js";
import {
getImageAsBlob,
getPrim,
getPrimitive,
getPrimTree,
getStreamAsString,
} from "../../src/core/obj_walker.js";
import { Lexer, Parser } from "../../src/core/parser.js";
import { EOF } from "../../src/core/primitives.js";
import fs from "fs";
import { PDFDocument } from "../../src/core/document.js";
import { Stream } from "../../src/core/stream.js";
const filePath =
"C:\\Users\\kj131\\pdf-forge\\test_pdfs\\ISO_32000-2_2020(en).pdf";
fs.readFile(filePath, (err, data) => {
if (err) {
console.error("Error reading file:", err);
return;
}
console.log("Reading file");
const stream = new Stream(data); // No need for Uint8Array, `data` is already a buffer
const manager = { enableXfa: false };
const doc = new PDFDocument(manager, stream);
try {
doc.parseStartXRef();
doc.parse(false);
console.log("Number of pages:", doc.numPages);
parse(doc);
} catch (e) {
console.error("Failed to parse PDF:", e);
}
});
function bytesToString(bytes) {
let string = "";
for (let i = 0; i < bytes.length; i++) {
string += String.fromCharCode(bytes[i]);
}
return string;
}
async function parse(doc) {
const path = "/Page2/Contents/1";
let [stream] = await getPrimitive(path, doc);
const lexer = new Lexer(stream);
const parser = new Parser({ lexer, xref: doc.xref, trackRanges: true });
const objs = [];
let [obj, start, end] = parser.getObjWithRange();
while (obj !== EOF) {
if (obj.cmd) {
objs.push([obj.cmd, start, end]);
} else {
objs.push([obj, start, end]);
}
[obj, start, end] = parser.getObjWithRange();
}
[stream] = await getPrimitive(path, doc);
const bytes = stream.getBytes();
const classes = new Set();
for (const o of objs) {
// console.log(o[0].constructor.name);
classes.add(o[0].constructor.name);
const lexemmeBytes = bytes.slice(o[1], o[2]);
// console.log(bytesToString(lexemmeBytes));
}
// console.log("unique classes", classes);
[stream] = await getPrimitive(path, doc);
const preprocessor = new EvaluatorPreprocessor(stream, doc.xref);
const operation = {};
operation.args = null;
while (preprocessor.read(operation)) {
const args = operation.args;
const fn = operation.fn;
const range = operation.range;
const op = bytesToString(bytes.slice(range[0], range[1]));
// console.log(args, fn);
// console.log(`----------------- ${range} -------------------`);
console.log(`${fn}: ${op}`);
// console.log(`---------------------------------------------`);
}
// console.time("xref");
// let table = await retrieveXref(doc);
// console.timeEnd("xref");
// console.time("get prim");
// const prim = await getPrim("/Trailer/Root/Names/Dests/Names/1", doc);
// console.timeEnd("get prim");
// console.log(prim);
// const request = {
// key: "Page2",
// children: [
// { key: "CropBox" },
// { key: "Contents", children: [{ key: "1" , children: [{ key: "Length" }]}] },
// {
// key: "Resources",
// children: [{ key: "ProcSet" }],
// },
// ],
// };
// console.time("get tree");
// const tree = await getPrimTree([request], doc);
// console.timeEnd("get tree");
// logTree(tree);
// console.time("string");
// const string = await getStreamAsString("/Page2/Contents/0/Data", doc);
// console.timeEnd("string");
// console.log(string);
// console.time("image");
// // const image = await getStreamAsImage("/Page2/Contents/2/Data", doc);
// const blob = await getImageAsBlob(
// "/Page2/Resources/XObject/Im0/Data",
// doc
// );
// console.timeEnd("image");
// console.log(blob);
// saveBlobToFile(blob, "image.png");
}
async function saveBlobToFile(blob, imagePath) {
const buffer = await blobToBuffer(blob);
fs.writeFile(imagePath, buffer, err => {
if (err) {
console.error("Error saving file:", err);
} else {
console.log("File saved successfully!");
}
});
}
function blobToBuffer(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(Buffer.from(reader.result));
reader.onerror = reject;
reader.readAsArrayBuffer(blob);
});
}
function logTree(tree) {
for (const node of tree) {
let str = " ".repeat(node.depth);
if (!node.container) {
str += " ";
} else {
str += node.expanded ? "v " : "> ";
}
str += node.key + " | " + node.ptype + " | ";
// if (node.sub_type !== "-") {
// str += node.sub_type + " | ";
// }
// str += node.value;
str += node.trace.map(t => t.key).join(", ");
console.log(str);
}
}

View File

@ -1,3 +1,4 @@
/* THIS FILE IS GENERATED - DO NOT EDIT */
var OpenJPEG = (() => { var OpenJPEG = (() => {
var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined; var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined;

View File

@ -1,3 +1,4 @@
/* THIS FILE IS GENERATED - DO NOT EDIT */
var OpenJPEG = (() => { var OpenJPEG = (() => {
var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined; var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined;

View File

@ -1,5 +1,5 @@
{ {
"stableVersion": "5.0.229", "stableVersion": "4.10.38",
"baseVersion": "7a57af12e13a47927c460e6b739a6ca132e7603d", "baseVersion": "7a57af12e13a47927c460e6b739a6ca132e7603d",
"versionPrefix": "5.0." "versionPrefix": "5.0."
} }

View File

@ -1787,7 +1787,14 @@ class CipherTransformFactory {
); );
} }
this.encryptionKey = encryptionKey; if (algorithm === 4 && encryptionKey.length < 16) {
// Extend key to 16 byte minimum (undocumented),
// fixes issue19484_1.pdf and issue19484_2.pdf.
this.encryptionKey = new Uint8Array(16);
this.encryptionKey.set(encryptionKey);
} else {
this.encryptionKey = encryptionKey;
}
if (algorithm >= 4) { if (algorithm >= 4) {
const cf = dict.get("CF"); const cf = dict.get("CF");

View File

@ -65,7 +65,7 @@ import { Catalog } from "./catalog.js";
import { clearGlobalCaches } from "./cleanup_helper.js"; import { clearGlobalCaches } from "./cleanup_helper.js";
import { DatasetReader } from "./dataset_reader.js"; import { DatasetReader } from "./dataset_reader.js";
import { Linearization } from "./parser.js"; import { Linearization } from "./parser.js";
import { NullStream, StringStream } from "./stream.js"; import { NullStream } from "./stream.js";
import { ObjectLoader } from "./object_loader.js"; import { ObjectLoader } from "./object_loader.js";
import { OperatorList } from "./operator_list.js"; import { OperatorList } from "./operator_list.js";
import { PartialEvaluator } from "./evaluator.js"; import { PartialEvaluator } from "./evaluator.js";
@ -107,8 +107,6 @@ class Page {
this.resourcesPromise = null; this.resourcesPromise = null;
this.xfaFactory = xfaFactory; this.xfaFactory = xfaFactory;
this.updatedContents = null;
const idCounters = { const idCounters = {
obj: 0, obj: 0,
}; };
@ -248,19 +246,10 @@ class Page {
throw reason; throw reason;
} }
setContents(newContents) {
this.updatedContents = newContents;
}
/** /**
* @returns {Promise<BaseStream>} * @returns {Promise<BaseStream>}
*/ */
getContentStream() { getContentStream() {
if (this.updatedContents !== null) {
return new Promise(resolve => {
resolve(new StringStream(this.updatedContents));
});
}
return this.pdfManager.ensure(this, "content").then(content => { return this.pdfManager.ensure(this, "content").then(content => {
if (content instanceof BaseStream) { if (content instanceof BaseStream) {
return content; return content;
@ -437,11 +426,8 @@ class Page {
cacheKey, cacheKey,
annotationStorage = null, annotationStorage = null,
modifiedIds = null, modifiedIds = null,
contentOverride = null,
}) { }) {
const contentStreamPromise = contentOverride const contentStreamPromise = this.getContentStream();
? new StringStream(contentOverride)
: this.getContentStream();
const resourcesPromise = this.loadResources([ const resourcesPromise = this.loadResources([
"ColorSpace", "ColorSpace",
"ExtGState", "ExtGState",

View File

@ -177,17 +177,11 @@ function normalizeBlendMode(value, parsingArray = false) {
return "source-over"; return "source-over";
} }
function addLocallyCachedImageOps(opList, data, range) { function addLocallyCachedImageOps(opList, data) {
if (data.objId) { if (data.objId) {
opList.addDependency(data.objId); opList.addDependency(data.objId);
} }
opList.addImageOps( opList.addImageOps(data.fn, data.args, data.optionalContent, data.hasMask);
data.fn,
data.args,
range,
data.optionalContent,
data.hasMask
);
if (data.fn === OPS.paintImageMaskXObject && data.args[0]?.count > 0) { if (data.fn === OPS.paintImageMaskXObject && data.args[0]?.count > 0) {
data.args[0].count++; data.args[0].count++;
@ -461,8 +455,7 @@ class PartialEvaluator {
operatorList, operatorList,
task, task,
initialState, initialState,
localColorSpaceCache, localColorSpaceCache
range = null
) { ) {
const dict = xobj.dict; const dict = xobj.dict;
const matrix = lookupMatrix(dict.getArray("Matrix"), null); const matrix = lookupMatrix(dict.getArray("Matrix"), null);
@ -476,11 +469,7 @@ class PartialEvaluator {
); );
} }
if (optionalContent !== undefined) { if (optionalContent !== undefined) {
operatorList.addOp( operatorList.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]);
OPS.beginMarkedContentProps,
["OC", optionalContent],
range
);
} }
const group = dict.get("Group"); const group = dict.get("Group");
if (group) { if (group) {
@ -522,14 +511,14 @@ class PartialEvaluator {
smask.backdrop = colorSpace.getRgb(smask.backdrop, 0); smask.backdrop = colorSpace.getRgb(smask.backdrop, 0);
} }
operatorList.addOp(OPS.beginGroup, [groupOptions], range); operatorList.addOp(OPS.beginGroup, [groupOptions]);
} }
// If it's a group, a new canvas will be created that is the size of the // If it's a group, a new canvas will be created that is the size of the
// bounding box and translated to the correct position so we don't need to // bounding box and translated to the correct position so we don't need to
// apply the bounding box to it. // apply the bounding box to it.
const args = group ? [matrix, null] : [matrix, bbox]; const args = group ? [matrix, null] : [matrix, bbox];
operatorList.addOp(OPS.paintFormXObjectBegin, args, range); operatorList.addOp(OPS.paintFormXObjectBegin, args);
await this.getOperatorList({ await this.getOperatorList({
stream: xobj, stream: xobj,
@ -538,14 +527,14 @@ class PartialEvaluator {
operatorList, operatorList,
initialState, initialState,
}); });
operatorList.addOp(OPS.paintFormXObjectEnd, [], range); operatorList.addOp(OPS.paintFormXObjectEnd, []);
if (group) { if (group) {
operatorList.addOp(OPS.endGroup, [groupOptions], range); operatorList.addOp(OPS.endGroup, [groupOptions]);
} }
if (optionalContent !== undefined) { if (optionalContent !== undefined) {
operatorList.addOp(OPS.endMarkedContent, [], range); operatorList.addOp(OPS.endMarkedContent, []);
} }
} }
@ -580,7 +569,6 @@ class PartialEvaluator {
cacheKey, cacheKey,
localImageCache, localImageCache,
localColorSpaceCache, localColorSpaceCache,
range,
}) { }) {
const dict = image.dict; const dict = image.dict;
const imageRef = dict.objId; const imageRef = dict.objId;
@ -639,7 +627,6 @@ class PartialEvaluator {
operatorList.addImageOps( operatorList.addImageOps(
OPS.paintImageMaskXObject, OPS.paintImageMaskXObject,
args, args,
range,
optionalContent optionalContent
); );
@ -802,7 +789,6 @@ class PartialEvaluator {
operatorList.addImageOps( operatorList.addImageOps(
OPS.paintImageXObject, OPS.paintImageXObject,
args, args,
range,
optionalContent, optionalContent,
hasMask hasMask
); );
@ -913,8 +899,7 @@ class PartialEvaluator {
operatorList, operatorList,
task, task,
stateManager, stateManager,
localColorSpaceCache, localColorSpaceCache
range
) { ) {
const smaskContent = smask.get("G"); const smaskContent = smask.get("G");
const smaskOptions = { const smaskOptions = {
@ -944,8 +929,7 @@ class PartialEvaluator {
operatorList, operatorList,
task, task,
stateManager.state.clone(), stateManager.state.clone(),
localColorSpaceCache, localColorSpaceCache
range
); );
} }
@ -1029,7 +1013,7 @@ class PartialEvaluator {
// Add the dependencies to the parent operator list so they are // Add the dependencies to the parent operator list so they are
// resolved before the sub operator list is executed synchronously. // resolved before the sub operator list is executed synchronously.
operatorList.addDependencies(tilingOpList.dependencies); operatorList.addDependencies(tilingOpList.dependencies);
operatorList.addOp(fn, tilingPatternIR, range); operatorList.addOp(fn, tilingPatternIR);
if (patternDict.objId) { if (patternDict.objId) {
localTilingPatternCache.set(/* name = */ null, patternDict.objId, { localTilingPatternCache.set(/* name = */ null, patternDict.objId, {
@ -1140,7 +1124,6 @@ class PartialEvaluator {
stateManager, stateManager,
localGStateCache, localGStateCache,
localColorSpaceCache, localColorSpaceCache,
range,
}) { }) {
const gStateRef = gState.objId; const gStateRef = gState.objId;
let isSimpleGState = true; let isSimpleGState = true;
@ -1197,8 +1180,7 @@ class PartialEvaluator {
operatorList, operatorList,
task, task,
stateManager, stateManager,
localColorSpaceCache, localColorSpaceCache
range
) )
); );
gStateObj.push([key, true]); gStateObj.push([key, true]);
@ -1236,7 +1218,7 @@ class PartialEvaluator {
await promise; await promise;
if (gStateObj.length > 0) { if (gStateObj.length > 0) {
operatorList.addOp(OPS.setGState, [gStateObj], range); operatorList.addOp(OPS.setGState, [gStateObj]);
} }
if (isSimpleGState) { if (isSimpleGState) {
@ -1405,7 +1387,7 @@ class PartialEvaluator {
return promise; return promise;
} }
buildPath(operatorList, fn, args, parsingText = false, range = null) { buildPath(operatorList, fn, args, parsingText = false) {
const lastIndex = operatorList.length - 1; const lastIndex = operatorList.length - 1;
if (!args) { if (!args) {
args = []; args = [];
@ -1423,7 +1405,7 @@ class PartialEvaluator {
// *extremely* rare that shouldn't really matter much in practice. // *extremely* rare that shouldn't really matter much in practice.
if (parsingText) { if (parsingText) {
warn(`Encountered path operator "${fn}" inside of a text object.`); warn(`Encountered path operator "${fn}" inside of a text object.`);
operatorList.addOp(OPS.save, null, range); operatorList.addOp(OPS.save, null);
} }
let minMax; let minMax;
@ -1446,11 +1428,7 @@ class PartialEvaluator {
minMax = [Infinity, Infinity, -Infinity, -Infinity]; minMax = [Infinity, Infinity, -Infinity, -Infinity];
break; break;
} }
operatorList.addOp( operatorList.addOp(OPS.constructPath, [[fn], args, minMax]);
OPS.constructPath,
[[fn], args, minMax, [range[0], range[1]]],
range
);
if (parsingText) { if (parsingText) {
operatorList.addOp(OPS.restore, null); operatorList.addOp(OPS.restore, null);
@ -1459,12 +1437,8 @@ class PartialEvaluator {
const opArgs = operatorList.argsArray[lastIndex]; const opArgs = operatorList.argsArray[lastIndex];
opArgs[0].push(fn); opArgs[0].push(fn);
opArgs[1].push(...args); opArgs[1].push(...args);
opArgs[3].push(...range);
const minMax = opArgs[2]; const minMax = opArgs[2];
const opRange = operatorList.rangeArray[lastIndex];
opRange[1] = range[1];
// Compute min/max in the worker instead of the main thread. // Compute min/max in the worker instead of the main thread.
// If the current matrix (when drawing) is a scaling one // If the current matrix (when drawing) is a scaling one
// then min/max can be easily computed in using those values. // then min/max can be easily computed in using those values.
@ -1569,8 +1543,7 @@ class PartialEvaluator {
task, task,
localColorSpaceCache, localColorSpaceCache,
localTilingPatternCache, localTilingPatternCache,
localShadingPatternCache, localShadingPatternCache
range
) { ) {
// compile tiling patterns // compile tiling patterns
const patternName = args.pop(); const patternName = args.pop();
@ -1589,7 +1562,7 @@ class PartialEvaluator {
localTilingPattern.dict, localTilingPattern.dict,
color color
); );
operatorList.addOp(fn, tilingPatternIR, range); operatorList.addOp(fn, tilingPatternIR);
return undefined; return undefined;
} catch { } catch {
// Handle any errors during normal TilingPattern parsing. // Handle any errors during normal TilingPattern parsing.
@ -1623,7 +1596,7 @@ class PartialEvaluator {
}); });
if (objId) { if (objId) {
const matrix = lookupMatrix(dict.getArray("Matrix"), null); const matrix = lookupMatrix(dict.getArray("Matrix"), null);
operatorList.addOp(fn, ["Shading", objId, matrix], range); operatorList.addOp(fn, ["Shading", objId, matrix]);
} }
return undefined; return undefined;
} }
@ -1798,7 +1771,6 @@ class PartialEvaluator {
} }
let args = operation.args; let args = operation.args;
let fn = operation.fn; let fn = operation.fn;
const range = operation.range;
switch (fn | 0) { switch (fn | 0) {
case OPS.paintXObject: case OPS.paintXObject:
@ -1809,7 +1781,7 @@ class PartialEvaluator {
if (isValidName) { if (isValidName) {
const localImage = localImageCache.getByName(name); const localImage = localImageCache.getByName(name);
if (localImage) { if (localImage) {
addLocallyCachedImageOps(operatorList, localImage, range); addLocallyCachedImageOps(operatorList, localImage);
args = null; args = null;
continue; continue;
} }
@ -1887,7 +1859,6 @@ class PartialEvaluator {
cacheKey: name, cacheKey: name,
localImageCache, localImageCache,
localColorSpaceCache, localColorSpaceCache,
range,
}) })
.then(resolveXObject, rejectXObject); .then(resolveXObject, rejectXObject);
return; return;
@ -1929,11 +1900,7 @@ class PartialEvaluator {
) )
.then(function (loadedName) { .then(function (loadedName) {
operatorList.addDependency(loadedName); operatorList.addDependency(loadedName);
operatorList.addOp( operatorList.addOp(OPS.setFont, [loadedName, fontSize]);
OPS.setFont,
[loadedName, fontSize],
range
);
}) })
); );
return; return;
@ -1962,7 +1929,6 @@ class PartialEvaluator {
cacheKey, cacheKey,
localImageCache, localImageCache,
localColorSpaceCache, localColorSpaceCache,
range,
}) })
); );
return; return;
@ -1995,7 +1961,7 @@ class PartialEvaluator {
self.ensureStateFont(stateManager.state); self.ensureStateFont(stateManager.state);
continue; continue;
} }
operatorList.addOp(OPS.nextLine, null, range); operatorList.addOp(OPS.nextLine);
args[0] = self.handleText(args[0], stateManager.state); args[0] = self.handleText(args[0], stateManager.state);
fn = OPS.showText; fn = OPS.showText;
break; break;
@ -2004,9 +1970,9 @@ class PartialEvaluator {
self.ensureStateFont(stateManager.state); self.ensureStateFont(stateManager.state);
continue; continue;
} }
operatorList.addOp(OPS.nextLine, null, range); operatorList.addOp(OPS.nextLine);
operatorList.addOp(OPS.setWordSpacing, [args.shift()], range); operatorList.addOp(OPS.setWordSpacing, [args.shift()]);
operatorList.addOp(OPS.setCharSpacing, [args.shift()], range); operatorList.addOp(OPS.setCharSpacing, [args.shift()]);
args[0] = self.handleText(args[0], stateManager.state); args[0] = self.handleText(args[0], stateManager.state);
fn = OPS.showText; fn = OPS.showText;
break; break;
@ -2126,8 +2092,7 @@ class PartialEvaluator {
task, task,
localColorSpaceCache, localColorSpaceCache,
localTilingPatternCache, localTilingPatternCache,
localShadingPatternCache, localShadingPatternCache
range
) )
); );
return; return;
@ -2159,8 +2124,7 @@ class PartialEvaluator {
task, task,
localColorSpaceCache, localColorSpaceCache,
localTilingPatternCache, localTilingPatternCache,
localShadingPatternCache, localShadingPatternCache
range
) )
); );
return; return;
@ -2211,7 +2175,7 @@ class PartialEvaluator {
const localGStateObj = localGStateCache.getByName(name); const localGStateObj = localGStateCache.getByName(name);
if (localGStateObj) { if (localGStateObj) {
if (localGStateObj.length > 0) { if (localGStateObj.length > 0) {
operatorList.addOp(OPS.setGState, [localGStateObj], range); operatorList.addOp(OPS.setGState, [localGStateObj]);
} }
args = null; args = null;
continue; continue;
@ -2247,7 +2211,6 @@ class PartialEvaluator {
stateManager, stateManager,
localGStateCache, localGStateCache,
localColorSpaceCache, localColorSpaceCache,
range,
}) })
.then(resolveGState, rejectGState); .then(resolveGState, rejectGState);
}).catch(function (reason) { }).catch(function (reason) {
@ -2269,7 +2232,7 @@ class PartialEvaluator {
case OPS.curveTo3: case OPS.curveTo3:
case OPS.closePath: case OPS.closePath:
case OPS.rectangle: case OPS.rectangle:
self.buildPath(operatorList, fn, args, parsingText, range); self.buildPath(operatorList, fn, args, parsingText);
continue; continue;
case OPS.markPoint: case OPS.markPoint:
case OPS.markPointProps: case OPS.markPointProps:
@ -2285,11 +2248,7 @@ class PartialEvaluator {
case OPS.beginMarkedContentProps: case OPS.beginMarkedContentProps:
if (!(args[0] instanceof Name)) { if (!(args[0] instanceof Name)) {
warn(`Expected name for beginMarkedContentProps arg0=${args[0]}`); warn(`Expected name for beginMarkedContentProps arg0=${args[0]}`);
operatorList.addOp( operatorList.addOp(OPS.beginMarkedContentProps, ["OC", null]);
OPS.beginMarkedContentProps,
["OC", null],
range
);
continue; continue;
} }
if (args[0].name === "OC") { if (args[0].name === "OC") {
@ -2297,11 +2256,10 @@ class PartialEvaluator {
self self
.parseMarkedContentProps(args[1], resources) .parseMarkedContentProps(args[1], resources)
.then(data => { .then(data => {
operatorList.addOp( operatorList.addOp(OPS.beginMarkedContentProps, [
OPS.beginMarkedContentProps, "OC",
["OC", data], data,
range ]);
);
}) })
.catch(reason => { .catch(reason => {
if (reason instanceof AbortException) { if (reason instanceof AbortException) {
@ -2311,11 +2269,10 @@ class PartialEvaluator {
warn( warn(
`getOperatorList - ignoring beginMarkedContentProps: "${reason}".` `getOperatorList - ignoring beginMarkedContentProps: "${reason}".`
); );
operatorList.addOp( operatorList.addOp(OPS.beginMarkedContentProps, [
OPS.beginMarkedContentProps, "OC",
["OC", null], null,
range ]);
);
return; return;
} }
throw reason; throw reason;
@ -2348,7 +2305,7 @@ class PartialEvaluator {
} }
} }
} }
operatorList.addOp(fn, args, range); operatorList.addOp(fn, args);
} }
if (stop) { if (stop) {
next(deferred); next(deferred);
@ -5144,7 +5101,6 @@ class EvaluatorPreprocessor {
// avoiding allocations where possible is worthwhile. // avoiding allocations where possible is worthwhile.
// //
read(operation) { read(operation) {
const start = this.parser.getPosition();
let args = operation.args; let args = operation.args;
while (true) { while (true) {
const obj = this.parser.getObj(); const obj = this.parser.getObj();
@ -5221,8 +5177,6 @@ class EvaluatorPreprocessor {
operation.fn = fn; operation.fn = fn;
operation.args = args; operation.args = args;
const end = this.parser.getEnd();
operation.range = [start, end];
return true; return true;
} }
if (obj === EOF) { if (obj === EOF) {

View File

@ -1,384 +0,0 @@
import { Dict, Name, Ref } from "./primitives.js";
import { BaseStream } from "./base_stream.js";
import { LocalColorSpaceCache } from "./image_utils.js";
import { PDFFunctionFactory } from "./function.js";
import { PDFImage } from "./image.js";
async function getPrim(path, doc) {
const [prim, trace] = await getPrimitive(path, doc);
return toModel(trace.at(-1).key, trace, prim);
}
async function getStreamAsString(path, doc) {
if (!path.endsWith("Data")) {
throw new Error(`Path ${path} does not end with Data!`);
}
const [prim] = await getPrimitive(path.replace("/Data", ""), doc);
if ((!prim) instanceof BaseStream) {
throw new Error(`Selected primitive with path ${path} is not a Stream!`);
}
const bytes = prim.getBytes();
let string = "";
for (let i = 0; i < bytes.length; i++) {
string += String.fromCharCode(bytes[i]);
}
return string;
}
async function getImageAsBlob(path, doc) {
if (!path.endsWith("Data")) {
throw new Error(`Path ${path} does not end with Data!`);
}
const [prim] = await getPrimitive(path.replace("/Data", ""), doc);
if ((!prim) instanceof BaseStream) {
throw new Error(`Selected primitive with path ${path} is not a Stream!`);
}
const info = prim.dict;
if (!info || info.getRaw("Subtype")?.name !== "Image") {
throw new Error(`Selected Stream is not an Image!"`);
}
const pdfFunctionFactory = new PDFFunctionFactory({
xref: doc.xref,
isEvalSupported: true,
});
const pdfImage = new PDFImage({
xref: doc.xref,
image: prim,
pdfFunctionFactory,
localColorSpaceCache: new LocalColorSpaceCache(),
});
return pdfImage.createImageData(true, false);
}
async function getPrimitive(path, doc) {
const xref = doc.xref;
let path_arr = parsePath(path);
let [prim, trace] = await getRoot(path_arr[0], doc);
while (path_arr.length > 1) {
path_arr = path_arr.slice(1);
[prim, trace] = resolveStep(xref, prim, trace, path_arr[0]);
}
return [prim, trace];
}
async function getPrimTree(request, doc) {
let results = [];
for (const item of request) {
results = results.concat(await _getPrimTree(item, doc));
}
return results;
}
async function _getPrimTree(request, doc) {
const results = [];
const [prim, trace] = await getRoot(request.key, doc);
const root = toModel(request.key, trace, prim);
results.push(toTreeModel(root, 0, true));
addChildren(root, request, results, prim, doc, trace, 1);
return results;
}
function addChildren(model, request, results, prim, doc, trace, depth) {
for (const child of model.children) {
const childRequest = request.children?.find(c => c.key === child.key);
if (childRequest) {
results.push(toTreeModel(child, depth, true));
expandPrim(results, prim, childRequest, doc, trace, depth + 1);
} else {
results.push(toTreeModel(child, depth, false));
}
}
}
function expandPrim(results, rootPrim, request, doc, trace, depth) {
if (depth > 20) {
throw new Error(`Depth limit exceeded: ${depth}`);
}
const [prim, _trace] = resolveStep(doc.xref, rootPrim, trace, request.key);
const model = toModel(request.key, _trace, prim);
addChildren(model, request, results, prim, doc, _trace, depth);
}
function toTreeModel(primModel, depth, expand) {
return new TreeViewModel(
depth,
primModel.key,
primModel.ptype,
primModel.sub_type,
primModel.value,
primModel.container,
expand,
primModel.trace
);
}
function isContainer(prim) {
return (
prim instanceof Dict || Array.isArray(prim) || isRef(prim) || isStream(prim)
);
}
async function getRoot(first, doc) {
let root;
const trace = [];
if (first === "Trailer") {
root = doc.xref.trailer;
trace.push({ key: first, last_jump: first });
} else if (first.startsWith("Page")) {
const page = await doc.getPage(+first.replace("Page", "") - 1);
const ref = page.ref;
root = doc.xref.fetch(ref);
trace.push({ key: first, last_jump: ref.num });
} else {
const ref = Ref.get(+first, 0);
root = doc.xref.fetch(ref);
trace.push({ key: first, last_jump: ref.num });
}
return [root, trace];
}
function parsePath(path) {
if (Array.isArray(path)) {
return path;
}
if (path.length === 0) {
return [];
}
return path.split("/").filter(x => x !== "");
}
function isRef(obj) {
return obj instanceof Ref;
}
function resolveStep(xref, root, trace, step) {
let prim;
const last_jump = trace.at(-1).last_jump;
if (root instanceof Dict) {
prim = root.getRaw(step);
} else if (Array.isArray(root)) {
const _step = +step;
if (isNaN(_step) || _step >= root.length || _step < 0) {
throw new Error(
`Invalid step ${step} for Array of length: ${root.length}`
);
}
prim = root[_step];
} else if (root instanceof BaseStream && root.dict) {
prim = root.dict.getRaw(step);
} else {
throw new Error(
`Unexpected step ${step} at trace: /${trace.map(t => t.key).join("/")}`
);
}
const _trace = copy(trace);
if (isRef(prim)) {
const num = prim.num;
prim = xref.fetch(prim);
_trace.push({ key: step, last_jump: num });
} else {
_trace.push({ key: step, last_jump });
}
return [prim, _trace];
}
function toModel(name, trace, prim) {
const [type, subType] = toType(prim);
let value = primToString(prim);
const children = [];
if (prim instanceof Dict) {
value = format_dict_content(prim);
const keys = prim.getKeys();
const last = trace.at(-1);
keys.forEach(child => {
const _trace = copy(trace);
_trace.push({ key: child, last_jump: last.last_jump });
children.push(toModel(child, _trace, prim.getRaw(child)));
});
} else if (Array.isArray(prim)) {
value = format_arr_content(prim);
const last = trace.at(-1);
for (let i = 0; i < prim.length; i++) {
const _trace = copy(trace);
_trace.push({ key: i.toString(), last_jump: last.last_jump });
children.push(toModel(i.toString(), _trace, prim[i]));
}
} else if (isStream(prim)) {
const info_dict = prim.dict;
if (info_dict) {
value = format_dict_content(info_dict);
const keys = info_dict.getKeys();
const last = trace.at(-1);
keys.forEach(child => {
const _trace = copy(trace);
_trace.push({ key: child, last_jump: last.last_jump });
children.push(toModel(child, _trace, info_dict.getRaw(child)));
});
const _trace = copy(trace);
_trace.push({ key: "Data", last_jump: last.last_jump });
children.push(
new PrimitiveModel("Data", "-", "-", "Stream Data", false, [], _trace)
);
}
}
return new PrimitiveModel(
name,
type,
subType,
value,
isContainer(prim),
children,
trace
);
}
function toType(prim) {
if (prim instanceof Dict) {
const subType = prim.getRaw("Type");
return ["Dictionary", subType ? subType.name : "-"];
} else if (Array.isArray(prim)) {
return ["Array", "-"];
} else if (isStream(prim)) {
const subType = prim.dict?.getRaw("Subtype");
return ["Stream", subType ? subType.name : "-"];
} else if (prim instanceof Name) {
return ["Name", "-"];
} else if (isInt(prim)) {
return ["Integer", "-"];
} else if (isNum(prim)) {
return ["Number", "-"];
} else if (isBool(prim)) {
return ["Boolean", "-"];
} else if (isString(prim)) {
return ["String", "-"];
} else if (isRef(prim)) {
return ["Reference", "-"];
} else if (prim === null) {
return ["Null", "-"];
}
throw new Error("Unknown prim");
}
function copy(trace) {
const _trace = [];
for (let i = 0; i < trace.length; i++) {
_trace.push(trace[i]);
}
return _trace;
}
function isBool(v) {
return typeof v === "boolean";
}
function isInt(v) {
return typeof v === "number" && (v | 0) === v;
}
function isNum(v) {
return typeof v === "number";
}
function isString(v) {
return typeof v === "string";
}
function isStream(v) {
return v instanceof BaseStream;
}
function primToString(prim) {
if (prim instanceof Dict) {
return "Dictionary";
} else if (Array.isArray(prim)) {
return "Array";
} else if (isStream(prim)) {
return "Stream";
} else if (prim instanceof Name) {
return prim.name;
} else if (isInt(prim)) {
return prim.toString();
} else if (isNum(prim)) {
return prim.toString();
} else if (isBool(prim)) {
return prim.toString();
} else if (isString(prim)) {
return prim;
} else if (isRef(prim)) {
return "XRef(" + prim.num + ", " + prim.gen + ")";
} else if (prim === null) {
return "Null";
}
throw new Error("Unknown prim");
}
function format_dict_content(dict) {
let result = "{";
const keys = dict.getKeys();
result += keys
.slice(0, 4)
.map(key => key + ": " + primToString(dict.getRaw(key)))
.join(", ");
if (keys.length > 4) {
result += ",...";
}
result += "}";
return result;
}
function format_arr_content(arr) {
let result = "[";
result += arr
.slice(0, 4)
.map(p => primToString(p))
.join(", ");
if (arr.length > 4) {
result += ",...";
}
result += "]";
return result;
}
class PrimitiveModel {
constructor(
key,
ptype,
sub_type,
value,
container,
children = [],
trace = []
) {
this.key = key;
this.ptype = ptype;
this.sub_type = sub_type;
this.value = value;
this.children = children;
this.trace = trace;
this.container = container;
}
}
class TreeViewModel {
constructor(depth, key, ptype, sub_type, value, container, expanded, trace) {
this.depth = depth;
this.key = key;
this.ptype = ptype;
this.sub_type = sub_type;
this.value = value;
this.container = container;
this.expanded = expanded;
this.trace = trace;
}
}
export {
getImageAsBlob,
getPrim,
getPrimitive,
getPrimTree,
getStreamAsString,
PrimitiveModel,
toType,
TreeViewModel,
};

View File

@ -477,10 +477,9 @@ class NullOptimizer {
_optimize() {} _optimize() {}
push(fn, args, range) { push(fn, args) {
this.queue.fnArray.push(fn); this.queue.fnArray.push(fn);
this.queue.argsArray.push(args); this.queue.argsArray.push(args);
this.queue.rangeArray.push(range);
this._optimize(); this._optimize();
} }
@ -590,7 +589,6 @@ class OperatorList {
this._streamSink = streamSink; this._streamSink = streamSink;
this.fnArray = []; this.fnArray = [];
this.argsArray = []; this.argsArray = [];
this.rangeArray = [];
this.optimizer = this.optimizer =
streamSink && !(intent & RenderingIntentFlag.OPLIST) streamSink && !(intent & RenderingIntentFlag.OPLIST)
? new QueueOptimizer(this) ? new QueueOptimizer(this)
@ -622,8 +620,8 @@ class OperatorList {
return this._totalLength + this.length; return this._totalLength + this.length;
} }
addOp(fn, args, range = null) { addOp(fn, args) {
this.optimizer.push(fn, args, range); this.optimizer.push(fn, args);
this.weight++; this.weight++;
if (this._streamSink) { if (this._streamSink) {
if (this.weight >= OperatorList.CHUNK_SIZE) { if (this.weight >= OperatorList.CHUNK_SIZE) {
@ -638,7 +636,7 @@ class OperatorList {
} }
} }
addImageOps(fn, args, range, optionalContent, hasMask = false) { addImageOps(fn, args, optionalContent, hasMask = false) {
if (hasMask) { if (hasMask) {
this.addOp(OPS.save); this.addOp(OPS.save);
this.addOp(OPS.setGState, [[["SMask", false]]]); this.addOp(OPS.setGState, [[["SMask", false]]]);
@ -647,7 +645,7 @@ class OperatorList {
this.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]); this.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]);
} }
this.addOp(fn, args, range); this.addOp(fn, args);
if (optionalContent !== undefined) { if (optionalContent !== undefined) {
this.addOp(OPS.endMarkedContent, []); this.addOp(OPS.endMarkedContent, []);
@ -680,7 +678,7 @@ class OperatorList {
this.dependencies.add(dependency); this.dependencies.add(dependency);
} }
for (let i = 0, ii = opList.length; i < ii; i++) { for (let i = 0, ii = opList.length; i < ii; i++) {
this.addOp(opList.fnArray[i], opList.argsArray[i], opList.rangeArray[i]); this.addOp(opList.fnArray[i], opList.argsArray[i]);
} }
} }
@ -688,7 +686,6 @@ class OperatorList {
return { return {
fnArray: this.fnArray, fnArray: this.fnArray,
argsArray: this.argsArray, argsArray: this.argsArray,
rangeArray: this.rangeArray,
length: this.length, length: this.length,
}; };
} }
@ -720,7 +717,6 @@ class OperatorList {
{ {
fnArray: this.fnArray, fnArray: this.fnArray,
argsArray: this.argsArray, argsArray: this.argsArray,
rangeArray: this.rangeArray,
lastChunk, lastChunk,
separateAnnots, separateAnnots,
length, length,
@ -732,7 +728,6 @@ class OperatorList {
this.dependencies.clear(); this.dependencies.clear();
this.fnArray.length = 0; this.fnArray.length = 0;
this.argsArray.length = 0; this.argsArray.length = 0;
this.rangeArray.length = 0;
this.weight = 0; this.weight = 0;
this.optimizer.reset(); this.optimizer.reset();
} }

View File

@ -64,34 +64,24 @@ class Parser {
this.xref = xref; this.xref = xref;
this.allowStreams = allowStreams; this.allowStreams = allowStreams;
this.recoveryMode = recoveryMode; this.recoveryMode = recoveryMode;
this.imageCache = Object.create(null); this.imageCache = Object.create(null);
this._imageId = 0; this._imageId = 0;
this.refill(); this.refill();
} }
refill() { refill() {
const [buf1, start1, end1] = this.lexer.getObjWithRange(); this.buf1 = this.lexer.getObj();
const [buf2, start2, end2] = this.lexer.getObjWithRange(); this.buf2 = this.lexer.getObj();
this.buf1 = buf1;
this.range1 = [start1, end1];
this.buf2 = buf2;
this.range2 = [start2, end2];
} }
shift() { shift() {
if (this.buf2 instanceof Cmd && this.buf2.cmd === "ID") { if (this.buf2 instanceof Cmd && this.buf2.cmd === "ID") {
this.buf1 = this.buf2; this.buf1 = this.buf2;
this.buf2 = null; this.buf2 = null;
this.lastEnd = this.range1[1];
this.range1 = this.range2;
this.range2 = null;
} else { } else {
this.buf1 = this.buf2; this.buf1 = this.buf2;
this.lastEnd = this.range1[1]; this.buf2 = this.lexer.getObj();
this.range1 = this.range2;
const [buf2, start2, end2] = this.lexer.getObjWithRange();
this.buf2 = buf2;
this.range2 = [start2, end2];
} }
} }
@ -109,21 +99,6 @@ class Parser {
} }
} }
getPosition() {
return this.range1 ? this.range1[0] : 0;
}
getEnd() {
return this.lastEnd ?? 0;
}
getObjWithRange(cipherTransform = null) {
const start = this.range1[0];
const obj = this.getObj(cipherTransform);
const end = this.range1[0];
return [obj, start, end];
}
getObj(cipherTransform = null) { getObj(cipherTransform = null) {
const buf1 = this.buf1; const buf1 = this.buf1;
this.shift(); this.shift();
@ -1229,19 +1204,7 @@ class Lexer {
return strBuf.join(""); return strBuf.join("");
} }
getObjWithRange() { getObj() {
const ch = this._skipWhitespaceAndComments();
if (ch === EOF) {
return [ch, -1, -1];
}
// currentChar is always at pos - 1
const start = Math.max(this.stream.pos - 1, 0);
const obj = this.getObj();
const end = this.stream.pos - 1;
return [obj, start, end];
}
_skipWhitespaceAndComments() {
// Skip whitespace and comments. // Skip whitespace and comments.
let comment = false; let comment = false;
let ch = this.currentChar; let ch = this.currentChar;
@ -1260,14 +1223,7 @@ class Lexer {
} }
ch = this.nextChar(); ch = this.nextChar();
} }
return ch;
}
getObj() {
let ch = this._skipWhitespaceAndComments();
if (ch === EOF) {
return ch;
}
// Start reading a token. // Start reading a token.
switch (ch | 0) { switch (ch | 0) {
case 0x30: // '0' case 0x30: // '0'

View File

@ -1,37 +0,0 @@
import { Ref } from "./primitives.js";
import { toType } from "./obj_walker.js";
async function retrieveXref(doc) {
const result = new XRefTable(doc.xref.entries.length);
for (let i = 0; i < doc.xref.entries.length; i++) {
result.entries.push(to_model(i, doc.xref.entries[i], doc.xref));
}
return result;
}
function to_model(i, entry, xref) {
if (entry.free) {
return new XRefEntry("Free", i, entry.gen, entry.offset);
}
const fetched = xref.fetch(Ref.get(i, entry.gen));
const [type] = toType(fetched);
return new XRefEntry(type, i, entry.gen, entry.offset);
}
class XRefTable {
constructor(size) {
this.size = size;
this.entries = [];
}
}
class XRefEntry {
constructor(obj_type, obj_num, gen_num, offset) {
this.obj_type = obj_type;
this.obj_num = obj_num;
this.gen_num = gen_num;
this.offset = offset;
}
}
export { retrieveXref, XRefEntry, XRefTable };

View File

@ -31,19 +31,12 @@ import {
XRefParseException, XRefParseException,
} from "./core_utils.js"; } from "./core_utils.js";
import { Dict, isDict, Ref, RefSetCache } from "./primitives.js"; import { Dict, isDict, Ref, RefSetCache } from "./primitives.js";
import {
getImageAsBlob,
getPrim,
getPrimTree,
getStreamAsString
} from "./obj_walker.js";
import { LocalPdfManager, NetworkPdfManager } from "./pdf_manager.js"; import { LocalPdfManager, NetworkPdfManager } from "./pdf_manager.js";
import { MessageHandler, wrapReason } from "../shared/message_handler.js"; import { MessageHandler, wrapReason } from "../shared/message_handler.js";
import { AnnotationFactory } from "./annotation.js"; import { AnnotationFactory } from "./annotation.js";
import { clearGlobalCaches } from "./cleanup_helper.js"; import { clearGlobalCaches } from "./cleanup_helper.js";
import { incrementalUpdate } from "./writer.js"; import { incrementalUpdate } from "./writer.js";
import { PDFWorkerStream } from "./worker_stream.js"; import { PDFWorkerStream } from "./worker_stream.js";
import { retrieveXref } from "./retrieve_xref.js";
import { StructTreeRoot } from "./struct_tree.js"; import { StructTreeRoot } from "./struct_tree.js";
class WorkerTask { class WorkerTask {
@ -123,7 +116,7 @@ class WorkerMessageHandler {
if (apiVersion !== workerVersion) { if (apiVersion !== workerVersion) {
throw new Error( throw new Error(
`The API version "${apiVersion}" does not match ` + `The API version "${apiVersion}" does not match ` +
`the Worker version "${workerVersion}".` `the Worker version "${workerVersion}".`
); );
} }
@ -141,8 +134,8 @@ class WorkerMessageHandler {
if (enumerableProperties.length) { if (enumerableProperties.length) {
throw new Error( throw new Error(
"The `Array.prototype` contains unexpected enumerable properties: " + "The `Array.prototype` contains unexpected enumerable properties: " +
enumerableProperties.join(", ") + enumerableProperties.join(", ") +
"; thus breaking e.g. `for...in` iteration of `Array`s." "; thus breaking e.g. `for...in` iteration of `Array`s."
); );
} }
} }
@ -206,15 +199,15 @@ class WorkerMessageHandler {
} }
async function getPdfManager({ async function getPdfManager({
data, data,
password, password,
disableAutoFetch, disableAutoFetch,
rangeChunkSize, rangeChunkSize,
length, length,
docBaseUrl, docBaseUrl,
enableXfa, enableXfa,
evaluatorOptions, evaluatorOptions,
}) { }) {
const pdfManagerArgs = { const pdfManagerArgs = {
source: null, source: null,
disableAutoFetch, disableAutoFetch,
@ -242,7 +235,7 @@ class WorkerMessageHandler {
loaded = 0; loaded = 0;
fullRequest.headersReady fullRequest.headersReady
.then(function() { .then(function () {
if (!fullRequest.isRangeSupported) { if (!fullRequest.isRangeSupported) {
return; return;
} }
@ -263,13 +256,13 @@ class WorkerMessageHandler {
pdfManagerCapability.resolve(newPdfManager); pdfManagerCapability.resolve(newPdfManager);
cancelXHRs = null; cancelXHRs = null;
}) })
.catch(function(reason) { .catch(function (reason) {
pdfManagerCapability.reject(reason); pdfManagerCapability.reject(reason);
cancelXHRs = null; cancelXHRs = null;
}); });
new Promise(function(resolve, reject) { new Promise(function (resolve, reject) {
const readChunk = function({ value, done }) { const readChunk = function ({ value, done }) {
try { try {
ensureNotTerminated(); ensureNotTerminated();
if (done) { if (done) {
@ -314,7 +307,7 @@ class WorkerMessageHandler {
} }
}; };
fullRequest.read().then(readChunk, reject); fullRequest.read().then(readChunk, reject);
}).catch(function(e) { }).catch(function (e) {
pdfManagerCapability.reject(e); pdfManagerCapability.reject(e);
cancelXHRs = null; cancelXHRs = null;
}); });
@ -341,12 +334,12 @@ class WorkerMessageHandler {
handler handler
.sendWithPromise("PasswordRequest", ex) .sendWithPromise("PasswordRequest", ex)
.then(function({ password }) { .then(function ({ password }) {
finishWorkerTask(task); finishWorkerTask(task);
pdfManager.updatePassword(password); pdfManager.updatePassword(password);
pdfManagerReady(); pdfManagerReady();
}) })
.catch(function() { .catch(function () {
finishWorkerTask(task); finishWorkerTask(task);
handler.send("DocException", ex); handler.send("DocException", ex);
}); });
@ -359,7 +352,7 @@ class WorkerMessageHandler {
function pdfManagerReady() { function pdfManagerReady() {
ensureNotTerminated(); ensureNotTerminated();
loadDocument(false).then(onSuccess, function(reason) { loadDocument(false).then(onSuccess, function (reason) {
ensureNotTerminated(); ensureNotTerminated();
// Try again with recoveryMode == true // Try again with recoveryMode == true
@ -367,7 +360,7 @@ class WorkerMessageHandler {
onFailure(reason); onFailure(reason);
return; return;
} }
pdfManager.requestLoadedStream().then(function() { pdfManager.requestLoadedStream().then(function () {
ensureNotTerminated(); ensureNotTerminated();
loadDocument(true).then(onSuccess, onFailure); loadDocument(true).then(onSuccess, onFailure);
@ -378,7 +371,7 @@ class WorkerMessageHandler {
ensureNotTerminated(); ensureNotTerminated();
getPdfManager(data) getPdfManager(data)
.then(function(newPdfManager) { .then(function (newPdfManager) {
if (terminated) { if (terminated) {
// We were in a process of setting up the manager, but it got // We were in a process of setting up the manager, but it got
// terminated in the middle. // terminated in the middle.
@ -396,14 +389,14 @@ class WorkerMessageHandler {
.then(pdfManagerReady, onFailure); .then(pdfManagerReady, onFailure);
} }
handler.on("GetPage", function(data) { handler.on("GetPage", function (data) {
return pdfManager.getPage(data.pageIndex).then(function(page) { return pdfManager.getPage(data.pageIndex).then(function (page) {
return Promise.all([ return Promise.all([
pdfManager.ensure(page, "rotate"), pdfManager.ensure(page, "rotate"),
pdfManager.ensure(page, "ref"), pdfManager.ensure(page, "ref"),
pdfManager.ensure(page, "userUnit"), pdfManager.ensure(page, "userUnit"),
pdfManager.ensure(page, "view"), pdfManager.ensure(page, "view"),
]).then(function([rotate, ref, userUnit, view]) { ]).then(function ([rotate, ref, userUnit, view]) {
return { return {
rotate, rotate,
ref, ref,
@ -415,104 +408,84 @@ class WorkerMessageHandler {
}); });
}); });
handler.on("GetPageIndex", function(data) { handler.on("GetPageIndex", function (data) {
const pageRef = Ref.get(data.num, data.gen); const pageRef = Ref.get(data.num, data.gen);
return pdfManager.ensureCatalog("getPageIndex", [pageRef]); return pdfManager.ensureCatalog("getPageIndex", [pageRef]);
}); });
handler.on("GetDestinations", function(data) { handler.on("GetDestinations", function (data) {
return pdfManager.ensureCatalog("destinations"); return pdfManager.ensureCatalog("destinations");
}); });
handler.on("GetDestination", function(data) { handler.on("GetDestination", function (data) {
return pdfManager.ensureCatalog("getDestination", [data.id]); return pdfManager.ensureCatalog("getDestination", [data.id]);
}); });
handler.on("GetPageLabels", function(data) { handler.on("GetPageLabels", function (data) {
return pdfManager.ensureCatalog("pageLabels"); return pdfManager.ensureCatalog("pageLabels");
}); });
handler.on("GetPageLayout", function(data) { handler.on("GetPageLayout", function (data) {
return pdfManager.ensureCatalog("pageLayout"); return pdfManager.ensureCatalog("pageLayout");
}); });
handler.on("GetPageMode", function(data) { handler.on("GetPageMode", function (data) {
return pdfManager.ensureCatalog("pageMode"); return pdfManager.ensureCatalog("pageMode");
}); });
handler.on("GetViewerPreferences", function(data) { handler.on("GetViewerPreferences", function (data) {
return pdfManager.ensureCatalog("viewerPreferences"); return pdfManager.ensureCatalog("viewerPreferences");
}); });
handler.on("GetOpenAction", function(data) { handler.on("GetOpenAction", function (data) {
return pdfManager.ensureCatalog("openAction"); return pdfManager.ensureCatalog("openAction");
}); });
handler.on("GetAttachments", function(data) { handler.on("GetAttachments", function (data) {
return pdfManager.ensureCatalog("attachments"); return pdfManager.ensureCatalog("attachments");
}); });
handler.on("GetDocJSActions", function(data) { handler.on("GetDocJSActions", function (data) {
return pdfManager.ensureCatalog("jsActions"); return pdfManager.ensureCatalog("jsActions");
}); });
handler.on("GetPageJSActions", function({ pageIndex }) { handler.on("GetPageJSActions", function ({ pageIndex }) {
return pdfManager.getPage(pageIndex).then(function(page) { return pdfManager.getPage(pageIndex).then(function (page) {
return pdfManager.ensure(page, "jsActions"); return pdfManager.ensure(page, "jsActions");
}); });
}); });
handler.on("GetOutline", function(data) { handler.on("GetOutline", function (data) {
return pdfManager.ensureCatalog("documentOutline"); return pdfManager.ensureCatalog("documentOutline");
}); });
handler.on("GetOptionalContentConfig", function(data) { handler.on("GetOptionalContentConfig", function (data) {
return pdfManager.ensureCatalog("optionalContentConfig"); return pdfManager.ensureCatalog("optionalContentConfig");
}); });
handler.on("GetPermissions", function(data) { handler.on("GetPermissions", function (data) {
return pdfManager.ensureCatalog("permissions"); return pdfManager.ensureCatalog("permissions");
}); });
handler.on("GetMetadata", function(data) { handler.on("GetMetadata", function (data) {
return Promise.all([ return Promise.all([
pdfManager.ensureDoc("documentInfo"), pdfManager.ensureDoc("documentInfo"),
pdfManager.ensureCatalog("metadata"), pdfManager.ensureCatalog("metadata"),
]); ]);
}); });
handler.on("GetMarkInfo", function(data) { handler.on("GetMarkInfo", function (data) {
return pdfManager.ensureCatalog("markInfo"); return pdfManager.ensureCatalog("markInfo");
}); });
handler.on("GetData", function(data) { handler.on("GetData", function (data) {
return pdfManager.requestLoadedStream().then(function(stream) { return pdfManager.requestLoadedStream().then(function (stream) {
return stream.bytes; return stream.bytes;
}); });
}); });
handler.on("GetPrimitiveByPath", function(path_str) { handler.on("GetAnnotations", function ({ pageIndex, intent }) {
return getPrim(path_str, pdfManager.pdfDocument); return pdfManager.getPage(pageIndex).then(function (page) {
});
handler.on("GetXRefEntries", function(data) {
return retrieveXref(pdfManager.pdfDocument);
});
handler.on("GetPrimTree", function(request) {
return getPrimTree(request, pdfManager.pdfDocument);
});
handler.on("GetImageData", function(path) {
return getImageAsBlob(path, pdfManager.pdfDocument);
});
handler.on("GetStreamAsString", function(path) {
return getStreamAsString(path, pdfManager.pdfDocument);
});
handler.on("GetAnnotations", function({ pageIndex, intent }) {
return pdfManager.getPage(pageIndex).then(function(page) {
const task = new WorkerTask(`GetAnnotations: page ${pageIndex}`); const task = new WorkerTask(`GetAnnotations: page ${pageIndex}`);
startWorkerTask(task); startWorkerTask(task);
@ -529,23 +502,23 @@ class WorkerMessageHandler {
}); });
}); });
handler.on("GetFieldObjects", function(data) { handler.on("GetFieldObjects", function (data) {
return pdfManager return pdfManager
.ensureDoc("fieldObjects") .ensureDoc("fieldObjects")
.then(fieldObjects => fieldObjects?.allFields || null); .then(fieldObjects => fieldObjects?.allFields || null);
}); });
handler.on("HasJSActions", function(data) { handler.on("HasJSActions", function (data) {
return pdfManager.ensureDoc("hasJSActions"); return pdfManager.ensureDoc("hasJSActions");
}); });
handler.on("GetCalculationOrderIds", function(data) { handler.on("GetCalculationOrderIds", function (data) {
return pdfManager.ensureDoc("calculationOrderIds"); return pdfManager.ensureDoc("calculationOrderIds");
}); });
handler.on( handler.on(
"SaveDocument", "SaveDocument",
async function({ isPureXfa, numPages, annotationStorage, filename }) { async function ({ isPureXfa, numPages, annotationStorage, filename }) {
const globalPromises = [ const globalPromises = [
pdfManager.requestLoadedStream(), pdfManager.requestLoadedStream(),
pdfManager.ensureCatalog("acroForm"), pdfManager.ensureCatalog("acroForm"),
@ -615,7 +588,7 @@ class WorkerMessageHandler {
imagePromises, imagePromises,
changes changes
) )
.finally(function() { .finally(function () {
finishWorkerTask(task); finishWorkerTask(task);
}); });
}) })
@ -652,13 +625,13 @@ class WorkerMessageHandler {
} else { } else {
for (let pageIndex = 0; pageIndex < numPages; pageIndex++) { for (let pageIndex = 0; pageIndex < numPages; pageIndex++) {
promises.push( promises.push(
pdfManager.getPage(pageIndex).then(function(page) { pdfManager.getPage(pageIndex).then(function (page) {
const task = new WorkerTask(`Save: page ${pageIndex}`); const task = new WorkerTask(`Save: page ${pageIndex}`);
startWorkerTask(task); startWorkerTask(task);
return page return page
.save(handler, task, annotationStorage, changes) .save(handler, task, annotationStorage, changes)
.finally(function() { .finally(function () {
finishWorkerTask(task); finishWorkerTask(task);
}); });
}) })
@ -748,31 +721,6 @@ class WorkerMessageHandler {
} }
); );
handler.on("StreamContents", function(data, sink) {
const pageIndex = data.pageIndex;
pdfManager.getPage(pageIndex).then(function(page) {
page.getContentStream().then(stream => {
let byte;
let string = "";
while ((byte = stream.getByte()) !== -1) {
string += String.fromCharCode(byte);
}
sink.enqueue(string, string.length);
sink.close();
});
});
});
handler.on("UpdateContents", function (data) {
return new Promise(resolve => {
const pageIndex = data.pageIndex;
pdfManager.getPage(pageIndex).then(function (page) {
page.setContents(data.value);
resolve();
});
});
});
handler.on("GetOperatorList", function (data, sink) { handler.on("GetOperatorList", function (data, sink) {
const pageIndex = data.pageIndex; const pageIndex = data.pageIndex;
pdfManager.getPage(pageIndex).then(function (page) { pdfManager.getPage(pageIndex).then(function (page) {
@ -792,7 +740,6 @@ class WorkerMessageHandler {
cacheKey: data.cacheKey, cacheKey: data.cacheKey,
annotationStorage: data.annotationStorage, annotationStorage: data.annotationStorage,
modifiedIds: data.modifiedIds, modifiedIds: data.modifiedIds,
contentOverride: data.contentOverride,
}) })
.then( .then(
function (operatorListInfo) { function (operatorListInfo) {

View File

@ -17,25 +17,10 @@
* @module pdfjsLib * @module pdfjsLib
*/ */
import { DOMCMapReaderFactory } from "display-cmap_reader_factory";
import { PDFFetchStream } from "display-fetch_stream";
import { PDFNetworkStream } from "display-network";
import { PDFNodeStream } from "display-node_stream";
import {
NodeCanvasFactory,
NodeCMapReaderFactory,
NodeFilterFactory,
NodeStandardFontDataFactory,
NodeWasmFactory,
} from "display-node_utils";
import { DOMStandardFontDataFactory } from "display-standard_fontdata_factory";
import { DOMWasmFactory } from "display-wasm_factory";
import { MessageHandler, wrapReason } from "../shared/message_handler.js";
import { import {
AbortException, AbortException,
AnnotationMode, AnnotationMode,
assert, assert,
djb2Hash,
FeatureTest, FeatureTest,
getVerbosityLevel, getVerbosityLevel,
info, info,
@ -52,8 +37,7 @@ import {
PrintAnnotationStorage, PrintAnnotationStorage,
SerializableEmpty, SerializableEmpty,
} from "./annotation_storage.js"; } from "./annotation_storage.js";
import { CanvasGraphics } from "./canvas.js"; import { FontFaceObject, FontLoader } from "./font_loader.js";
import { DOMCanvasFactory } from "./canvas_factory.js";
import { import {
isDataScheme, isDataScheme,
isValidFetchUrl, isValidFetchUrl,
@ -61,13 +45,28 @@ import {
RenderingCancelledException, RenderingCancelledException,
StatTimer, StatTimer,
} from "./display_utils.js"; } from "./display_utils.js";
import { MessageHandler, wrapReason } from "../shared/message_handler.js";
import {
NodeCanvasFactory,
NodeCMapReaderFactory,
NodeFilterFactory,
NodeStandardFontDataFactory,
NodeWasmFactory,
} from "display-node_utils";
import { CanvasGraphics } from "./canvas.js";
import { DOMCanvasFactory } from "./canvas_factory.js";
import { DOMCMapReaderFactory } from "display-cmap_reader_factory";
import { DOMFilterFactory } from "./filter_factory.js"; import { DOMFilterFactory } from "./filter_factory.js";
import { FontFaceObject, FontLoader } from "./font_loader.js"; import { DOMStandardFontDataFactory } from "display-standard_fontdata_factory";
import { DOMWasmFactory } from "display-wasm_factory";
import { GlobalWorkerOptions } from "./worker_options.js";
import { Metadata } from "./metadata.js"; import { Metadata } from "./metadata.js";
import { OptionalContentConfig } from "./optional_content_config.js"; import { OptionalContentConfig } from "./optional_content_config.js";
import { TextLayer } from "./text_layer.js";
import { PDFDataTransportStream } from "./transport_stream.js"; import { PDFDataTransportStream } from "./transport_stream.js";
import { GlobalWorkerOptions } from "./worker_options.js"; import { PDFFetchStream } from "display-fetch_stream";
import { PDFNetworkStream } from "display-network";
import { PDFNodeStream } from "display-node_stream";
import { TextLayer } from "./text_layer.js";
import { XfaText } from "./xfa_text.js"; import { XfaText } from "./xfa_text.js";
const DEFAULT_RANGE_CHUNK_SIZE = 65536; // 2^16 = 65536 const DEFAULT_RANGE_CHUNK_SIZE = 65536; // 2^16 = 65536
@ -1101,46 +1100,6 @@ class PDFDocumentProxy {
return this._transport.downloadInfoCapability.promise; return this._transport.downloadInfoCapability.promise;
} }
/**
* @returns {Promise<PrimitiveModel>} A promise that is resolved to a view of
* a primitive inside the document.
*/
getPrimitiveByPath(path) {
return this._transport.getPrimitiveByPath(path);
}
/**
* @returns {Promise<TreeViewModel[]>} A promise that is resolved to a tree
* view of a primitive inside the document.
*/
getPrimitiveTree(request) {
return this._transport.getPrimitiveTree(request);
}
/**
* @returns {Promise<XRefTable>} A promise that is resolved to a view of
* the Cross-Reference Table.
*/
getXRefEntries() {
return this._transport.getXrefEntries();
}
/**
* @returns {Promise<string>} A promise that is resolved to a string
* representing the streams decoded contents.
*/
getStreamAsString(path) {
return this._transport.getStreamAsString(path);
}
/**
* @returns {Promise<ImageData>} A promise that is resolved to ImageData.
* Throws an Error if the path does not lead to an image!
*/
getImageDataByPath(path) {
return this._transport.getImageDataByPath(path);
}
/** /**
* Cleans up resources allocated by the document on both the main and worker * Cleans up resources allocated by the document on both the main and worker
* threads. * threads.
@ -1661,37 +1620,6 @@ class PDFPageProxy {
return renderTask; return renderTask;
} }
updateContents(newContents) {
if (!newContents) {
throw new Error("Contents may not be null or undefined");
}
this._intentStates.clear();
return this._transport.updateContents(newContents, this._pageIndex);
}
getContents() {
const readableStream = this._transport.streamContents(this._pageIndex);
return new Promise(function (resolve, reject) {
function pump() {
reader.read().then(function ({ value, done }) {
if (done) {
resolve(textContent.text);
return;
}
textContent.text += value;
pump();
}, reject);
}
const reader = readableStream.getReader();
const textContent = {
text: "",
};
pump();
});
}
/** /**
* @param {GetOperatorListParameters} params - Page getOperatorList * @param {GetOperatorListParameters} params - Page getOperatorList
* parameters. * parameters.
@ -1703,7 +1631,6 @@ class PDFPageProxy {
annotationMode = AnnotationMode.ENABLE, annotationMode = AnnotationMode.ENABLE,
printAnnotationStorage = null, printAnnotationStorage = null,
isEditing = false, isEditing = false,
contentOverride = null,
} = {}) { } = {}) {
if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("GENERIC")) { if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("GENERIC")) {
throw new Error("Not implemented: getOperatorList"); throw new Error("Not implemented: getOperatorList");
@ -1721,8 +1648,7 @@ class PDFPageProxy {
annotationMode, annotationMode,
printAnnotationStorage, printAnnotationStorage,
isEditing, isEditing,
/* isOpList = */ true, /* isOpList = */ true
contentOverride
); );
let intentState = this._intentStates.get(intentArgs.cacheKey); let intentState = this._intentStates.get(intentArgs.cacheKey);
if (!intentState) { if (!intentState) {
@ -1739,7 +1665,6 @@ class PDFPageProxy {
intentState.operatorList = { intentState.operatorList = {
fnArray: [], fnArray: [],
argsArray: [], argsArray: [],
rangeArray: [],
lastChunk: false, lastChunk: false,
separateAnnots: null, separateAnnots: null,
}; };
@ -1917,11 +1842,6 @@ class PDFPageProxy {
for (let i = 0, ii = operatorListChunk.length; i < ii; i++) { for (let i = 0, ii = operatorListChunk.length; i < ii; i++) {
intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]); intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]);
intentState.operatorList.argsArray.push(operatorListChunk.argsArray[i]); intentState.operatorList.argsArray.push(operatorListChunk.argsArray[i]);
if (intentState.operatorList.rangeArray) {
intentState.operatorList.rangeArray.push(
operatorListChunk.rangeArray[i]
);
}
} }
intentState.operatorList.lastChunk = operatorListChunk.lastChunk; intentState.operatorList.lastChunk = operatorListChunk.lastChunk;
intentState.operatorList.separateAnnots = operatorListChunk.separateAnnots; intentState.operatorList.separateAnnots = operatorListChunk.separateAnnots;
@ -1944,7 +1864,6 @@ class PDFPageProxy {
cacheKey, cacheKey,
annotationStorageSerializable, annotationStorageSerializable,
modifiedIds, modifiedIds,
contentOverride,
}) { }) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert( assert(
@ -1962,7 +1881,6 @@ class PDFPageProxy {
cacheKey, cacheKey,
annotationStorage: map, annotationStorage: map,
modifiedIds, modifiedIds,
contentOverride,
}, },
transfer transfer
); );
@ -2564,8 +2482,7 @@ class WorkerTransport {
annotationMode = AnnotationMode.ENABLE, annotationMode = AnnotationMode.ENABLE,
printAnnotationStorage = null, printAnnotationStorage = null,
isEditing = false, isEditing = false,
isOpList = false, isOpList = false
contentOverride = null
) { ) {
let renderingIntent = RenderingIntentFlag.DISPLAY; // Default value. let renderingIntent = RenderingIntentFlag.DISPLAY; // Default value.
let annotationStorageSerializable = SerializableEmpty; let annotationStorageSerializable = SerializableEmpty;
@ -2623,16 +2540,11 @@ class WorkerTransport {
modifiedIdsHash, modifiedIdsHash,
]; ];
if (contentOverride) {
cacheKeyBuf.push(djb2Hash(contentOverride));
}
return { return {
renderingIntent, renderingIntent,
cacheKey: cacheKeyBuf.join("_"), cacheKey: cacheKeyBuf.join("_"),
annotationStorageSerializable, annotationStorageSerializable,
modifiedIds, modifiedIds,
contentOverride,
}; };
} }
@ -2996,43 +2908,6 @@ class WorkerTransport {
return this.messageHandler.sendWithPromise("GetData", null); return this.messageHandler.sendWithPromise("GetData", null);
} }
getPrimitiveByPath(path) {
return this.messageHandler.sendWithPromise("GetPrimitiveByPath", path);
}
getPrimitiveTree(request) {
return this.messageHandler.sendWithPromise("GetPrimTree", request);
}
getImageDataByPath(path) {
return this.messageHandler.sendWithPromise("GetImageData", path);
}
getStreamAsString(path) {
return this.messageHandler.sendWithPromise("GetStreamAsString", path);
}
getXrefEntries() {
return this.messageHandler.sendWithPromise("GetXRefEntries", null);
}
streamContents(pageIndex) {
return this.messageHandler.sendWithStream("StreamContents", {
pageIndex,
});
}
/**
* @returns {Promise<void>} A promise that is resolved once the contents
* are updated.
*/
updateContents(newContents, pageIndex) {
return this.messageHandler.sendWithPromise("UpdateContents", {
value: newContents,
pageIndex,
});
}
saveDocument() { saveDocument() {
if (this.annotationStorage.size <= 0) { if (this.annotationStorage.size <= 0) {
warn( warn(
@ -3603,9 +3478,6 @@ class InternalRenderTask {
this.callback(); this.callback();
} }
if (this.stepper) {
this.stepper.finished = true;
}
} }
} }
} }

View File

@ -628,6 +628,12 @@ class DrawingEditor extends AnnotationEditor {
return this.div; return this.div;
} }
let baseX, baseY;
if (this._isCopy) {
baseX = this.x;
baseY = this.y;
}
const div = super.render(); const div = super.render();
div.classList.add("draw"); div.classList.add("draw");
@ -640,6 +646,10 @@ class DrawingEditor extends AnnotationEditor {
this._uiManager.addShouldRescale(this); this._uiManager.addShouldRescale(this);
this.disableEditing(); this.disableEditing();
if (this._isCopy) {
this._moveAfterPaste(baseX, baseY);
}
return div; return div;
} }

View File

@ -85,6 +85,8 @@ class AnnotationEditor {
#touchManager = null; #touchManager = null;
_isCopy = false;
_editToolbar = null; _editToolbar = null;
_initialOptions = Object.create(null); _initialOptions = Object.create(null);
@ -442,6 +444,17 @@ class AnnotationEditor {
this.fixAndSetPosition(); this.fixAndSetPosition();
} }
_moveAfterPaste(baseX, baseY) {
const [parentWidth, parentHeight] = this.parentDimensions;
this.setAt(
baseX * parentWidth,
baseY * parentHeight,
this.width * parentWidth,
this.height * parentHeight
);
this._onTranslated();
}
#translate([width, height], x, y) { #translate([width, height], x, y) {
[x, y] = this.screenToPageTranslation(x, y); [x, y] = this.screenToPageTranslation(x, y);
@ -1597,6 +1610,7 @@ class AnnotationEditor {
}); });
editor.rotation = data.rotation; editor.rotation = data.rotation;
editor.#accessibilityData = data.accessibilityData; editor.#accessibilityData = data.accessibilityData;
editor._isCopy = data.isCopy || false;
const [pageWidth, pageHeight] = editor.pageDimensions; const [pageWidth, pageHeight] = editor.pageDimensions;
const [x, y, width, height] = editor.getRectInCurrentCoords( const [x, y, width, height] = editor.getRectInCurrentCoords(

View File

@ -553,7 +553,7 @@ class FreeTextEditor extends AnnotationEditor {
} }
let baseX, baseY; let baseX, baseY;
if (this.width) { if (this._isCopy || this.annotationElementId) {
baseX = this.x; baseX = this.x;
baseY = this.y; baseY = this.y;
} }
@ -581,7 +581,7 @@ class FreeTextEditor extends AnnotationEditor {
bindEvents(this, this.div, ["dblclick", "keydown"]); bindEvents(this, this.div, ["dblclick", "keydown"]);
if (this.width) { if (this._isCopy || this.annotationElementId) {
// This editor was created in using copy (ctrl+c). // This editor was created in using copy (ctrl+c).
const [parentWidth, parentHeight] = this.parentDimensions; const [parentWidth, parentHeight] = this.parentDimensions;
if (this.annotationElementId) { if (this.annotationElementId) {
@ -627,12 +627,7 @@ class FreeTextEditor extends AnnotationEditor {
} }
this.setAt(posX * parentWidth, posY * parentHeight, tx, ty); this.setAt(posX * parentWidth, posY * parentHeight, tx, ty);
} else { } else {
this.setAt( this._moveAfterPaste(baseX, baseY);
baseX * parentWidth,
baseY * parentHeight,
this.width * parentWidth,
this.height * parentHeight
);
} }
this.#setContent(); this.#setContent();
@ -847,6 +842,7 @@ class FreeTextEditor extends AnnotationEditor {
if (isForCopying) { if (isForCopying) {
// Don't add the id when copying because the pasted editor mustn't be // Don't add the id when copying because the pasted editor mustn't be
// linked to an existing annotation. // linked to an existing annotation.
serialized.isCopy = true;
return serialized; return serialized;
} }

View File

@ -246,6 +246,7 @@ class InkEditor extends DrawingEditor {
}; };
if (isForCopying) { if (isForCopying) {
serialized.isCopy = true;
return serialized; return serialized;
} }

View File

@ -111,6 +111,22 @@ class SignatureEditor extends DrawingEditor {
return false; return false;
} }
/** @inheritdoc */
get telemetryFinalData() {
return {
type: "signature",
hasDescription: !!this.#description,
};
}
static computeTelemetryFinalData(data) {
const hasDescriptionStats = data.get("hasDescription");
return {
hasAltText: hasDescriptionStats.get(true) ?? 0,
hasNoAltText: hasDescriptionStats.get(false) ?? 0,
};
}
/** @inheritdoc */ /** @inheritdoc */
get isResizable() { get isResizable() {
return true; return true;
@ -130,6 +146,15 @@ class SignatureEditor extends DrawingEditor {
return this.div; return this.div;
} }
let baseX, baseY;
const { _isCopy } = this;
if (_isCopy) {
// No need to adjust the position when rendering in DrawingEditor.
this._isCopy = false;
baseX = this.x;
baseY = this.y;
}
super.render(); super.render();
this.div.setAttribute("role", "figure"); this.div.setAttribute("role", "figure");
@ -163,6 +188,11 @@ class SignatureEditor extends DrawingEditor {
} }
} }
if (_isCopy) {
this._isCopy = true;
this._moveAfterPaste(baseX, baseY);
}
return this.div; return this.div;
} }
@ -265,6 +295,14 @@ class SignatureEditor extends DrawingEditor {
this._uiManager.addToAnnotationStorage(this); this._uiManager.addToAnnotationStorage(this);
this.setUuid(uuid); this.setUuid(uuid);
this._reportTelemetry({
action: "pdfjs.signature.inserted",
data: {
hasBeenSaved: !!uuid,
hasDescription: !!description,
},
});
this.div.hidden = false; this.div.hidden = false;
} }
@ -348,6 +386,7 @@ class SignatureEditor extends DrawingEditor {
if (isForCopying) { if (isForCopying) {
serialized.paths = { lines, points }; serialized.paths = { lines, points };
serialized.uuid = this.#signatureUUID; serialized.uuid = this.#signatureUUID;
serialized.isCopy = true;
} else { } else {
serialized.lines = lines; serialized.lines = lines;
} }

View File

@ -346,7 +346,7 @@ class StampEditor extends AnnotationEditor {
} }
let baseX, baseY; let baseX, baseY;
if (this.width) { if (this._isCopy) {
baseX = this.x; baseX = this.x;
baseY = this.y; baseY = this.y;
} }
@ -365,15 +365,8 @@ class StampEditor extends AnnotationEditor {
} }
} }
if (this.width && !this.annotationElementId) { if (this._isCopy) {
// This editor was created in using copy (ctrl+c). this._moveAfterPaste(baseX, baseY);
const [parentWidth, parentHeight] = this.parentDimensions;
this.setAt(
baseX * parentWidth,
baseY * parentHeight,
this.width * parentWidth,
this.height * parentHeight
);
} }
this._uiManager.addShouldRescale(this); this._uiManager.addShouldRescale(this);
@ -854,6 +847,7 @@ class StampEditor extends AnnotationEditor {
// hence we serialize the bitmap to a data url. // hence we serialize the bitmap to a data url.
serialized.bitmapUrl = this.#serializeBitmap(/* toUrl = */ true); serialized.bitmapUrl = this.#serializeBitmap(/* toUrl = */ true);
serialized.accessibilityData = this.serializeAltText(true); serialized.accessibilityData = this.serializeAltText(true);
serialized.isCopy = true;
return serialized; return serialized;
} }

View File

@ -80,12 +80,16 @@ class FontLoader {
} }
} }
async loadSystemFont({ systemFontInfo: info, _inspectFont }) { async loadSystemFont({
systemFontInfo: info,
disableFontFace,
_inspectFont,
}) {
if (!info || this.#systemFonts.has(info.loadedName)) { if (!info || this.#systemFonts.has(info.loadedName)) {
return; return;
} }
assert( assert(
!this.disableFontFace, !disableFontFace,
"loadSystemFont shouldn't be called when `disableFontFace` is set." "loadSystemFont shouldn't be called when `disableFontFace` is set."
); );

View File

@ -578,16 +578,6 @@ function objectFromMap(map) {
return obj; return obj;
} }
// fast and easy hash
function djb2Hash(str) {
let hash = 5381;
for (let i = 0; i < str.length; i++) {
hash = (hash << 5) + hash + str.charCodeAt(i);
hash &= hash; // Convert to 32-bit integer
}
return hash >>> 0; // Convert to unsigned
}
// Checks the endianness of the platform. // Checks the endianness of the platform.
function isLittleEndian() { function isLittleEndian() {
const buffer8 = new Uint8Array(4); const buffer8 = new Uint8Array(4);
@ -1146,7 +1136,6 @@ export {
BASELINE_FACTOR, BASELINE_FACTOR,
bytesToString, bytesToString,
createValidAbsoluteUrl, createValidAbsoluteUrl,
djb2Hash,
DocumentActionEventType, DocumentActionEventType,
FeatureTest, FeatureTest,
FONT_IDENTITY_MATRIX, FONT_IDENTITY_MATRIX,

View File

@ -348,13 +348,7 @@ describe("Signature Editor", () => {
const editorSelector = getEditorSelector(0); const editorSelector = getEditorSelector(0);
await page.waitForSelector(editorSelector, { visible: true }); await page.waitForSelector(editorSelector, { visible: true });
await page.waitForSelector( const originalRect = await getRect(page, editorSelector);
`.canvasWrapper > svg use[href="#path_p1_0"]`,
{ visible: true }
);
const originalPath = await page.$eval("#path_p1_0", el =>
el.getAttribute("d")
);
const originalDescription = await page.$eval( const originalDescription = await page.$eval(
`${editorSelector} .altText.editDescription`, `${editorSelector} .altText.editDescription`,
el => el.title el => el.title
@ -365,21 +359,15 @@ describe("Signature Editor", () => {
const pastedEditorSelector = getEditorSelector(1); const pastedEditorSelector = getEditorSelector(1);
await page.waitForSelector(pastedEditorSelector, { visible: true }); await page.waitForSelector(pastedEditorSelector, { visible: true });
await page.waitForSelector( const pastedRect = await getRect(page, pastedEditorSelector);
`.canvasWrapper > svg use[href="#path_p1_1"]`,
{ visible: true }
);
const pastedPath = await page.$eval("#path_p1_1", el =>
el.getAttribute("d")
);
const pastedDescription = await page.$eval( const pastedDescription = await page.$eval(
`${pastedEditorSelector} .altText.editDescription`, `${pastedEditorSelector} .altText.editDescription`,
el => el.title el => el.title
); );
expect(pastedPath) expect(pastedRect)
.withContext(`In ${browserName}`) .withContext(`In ${browserName}`)
.toEqual(originalPath); .not.toEqual(originalRect);
expect(pastedDescription) expect(pastedDescription)
.withContext(`In ${browserName}`) .withContext(`In ${browserName}`)
.toEqual(originalDescription); .toEqual(originalDescription);

View File

@ -106,6 +106,7 @@
!issue9084.pdf !issue9084.pdf
!issue12963.pdf !issue12963.pdf
!issue9105_reduced.pdf !issue9105_reduced.pdf
!issue19484_2.pdf
!issue9105_other.pdf !issue9105_other.pdf
!issue9252.pdf !issue9252.pdf
!issue9262_reduced.pdf !issue9262_reduced.pdf
@ -481,6 +482,7 @@
!openoffice.pdf !openoffice.pdf
!js-buttons.pdf !js-buttons.pdf
!issue7014.pdf !issue7014.pdf
!issue19484_1.pdf
!issue8187.pdf !issue8187.pdf
!annotation-link-text-popup.pdf !annotation-link-text-popup.pdf
!issue9278.pdf !issue9278.pdf

BIN
test/pdfs/issue19484_1.pdf Normal file

Binary file not shown.

BIN
test/pdfs/issue19484_2.pdf Normal file

Binary file not shown.

View File

@ -3629,6 +3629,14 @@
"link": false, "link": false,
"type": "text" "type": "text"
}, },
{
"id": "issue19484_1",
"file": "pdfs/issue19484_1.pdf",
"md5": "4e9e78a84226dbdddbd735388ccc2dcd",
"rounds": 1,
"link": false,
"type": "eq"
},
{ {
"id": "issue5644", "id": "issue5644",
"file": "pdfs/issue5644.pdf", "file": "pdfs/issue5644.pdf",
@ -5043,6 +5051,14 @@
"rounds": 1, "rounds": 1,
"type": "eq" "type": "eq"
}, },
{
"id": "issue19484_2",
"file": "pdfs/issue19484_2.pdf",
"md5": "cd3050eda9fa18a7e6a78c702f9890bb",
"rounds": 1,
"link": false,
"type": "eq"
},
{ {
"id": "bug894572", "id": "bug894572",
"file": "pdfs/bug894572.pdf", "file": "pdfs/bug894572.pdf",

View File

@ -537,7 +537,11 @@ class SignatureStorage {
async isFull() { async isFull() {
// We want to store at most 5 signatures. // We want to store at most 5 signatures.
return (await this.getAll()).size === 5; return (await this.size()) === 5;
}
async size() {
return (await this.getAll()).size;
} }
async create(data) { async create(data) {

View File

@ -70,7 +70,11 @@ class SignatureStorage {
async isFull() { async isFull() {
// Only allow 5 signatures to be saved. // Only allow 5 signatures to be saved.
return (await this.getAll()).size === 5; return (await this.size()) === 5;
}
async size() {
return (await this.getAll()).size;
} }
async create(data) { async create(data) {

View File

@ -176,6 +176,12 @@ class SignatureManager {
clearButton.addEventListener( clearButton.addEventListener(
"click", "click",
() => { () => {
this.#reportTelemetry({
action: "pdfjs.signature.clear",
data: {
type: this.#currentTab,
},
});
this.#initTab(null); this.#initTab(null);
}, },
{ passive: true } { passive: true }
@ -253,7 +259,9 @@ class SignatureManager {
#resetCommon() { #resetCommon() {
this.#hasDescriptionChanged = false; this.#hasDescriptionChanged = false;
this.#description.value = ""; this.#description.value = "";
this.#tabsToAltText.set(this.#currentTab, ""); if (this.#currentTab) {
this.#tabsToAltText.get(this.#currentTab).value = "";
}
} }
#resetTab(name) { #resetTab(name) {
@ -283,7 +291,7 @@ class SignatureManager {
return; return;
} }
if (this.#currentTab) { if (this.#currentTab) {
this.#tabsToAltText.set(this.#currentTab, this.#description.value); this.#tabsToAltText.get(this.#currentTab).value = this.#description.value;
} }
if (name) { if (name) {
this.#currentTab = name; this.#currentTab = name;
@ -294,7 +302,7 @@ class SignatureManager {
if (reset) { if (reset) {
this.#resetCommon(); this.#resetCommon();
} else { } else {
this.#description.value = this.#tabsToAltText.get(this.#currentTab); this.#description.value = this.#tabsToAltText.get(this.#currentTab).value;
} }
this.#clearDescription.disabled = this.#description.value === ""; this.#clearDescription.disabled = this.#description.value === "";
this.#currentTabAC?.abort(); this.#currentTabAC?.abort();
@ -335,7 +343,8 @@ class SignatureManager {
() => { () => {
const { value } = this.#typeInput; const { value } = this.#typeInput;
if (!this.#hasDescriptionChanged) { if (!this.#hasDescriptionChanged) {
this.#description.value = value; this.#tabsToAltText.get("type").default = this.#description.value =
value;
this.#clearDescription.disabled = value === ""; this.#clearDescription.disabled = value === "";
} }
this.#disableButtons(value); this.#disableButtons(value);
@ -398,6 +407,7 @@ class SignatureManager {
this.#l10n this.#l10n
.get(SignatureManager.#l10nDescription.signature) .get(SignatureManager.#l10nDescription.signature)
.then(description => { .then(description => {
this.#tabsToAltText.get("draw").default = description;
this.#description.value ||= description; this.#description.value ||= description;
this.#clearDescription.disabled = this.#description.value === ""; this.#clearDescription.disabled = this.#description.value === "";
}); });
@ -609,6 +619,7 @@ class SignatureManager {
this.#imageSVG.setAttribute("preserveAspectRatio", "xMidYMid meet"); this.#imageSVG.setAttribute("preserveAspectRatio", "xMidYMid meet");
this.#imageSVG.append(path); this.#imageSVG.append(path);
path.setAttribute("d", outline.toSVGPath()); path.setAttribute("d", outline.toSVGPath());
this.#tabsToAltText.get("image").default = file.name;
if (this.#description.value === "") { if (this.#description.value === "") {
this.#description.value = file.name || ""; this.#description.value = file.name || "";
this.#clearDescription.disabled = this.#description.value === ""; this.#clearDescription.disabled = this.#description.value === "";
@ -633,6 +644,16 @@ class SignatureManager {
); );
} }
#reportTelemetry(data) {
this.#eventBus.dispatch("reporttelemetry", {
source: this,
details: {
type: "editing",
data,
},
});
}
#addToolbarButton(signatureData, uuid, description) { #addToolbarButton(signatureData, uuid, description) {
const { curves, areContours, thickness, width, height } = signatureData; const { curves, areContours, thickness, width, height } = signatureData;
const maxDim = Math.max(width, height); const maxDim = Math.max(width, height);
@ -716,6 +737,12 @@ class SignatureManager {
deleteButton.addEventListener("click", async () => { deleteButton.addEventListener("click", async () => {
if (await this.#signatureStorage.delete(uuid)) { if (await this.#signatureStorage.delete(uuid)) {
div.remove(); div.remove();
this.#reportTelemetry({
action: "pdfjs.signature.delete_saved",
data: {
savedCount: await this.#signatureStorage.size(),
},
});
} }
}); });
const deleteSpan = document.createElement("span"); const deleteSpan = document.createElement("span");
@ -805,7 +832,7 @@ class SignatureManager {
async open({ uiManager, editor }) { async open({ uiManager, editor }) {
this.#tabsToAltText ||= new Map( this.#tabsToAltText ||= new Map(
this.#tabButtons.keys().map(name => [name, ""]) this.#tabButtons.keys().map(name => [name, { value: "", default: "" }])
); );
this.#uiManager = uiManager; this.#uiManager = uiManager;
this.#currentEditor = editor; this.#currentEditor = editor;
@ -851,7 +878,8 @@ class SignatureManager {
async #add() { async #add() {
let data; let data;
switch (this.#currentTab) { const type = this.#currentTab;
switch (type) {
case "type": case "type":
data = this.#getOutlineForType(); data = this.#getOutlineForType();
break; break;
@ -863,8 +891,8 @@ class SignatureManager {
break; break;
} }
let uuid = null; let uuid = null;
const description = this.#description.value;
if (this.#saveCheckbox.checked) { if (this.#saveCheckbox.checked) {
const description = this.#description.value;
const { newCurves, areContours, thickness, width, height } = data; const { newCurves, areContours, thickness, width, height } = data;
const signatureData = await SignatureExtractor.compressSignature({ const signatureData = await SignatureExtractor.compressSignature({
outlines: newCurves, outlines: newCurves,
@ -893,6 +921,18 @@ class SignatureManager {
console.warn("SignatureManager.add: cannot save the signature."); console.warn("SignatureManager.add: cannot save the signature.");
} }
} }
const altText = this.#tabsToAltText.get(type);
this.#reportTelemetry({
action: "pdfjs.signature.created",
data: {
type,
saved: !!uuid,
savedCount: await this.#signatureStorage.size(),
descriptionChanged: description !== altText.default,
},
});
this.#currentEditor.addSignature( this.#currentEditor.addSignature(
data, data,
DEFAULT_HEIGHT_IN_PAGE, DEFAULT_HEIGHT_IN_PAGE,
@ -940,7 +980,7 @@ class EditDescriptionDialog {
e.preventDefault(); e.preventDefault();
} }
}); });
cancelButton.addEventListener("click", this.#finish.bind(this)); cancelButton.addEventListener("click", this.#cancel.bind(this));
updateButton.addEventListener("click", this.#update.bind(this)); updateButton.addEventListener("click", this.#update.bind(this));
const clearDescription = description.lastElementChild; const clearDescription = description.lastElementChild;
@ -983,12 +1023,24 @@ class EditDescriptionDialog {
} }
async #update() { async #update() {
const description = this.#description.value; // The description has been changed because the button isn't disabled.
if (this.#previousDescription === description) { this.#currentEditor._reportTelemetry({
this.#finish(); action: "pdfjs.signature.edit_description",
return; data: {
} hasBeenChanged: true,
this.#currentEditor.description = description; },
});
this.#currentEditor.description = this.#description.value;
this.#finish();
}
#cancel() {
this.#currentEditor._reportTelemetry({
action: "pdfjs.signature.edit_description",
data: {
hasBeenChanged: false,
},
});
this.#finish(); this.#finish();
} }

6660
yarn.lock

File diff suppressed because it is too large Load Diff