[Annotation] Fix printing/saving for annotations containing some non-ascii chars and with no fonts to handle them (bug 1666824)
- For text fields * when printing, we generate a fake font which contains some widths computed thanks to an OffscreenCanvas and its method measureText. In order to avoid to have to layout the glyphs ourselves, we just render all of them in one call in the showText method in using the system sans-serif/monospace fonts. * when saving, we continue to create the appearance streams if the fonts contain the char but when a char is missing, we just set, in the AcroForm dict, the flag /NeedAppearances to true and remove the appearance stream. This way, we let the different readers handle the rendering of the strings. - For FreeText annotations * when printing, we use the same trick as for text fields. * there is no need to save an appearance since Acrobat is able to infer one from the Content entry.
This commit is contained in:
parent
f7449563ef
commit
3ca03603c2
@ -22,7 +22,9 @@ import {
|
|||||||
AnnotationReplyType,
|
AnnotationReplyType,
|
||||||
AnnotationType,
|
AnnotationType,
|
||||||
assert,
|
assert,
|
||||||
|
BASELINE_FACTOR,
|
||||||
escapeString,
|
escapeString,
|
||||||
|
FeatureTest,
|
||||||
getModificationDate,
|
getModificationDate,
|
||||||
IDENTITY_MATRIX,
|
IDENTITY_MATRIX,
|
||||||
isAscii,
|
isAscii,
|
||||||
@ -33,7 +35,6 @@ import {
|
|||||||
shadow,
|
shadow,
|
||||||
stringToPDFString,
|
stringToPDFString,
|
||||||
stringToUTF16BEString,
|
stringToUTF16BEString,
|
||||||
stringToUTF8String,
|
|
||||||
unreachable,
|
unreachable,
|
||||||
Util,
|
Util,
|
||||||
warn,
|
warn,
|
||||||
@ -41,10 +42,13 @@ import {
|
|||||||
import {
|
import {
|
||||||
collectActions,
|
collectActions,
|
||||||
getInheritableProperty,
|
getInheritableProperty,
|
||||||
|
getRotationMatrix,
|
||||||
numberToString,
|
numberToString,
|
||||||
|
stringToUTF16String,
|
||||||
} from "./core_utils.js";
|
} from "./core_utils.js";
|
||||||
import {
|
import {
|
||||||
createDefaultAppearance,
|
createDefaultAppearance,
|
||||||
|
FakeUnicodeFont,
|
||||||
getPdfColor,
|
getPdfColor,
|
||||||
parseDefaultAppearance,
|
parseDefaultAppearance,
|
||||||
} from "./default_appearance.js";
|
} from "./default_appearance.js";
|
||||||
@ -143,6 +147,9 @@ class AnnotationFactory {
|
|||||||
needAppearances:
|
needAppearances:
|
||||||
!collectFields && acroFormDict.get("NeedAppearances") === true,
|
!collectFields && acroFormDict.get("NeedAppearances") === true,
|
||||||
pageIndex,
|
pageIndex,
|
||||||
|
isOffscreenCanvasSupported:
|
||||||
|
FeatureTest.isOffscreenCanvasSupported &&
|
||||||
|
pdfManager.evaluatorOptions.isOffscreenCanvasSupported,
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (subtype) {
|
switch (subtype) {
|
||||||
@ -268,7 +275,7 @@ class AnnotationFactory {
|
|||||||
baseFont.set("Subtype", Name.get("Type1"));
|
baseFont.set("Subtype", Name.get("Type1"));
|
||||||
baseFont.set("Encoding", Name.get("WinAnsiEncoding"));
|
baseFont.set("Encoding", Name.get("WinAnsiEncoding"));
|
||||||
const buffer = [];
|
const buffer = [];
|
||||||
baseFontRef = xref.getNewRef();
|
baseFontRef = xref.getNewTemporaryRef();
|
||||||
writeObject(baseFontRef, baseFont, buffer, null);
|
writeObject(baseFontRef, baseFont, buffer, null);
|
||||||
dependencies.push({ ref: baseFontRef, data: buffer.join("") });
|
dependencies.push({ ref: baseFontRef, data: buffer.join("") });
|
||||||
}
|
}
|
||||||
@ -301,6 +308,9 @@ class AnnotationFactory {
|
|||||||
|
|
||||||
const xref = evaluator.xref;
|
const xref = evaluator.xref;
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
const isOffscreenCanvasSupported =
|
||||||
|
FeatureTest.isOffscreenCanvasSupported &&
|
||||||
|
evaluator.options.isOffscreenCanvasSupported;
|
||||||
for (const annotation of annotations) {
|
for (const annotation of annotations) {
|
||||||
switch (annotation.annotationType) {
|
switch (annotation.annotationType) {
|
||||||
case AnnotationEditorType.FREETEXT:
|
case AnnotationEditorType.FREETEXT:
|
||||||
@ -308,12 +318,15 @@ class AnnotationFactory {
|
|||||||
FreeTextAnnotation.createNewPrintAnnotation(xref, annotation, {
|
FreeTextAnnotation.createNewPrintAnnotation(xref, annotation, {
|
||||||
evaluator,
|
evaluator,
|
||||||
task,
|
task,
|
||||||
|
isOffscreenCanvasSupported,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case AnnotationEditorType.INK:
|
case AnnotationEditorType.INK:
|
||||||
promises.push(
|
promises.push(
|
||||||
InkAnnotation.createNewPrintAnnotation(xref, annotation)
|
InkAnnotation.createNewPrintAnnotation(xref, annotation, {
|
||||||
|
isOffscreenCanvasSupported,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -614,6 +627,17 @@ class Annotation {
|
|||||||
return { str, dir };
|
return { str, dir };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setDefaultAppearance(params) {
|
||||||
|
const defaultAppearance =
|
||||||
|
getInheritableProperty({ dict: params.dict, key: "DA" }) ||
|
||||||
|
params.acroForm.get("DA");
|
||||||
|
this._defaultAppearance =
|
||||||
|
typeof defaultAppearance === "string" ? defaultAppearance : "";
|
||||||
|
this.data.defaultAppearanceData = parseDefaultAppearance(
|
||||||
|
this._defaultAppearance
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the title.
|
* Set the title.
|
||||||
*
|
*
|
||||||
@ -1449,20 +1473,25 @@ class MarkupAnnotation extends Annotation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async createNewAnnotation(xref, annotation, dependencies, params) {
|
static async createNewAnnotation(xref, annotation, dependencies, params) {
|
||||||
const annotationRef = xref.getNewRef();
|
const annotationRef = xref.getNewTemporaryRef();
|
||||||
const apRef = xref.getNewRef();
|
|
||||||
const annotationDict = this.createNewDict(annotation, xref, { apRef });
|
|
||||||
const ap = await this.createNewAppearanceStream(annotation, xref, params);
|
const ap = await this.createNewAppearanceStream(annotation, xref, params);
|
||||||
|
|
||||||
const buffer = [];
|
const buffer = [];
|
||||||
let transform = xref.encrypt
|
let annotationDict;
|
||||||
? xref.encrypt.createCipherTransform(apRef.num, apRef.gen)
|
|
||||||
: null;
|
if (ap) {
|
||||||
writeObject(apRef, ap, buffer, transform);
|
const apRef = xref.getNewTemporaryRef();
|
||||||
dependencies.push({ ref: apRef, data: buffer.join("") });
|
annotationDict = this.createNewDict(annotation, xref, { apRef });
|
||||||
|
const transform = xref.encrypt
|
||||||
|
? xref.encrypt.createCipherTransform(apRef.num, apRef.gen)
|
||||||
|
: null;
|
||||||
|
writeObject(apRef, ap, buffer, transform);
|
||||||
|
dependencies.push({ ref: apRef, data: buffer.join("") });
|
||||||
|
} else {
|
||||||
|
annotationDict = this.createNewDict(annotation, xref, {});
|
||||||
|
}
|
||||||
|
|
||||||
buffer.length = 0;
|
buffer.length = 0;
|
||||||
transform = xref.encrypt
|
const transform = xref.encrypt
|
||||||
? xref.encrypt.createCipherTransform(annotationRef.num, annotationRef.gen)
|
? xref.encrypt.createCipherTransform(annotationRef.num, annotationRef.gen)
|
||||||
: null;
|
: null;
|
||||||
writeObject(annotationRef, annotationDict, buffer, transform);
|
writeObject(annotationRef, annotationDict, buffer, transform);
|
||||||
@ -1477,6 +1506,7 @@ class MarkupAnnotation extends Annotation {
|
|||||||
return new this.prototype.constructor({
|
return new this.prototype.constructor({
|
||||||
dict: annotationDict,
|
dict: annotationDict,
|
||||||
xref,
|
xref,
|
||||||
|
isOffscreenCanvasSupported: params.isOffscreenCanvasSupported,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1489,6 +1519,7 @@ class WidgetAnnotation extends Annotation {
|
|||||||
const data = this.data;
|
const data = this.data;
|
||||||
this.ref = params.ref;
|
this.ref = params.ref;
|
||||||
this._needAppearances = params.needAppearances;
|
this._needAppearances = params.needAppearances;
|
||||||
|
this._isOffscreenCanvasSupported = params.isOffscreenCanvasSupported;
|
||||||
|
|
||||||
data.annotationType = AnnotationType.WIDGET;
|
data.annotationType = AnnotationType.WIDGET;
|
||||||
if (data.fieldName === undefined) {
|
if (data.fieldName === undefined) {
|
||||||
@ -1533,13 +1564,7 @@ class WidgetAnnotation extends Annotation {
|
|||||||
|
|
||||||
data.alternativeText = stringToPDFString(dict.get("TU") || "");
|
data.alternativeText = stringToPDFString(dict.get("TU") || "");
|
||||||
|
|
||||||
const defaultAppearance =
|
this.setDefaultAppearance(params);
|
||||||
getInheritableProperty({ dict, key: "DA" }) || params.acroForm.get("DA");
|
|
||||||
this._defaultAppearance =
|
|
||||||
typeof defaultAppearance === "string" ? defaultAppearance : "";
|
|
||||||
data.defaultAppearanceData = parseDefaultAppearance(
|
|
||||||
this._defaultAppearance
|
|
||||||
);
|
|
||||||
|
|
||||||
data.hasAppearance =
|
data.hasAppearance =
|
||||||
(this._needAppearances &&
|
(this._needAppearances &&
|
||||||
@ -1612,19 +1637,6 @@ class WidgetAnnotation extends Annotation {
|
|||||||
return !!(this.data.fieldFlags & flag);
|
return !!(this.data.fieldFlags & flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
static _getRotationMatrix(rotation, width, height) {
|
|
||||||
switch (rotation) {
|
|
||||||
case 90:
|
|
||||||
return [0, 1, -1, 0, width, 0];
|
|
||||||
case 180:
|
|
||||||
return [-1, 0, 0, -1, width, height];
|
|
||||||
case 270:
|
|
||||||
return [0, -1, 1, 0, 0, height];
|
|
||||||
default:
|
|
||||||
throw new Error("Invalid rotation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getRotationMatrix(annotationStorage) {
|
getRotationMatrix(annotationStorage) {
|
||||||
const storageEntry = annotationStorage
|
const storageEntry = annotationStorage
|
||||||
? annotationStorage.get(this.data.id)
|
? annotationStorage.get(this.data.id)
|
||||||
@ -1641,7 +1653,7 @@ class WidgetAnnotation extends Annotation {
|
|||||||
const width = this.data.rect[2] - this.data.rect[0];
|
const width = this.data.rect[2] - this.data.rect[0];
|
||||||
const height = this.data.rect[3] - this.data.rect[1];
|
const height = this.data.rect[3] - this.data.rect[1];
|
||||||
|
|
||||||
return WidgetAnnotation._getRotationMatrix(rotation, width, height);
|
return getRotationMatrix(rotation, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBorderAndBackgroundAppearances(annotationStorage) {
|
getBorderAndBackgroundAppearances(annotationStorage) {
|
||||||
@ -1712,6 +1724,7 @@ class WidgetAnnotation extends Annotation {
|
|||||||
const content = await this._getAppearance(
|
const content = await this._getAppearance(
|
||||||
evaluator,
|
evaluator,
|
||||||
task,
|
task,
|
||||||
|
intent,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
if (this.appearance && content === null) {
|
if (this.appearance && content === null) {
|
||||||
@ -1824,89 +1837,121 @@ class WidgetAnnotation extends Annotation {
|
|||||||
rotation = this.rotation;
|
rotation = this.rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
let appearance = await this._getAppearance(
|
let appearance = null;
|
||||||
evaluator,
|
if (!this._needAppearances) {
|
||||||
task,
|
appearance = await this._getAppearance(
|
||||||
annotationStorage
|
evaluator,
|
||||||
);
|
task,
|
||||||
if (appearance === null) {
|
RenderingIntentFlag.SAVE,
|
||||||
return null;
|
annotationStorage
|
||||||
|
);
|
||||||
|
if (appearance === null) {
|
||||||
|
// Appearance didn't change.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No need to create an appearance: the pdf has the flag /NeedAppearances
|
||||||
|
// which means that it's up to the reader to produce an appearance.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let needAppearances = false;
|
||||||
|
if (appearance && appearance.needAppearances) {
|
||||||
|
needAppearances = true;
|
||||||
|
appearance = null;
|
||||||
|
}
|
||||||
|
|
||||||
const { xref } = evaluator;
|
const { xref } = evaluator;
|
||||||
|
|
||||||
const dict = xref.fetchIfRef(this.ref);
|
const originalDict = xref.fetchIfRef(this.ref);
|
||||||
if (!(dict instanceof Dict)) {
|
if (!(originalDict instanceof Dict)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bbox = [
|
const dict = new Dict(xref);
|
||||||
0,
|
for (const key of originalDict.getKeys()) {
|
||||||
0,
|
if (key !== "AP") {
|
||||||
this.data.rect[2] - this.data.rect[0],
|
dict.set(key, originalDict.getRaw(key));
|
||||||
this.data.rect[3] - this.data.rect[1],
|
}
|
||||||
];
|
}
|
||||||
|
|
||||||
const xfa = {
|
const xfa = {
|
||||||
path: stringToPDFString(dict.get("T") || ""),
|
path: stringToPDFString(dict.get("T") || ""),
|
||||||
value,
|
value,
|
||||||
};
|
};
|
||||||
|
|
||||||
const newRef = xref.getNewRef();
|
|
||||||
const AP = new Dict(xref);
|
|
||||||
AP.set("N", newRef);
|
|
||||||
|
|
||||||
const encrypt = xref.encrypt;
|
|
||||||
let originalTransform = null;
|
|
||||||
let newTransform = null;
|
|
||||||
if (encrypt) {
|
|
||||||
originalTransform = encrypt.createCipherTransform(
|
|
||||||
this.ref.num,
|
|
||||||
this.ref.gen
|
|
||||||
);
|
|
||||||
newTransform = encrypt.createCipherTransform(newRef.num, newRef.gen);
|
|
||||||
appearance = newTransform.encryptString(appearance);
|
|
||||||
}
|
|
||||||
|
|
||||||
const encoder = val => (isAscii(val) ? val : stringToUTF16BEString(val));
|
const encoder = val => (isAscii(val) ? val : stringToUTF16BEString(val));
|
||||||
dict.set("V", Array.isArray(value) ? value.map(encoder) : encoder(value));
|
dict.set("V", Array.isArray(value) ? value.map(encoder) : encoder(value));
|
||||||
dict.set("AP", AP);
|
|
||||||
dict.set("M", `D:${getModificationDate()}`);
|
|
||||||
|
|
||||||
const maybeMK = this._getMKDict(rotation);
|
const maybeMK = this._getMKDict(rotation);
|
||||||
if (maybeMK) {
|
if (maybeMK) {
|
||||||
dict.set("MK", maybeMK);
|
dict.set("MK", maybeMK);
|
||||||
}
|
}
|
||||||
|
|
||||||
const appearanceDict = new Dict(xref);
|
const encrypt = xref.encrypt;
|
||||||
appearanceDict.set("Length", appearance.length);
|
const originalTransform = encrypt
|
||||||
appearanceDict.set("Subtype", Name.get("Form"));
|
? encrypt.createCipherTransform(this.ref.num, this.ref.gen)
|
||||||
appearanceDict.set("Resources", this._getSaveFieldResources(xref));
|
: null;
|
||||||
appearanceDict.set("BBox", bbox);
|
|
||||||
|
|
||||||
const rotationMatrix = this.getRotationMatrix(annotationStorage);
|
const buffer = [];
|
||||||
if (rotationMatrix !== IDENTITY_MATRIX) {
|
const changes = [
|
||||||
// The matrix isn't the identity one.
|
|
||||||
appearanceDict.set("Matrix", rotationMatrix);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bufferOriginal = [`${this.ref.num} ${this.ref.gen} obj\n`];
|
|
||||||
writeDict(dict, bufferOriginal, originalTransform);
|
|
||||||
bufferOriginal.push("\nendobj\n");
|
|
||||||
|
|
||||||
const bufferNew = [`${newRef.num} ${newRef.gen} obj\n`];
|
|
||||||
writeDict(appearanceDict, bufferNew, newTransform);
|
|
||||||
bufferNew.push(" stream\n", appearance, "\nendstream\nendobj\n");
|
|
||||||
|
|
||||||
return [
|
|
||||||
// data for the original object
|
// data for the original object
|
||||||
// V field changed + reference for new AP
|
// V field changed + reference for new AP
|
||||||
{ ref: this.ref, data: bufferOriginal.join(""), xfa },
|
{ ref: this.ref, data: "", xfa, needAppearances },
|
||||||
// data for the new AP
|
|
||||||
{ ref: newRef, data: bufferNew.join(""), xfa: null },
|
|
||||||
];
|
];
|
||||||
|
if (appearance !== null) {
|
||||||
|
const newRef = xref.getNewTemporaryRef();
|
||||||
|
const AP = new Dict(xref);
|
||||||
|
dict.set("AP", AP);
|
||||||
|
AP.set("N", newRef);
|
||||||
|
|
||||||
|
let newTransform = null;
|
||||||
|
if (encrypt) {
|
||||||
|
newTransform = encrypt.createCipherTransform(newRef.num, newRef.gen);
|
||||||
|
appearance = newTransform.encryptString(appearance);
|
||||||
|
}
|
||||||
|
|
||||||
|
const resources = this._getSaveFieldResources(xref);
|
||||||
|
const appearanceStream = new StringStream(appearance);
|
||||||
|
const appearanceDict = (appearanceStream.dict = new Dict(xref));
|
||||||
|
appearanceDict.set("Length", appearance.length);
|
||||||
|
appearanceDict.set("Subtype", Name.get("Form"));
|
||||||
|
appearanceDict.set("Resources", resources);
|
||||||
|
appearanceDict.set("BBox", [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
this.data.rect[2] - this.data.rect[0],
|
||||||
|
this.data.rect[3] - this.data.rect[1],
|
||||||
|
]);
|
||||||
|
|
||||||
|
const rotationMatrix = this.getRotationMatrix(annotationStorage);
|
||||||
|
if (rotationMatrix !== IDENTITY_MATRIX) {
|
||||||
|
// The matrix isn't the identity one.
|
||||||
|
appearanceDict.set("Matrix", rotationMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeObject(newRef, appearanceStream, buffer, newTransform);
|
||||||
|
|
||||||
|
changes.push(
|
||||||
|
// data for the new AP
|
||||||
|
{
|
||||||
|
ref: newRef,
|
||||||
|
data: buffer.join(""),
|
||||||
|
xfa: null,
|
||||||
|
needAppearances: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
buffer.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dict.set("M", `D:${getModificationDate()}`);
|
||||||
|
writeObject(this.ref, dict, buffer, originalTransform);
|
||||||
|
|
||||||
|
changes[0].data = buffer.join("");
|
||||||
|
|
||||||
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getAppearance(evaluator, task, annotationStorage) {
|
async _getAppearance(evaluator, task, intent, annotationStorage) {
|
||||||
const isPassword = this.hasFieldFlag(AnnotationFieldFlag.PASSWORD);
|
const isPassword = this.hasFieldFlag(AnnotationFieldFlag.PASSWORD);
|
||||||
if (isPassword) {
|
if (isPassword) {
|
||||||
return null;
|
return null;
|
||||||
@ -1961,12 +2006,30 @@ class WidgetAnnotation extends Annotation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let lineCount = -1;
|
let lineCount = -1;
|
||||||
|
let lines;
|
||||||
|
|
||||||
|
// We could have a text containing for example some sequences of chars and
|
||||||
|
// their diacritics (e.g. "é".normalize("NFKD") shows 1 char when it's 2).
|
||||||
|
// Positioning diacritics is really something we don't want to do here.
|
||||||
|
// So if a font has a glyph for a acute accent and one for "e" then we won't
|
||||||
|
// get any encoding issues but we'll render "e" and then "´".
|
||||||
|
// It's why we normalize the string. We use NFC to preserve the initial
|
||||||
|
// string, (e.g. "²".normalize("NFC") === "²"
|
||||||
|
// but "²".normalize("NFKC") === "2").
|
||||||
|
//
|
||||||
|
// TODO: it isn't a perfect solution, some chars like "ẹ́" will be
|
||||||
|
// decomposed into two chars ("ẹ" and "´"), so we should detect such
|
||||||
|
// situations and then use either FakeUnicodeFont or set the
|
||||||
|
// /NeedAppearances flag.
|
||||||
if (this.data.multiLine) {
|
if (this.data.multiLine) {
|
||||||
lineCount = value.split(/\r\n|\r|\n/).length;
|
lines = value.split(/\r\n?|\n/).map(line => line.normalize("NFC"));
|
||||||
|
lineCount = lines.length;
|
||||||
|
} else {
|
||||||
|
lines = [value.replace(/\r\n?|\n/, "").normalize("NFC")];
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultPadding = 2;
|
const defaultPadding = 1;
|
||||||
const hPadding = defaultPadding;
|
const defaultHPadding = 2;
|
||||||
let totalHeight = this.data.rect[3] - this.data.rect[1];
|
let totalHeight = this.data.rect[3] - this.data.rect[1];
|
||||||
let totalWidth = this.data.rect[2] - this.data.rect[0];
|
let totalWidth = this.data.rect[2] - this.data.rect[0];
|
||||||
|
|
||||||
@ -1985,23 +2048,107 @@ class WidgetAnnotation extends Annotation {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const font = await WidgetAnnotation._getFontData(
|
let font = await WidgetAnnotation._getFontData(
|
||||||
evaluator,
|
evaluator,
|
||||||
task,
|
task,
|
||||||
this.data.defaultAppearanceData,
|
this.data.defaultAppearanceData,
|
||||||
this._fieldResources.mergedResources
|
this._fieldResources.mergedResources
|
||||||
);
|
);
|
||||||
const [defaultAppearance, fontSize] = this._computeFontSize(
|
|
||||||
totalHeight - defaultPadding,
|
let defaultAppearance, fontSize, lineHeight;
|
||||||
totalWidth - 2 * hPadding,
|
const encodedLines = [];
|
||||||
value,
|
let encodingError = false;
|
||||||
font,
|
for (const line of lines) {
|
||||||
lineCount
|
const encodedString = font.encodeString(line);
|
||||||
);
|
if (encodedString.length > 1) {
|
||||||
|
encodingError = true;
|
||||||
|
}
|
||||||
|
encodedLines.push(encodedString.join(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encodingError && intent & RenderingIntentFlag.SAVE) {
|
||||||
|
// We don't have a way to render the field, so we just rely on the
|
||||||
|
// /NeedAppearances trick to let the different sofware correctly render
|
||||||
|
// this pdf.
|
||||||
|
return { needAppearances: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
// We check that the font is able to encode the string.
|
||||||
|
if (encodingError && this._isOffscreenCanvasSupported) {
|
||||||
|
// If it can't then we fallback on fake unicode font (mapped to sans-serif
|
||||||
|
// for the rendering).
|
||||||
|
// It means that a printed form can be rendered differently (it depends on
|
||||||
|
// the sans-serif font) but at least we've something to render.
|
||||||
|
// In an ideal world the associated font should correctly handle the
|
||||||
|
// possible chars but a user can add a smiley or whatever.
|
||||||
|
// We could try to embed a font but it means that we must have access
|
||||||
|
// to the raw font file.
|
||||||
|
const fontFamily = this.data.comb ? "monospace" : "sans-serif";
|
||||||
|
const fakeUnicodeFont = new FakeUnicodeFont(evaluator.xref, fontFamily);
|
||||||
|
const resources = fakeUnicodeFont.createFontResources(lines.join(""));
|
||||||
|
const newFont = resources.getRaw("Font");
|
||||||
|
|
||||||
|
if (this._fieldResources.mergedResources.has("Font")) {
|
||||||
|
const oldFont = this._fieldResources.mergedResources.get("Font");
|
||||||
|
for (const key of newFont.getKeys()) {
|
||||||
|
oldFont.set(key, newFont.getRaw(key));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._fieldResources.mergedResources.set("Font", newFont);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fontName = fakeUnicodeFont.fontName.name;
|
||||||
|
font = await WidgetAnnotation._getFontData(
|
||||||
|
evaluator,
|
||||||
|
task,
|
||||||
|
{ fontName, fontSize: 0 },
|
||||||
|
resources
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0, ii = encodedLines.length; i < ii; i++) {
|
||||||
|
encodedLines[i] = stringToUTF16String(lines[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedDefaultAppearance = Object.assign(
|
||||||
|
Object.create(null),
|
||||||
|
this.data.defaultAppearanceData
|
||||||
|
);
|
||||||
|
this.data.defaultAppearanceData.fontSize = 0;
|
||||||
|
this.data.defaultAppearanceData.fontName = fontName;
|
||||||
|
|
||||||
|
[defaultAppearance, fontSize, lineHeight] = this._computeFontSize(
|
||||||
|
totalHeight - 2 * defaultPadding,
|
||||||
|
totalWidth - 2 * defaultHPadding,
|
||||||
|
value,
|
||||||
|
font,
|
||||||
|
lineCount
|
||||||
|
);
|
||||||
|
|
||||||
|
this.data.defaultAppearanceData = savedDefaultAppearance;
|
||||||
|
} else {
|
||||||
|
if (!this._isOffscreenCanvasSupported) {
|
||||||
|
warn(
|
||||||
|
"_getAppearance: OffscreenCanvas is not supported, annotation may not render correctly."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[defaultAppearance, fontSize, lineHeight] = this._computeFontSize(
|
||||||
|
totalHeight - 2 * defaultPadding,
|
||||||
|
totalWidth - 2 * defaultHPadding,
|
||||||
|
value,
|
||||||
|
font,
|
||||||
|
lineCount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let descent = font.descent;
|
let descent = font.descent;
|
||||||
if (isNaN(descent)) {
|
if (isNaN(descent)) {
|
||||||
descent = 0;
|
descent = BASELINE_FACTOR * lineHeight;
|
||||||
|
} else {
|
||||||
|
descent = Math.max(
|
||||||
|
BASELINE_FACTOR * lineHeight,
|
||||||
|
Math.abs(descent) * fontSize
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take into account the space we have to compute the default vertical
|
// Take into account the space we have to compute the default vertical
|
||||||
@ -2010,59 +2157,64 @@ class WidgetAnnotation extends Annotation {
|
|||||||
Math.floor((totalHeight - fontSize) / 2),
|
Math.floor((totalHeight - fontSize) / 2),
|
||||||
defaultPadding
|
defaultPadding
|
||||||
);
|
);
|
||||||
const vPadding = defaultVPadding + Math.abs(descent) * fontSize;
|
|
||||||
const alignment = this.data.textAlignment;
|
const alignment = this.data.textAlignment;
|
||||||
|
|
||||||
if (this.data.multiLine) {
|
if (this.data.multiLine) {
|
||||||
return this._getMultilineAppearance(
|
return this._getMultilineAppearance(
|
||||||
defaultAppearance,
|
defaultAppearance,
|
||||||
value,
|
encodedLines,
|
||||||
font,
|
font,
|
||||||
fontSize,
|
fontSize,
|
||||||
totalWidth,
|
totalWidth,
|
||||||
totalHeight,
|
totalHeight,
|
||||||
alignment,
|
alignment,
|
||||||
hPadding,
|
defaultHPadding,
|
||||||
vPadding,
|
defaultVPadding,
|
||||||
|
descent,
|
||||||
|
lineHeight,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: need to handle chars which are not in the font.
|
|
||||||
const encodedString = font.encodeString(value).join("");
|
|
||||||
|
|
||||||
if (this.data.comb) {
|
if (this.data.comb) {
|
||||||
return this._getCombAppearance(
|
return this._getCombAppearance(
|
||||||
defaultAppearance,
|
defaultAppearance,
|
||||||
font,
|
font,
|
||||||
encodedString,
|
encodedLines[0],
|
||||||
|
fontSize,
|
||||||
totalWidth,
|
totalWidth,
|
||||||
hPadding,
|
totalHeight,
|
||||||
vPadding,
|
defaultHPadding,
|
||||||
|
defaultVPadding,
|
||||||
|
descent,
|
||||||
|
lineHeight,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bottomPadding = defaultVPadding + descent;
|
||||||
if (alignment === 0 || alignment > 2) {
|
if (alignment === 0 || alignment > 2) {
|
||||||
// Left alignment: nothing to do
|
// Left alignment: nothing to do
|
||||||
return (
|
return (
|
||||||
`/Tx BMC q ${colors}BT ` +
|
`/Tx BMC q ${colors}BT ` +
|
||||||
defaultAppearance +
|
defaultAppearance +
|
||||||
` 1 0 0 1 ${hPadding} ${vPadding} Tm (${escapeString(
|
` 1 0 0 1 ${numberToString(defaultHPadding)} ${numberToString(
|
||||||
encodedString
|
bottomPadding
|
||||||
)}) Tj` +
|
)} Tm (${escapeString(encodedLines[0])}) Tj` +
|
||||||
" ET Q EMC"
|
" ET Q EMC"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const prevInfo = { shift: 0 };
|
||||||
const renderedText = this._renderText(
|
const renderedText = this._renderText(
|
||||||
encodedString,
|
encodedLines[0],
|
||||||
font,
|
font,
|
||||||
fontSize,
|
fontSize,
|
||||||
totalWidth,
|
totalWidth,
|
||||||
alignment,
|
alignment,
|
||||||
hPadding,
|
prevInfo,
|
||||||
vPadding
|
defaultHPadding,
|
||||||
|
bottomPadding
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
`/Tx BMC q ${colors}BT ` +
|
`/Tx BMC q ${colors}BT ` +
|
||||||
@ -2105,6 +2257,9 @@ class WidgetAnnotation extends Annotation {
|
|||||||
|
|
||||||
_computeFontSize(height, width, text, font, lineCount) {
|
_computeFontSize(height, width, text, font, lineCount) {
|
||||||
let { fontSize } = this.data.defaultAppearanceData;
|
let { fontSize } = this.data.defaultAppearanceData;
|
||||||
|
let lineHeight = (fontSize || 12) * LINE_FACTOR,
|
||||||
|
numberOfLines = Math.round(height / lineHeight);
|
||||||
|
|
||||||
if (!fontSize) {
|
if (!fontSize) {
|
||||||
// A zero value for size means that the font shall be auto-sized:
|
// A zero value for size means that the font shall be auto-sized:
|
||||||
// its size shall be computed as a function of the height of the
|
// its size shall be computed as a function of the height of the
|
||||||
@ -2115,8 +2270,12 @@ class WidgetAnnotation extends Annotation {
|
|||||||
if (lineCount === -1) {
|
if (lineCount === -1) {
|
||||||
const textWidth = this._getTextWidth(text, font);
|
const textWidth = this._getTextWidth(text, font);
|
||||||
fontSize = roundWithTwoDigits(
|
fontSize = roundWithTwoDigits(
|
||||||
Math.min(height / LINE_FACTOR, width / textWidth)
|
Math.min(
|
||||||
|
height / LINE_FACTOR,
|
||||||
|
textWidth > width ? width / textWidth : Infinity
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
numberOfLines = 1;
|
||||||
} else {
|
} else {
|
||||||
const lines = text.split(/\r\n?|\n/);
|
const lines = text.split(/\r\n?|\n/);
|
||||||
const cachedLines = [];
|
const cachedLines = [];
|
||||||
@ -2152,9 +2311,6 @@ class WidgetAnnotation extends Annotation {
|
|||||||
// a font size equal to 12 (this is the default font size in
|
// a font size equal to 12 (this is the default font size in
|
||||||
// Acrobat).
|
// Acrobat).
|
||||||
// Then we'll adjust font size to what we have really.
|
// Then we'll adjust font size to what we have really.
|
||||||
fontSize = 12;
|
|
||||||
let lineHeight = fontSize * LINE_FACTOR;
|
|
||||||
let numberOfLines = Math.round(height / lineHeight);
|
|
||||||
numberOfLines = Math.max(numberOfLines, lineCount);
|
numberOfLines = Math.max(numberOfLines, lineCount);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -2177,10 +2333,24 @@ class WidgetAnnotation extends Annotation {
|
|||||||
fontColor,
|
fontColor,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return [this._defaultAppearance, fontSize];
|
|
||||||
|
return [this._defaultAppearance, fontSize, height / numberOfLines];
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderText(text, font, fontSize, totalWidth, alignment, hPadding, vPadding) {
|
_renderText(
|
||||||
|
text,
|
||||||
|
font,
|
||||||
|
fontSize,
|
||||||
|
totalWidth,
|
||||||
|
alignment,
|
||||||
|
prevInfo,
|
||||||
|
hPadding,
|
||||||
|
vPadding
|
||||||
|
) {
|
||||||
|
// TODO: we need to take into account (if possible) how the text
|
||||||
|
// is rendered. For example in arabic, the cumulated width of some
|
||||||
|
// glyphs isn't equal to the width of the rendered glyphs because
|
||||||
|
// of ligatures.
|
||||||
let shift;
|
let shift;
|
||||||
if (alignment === 1) {
|
if (alignment === 1) {
|
||||||
// Center
|
// Center
|
||||||
@ -2193,10 +2363,11 @@ class WidgetAnnotation extends Annotation {
|
|||||||
} else {
|
} else {
|
||||||
shift = hPadding;
|
shift = hPadding;
|
||||||
}
|
}
|
||||||
shift = numberToString(shift);
|
const shiftStr = numberToString(shift - prevInfo.shift);
|
||||||
|
prevInfo.shift = shift;
|
||||||
vPadding = numberToString(vPadding);
|
vPadding = numberToString(vPadding);
|
||||||
|
|
||||||
return `${shift} ${vPadding} Td (${escapeString(text)}) Tj`;
|
return `${shiftStr} ${vPadding} Td (${escapeString(text)}) Tj`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2296,32 +2467,39 @@ class TextWidgetAnnotation extends WidgetAnnotation {
|
|||||||
defaultAppearance,
|
defaultAppearance,
|
||||||
font,
|
font,
|
||||||
text,
|
text,
|
||||||
|
fontSize,
|
||||||
width,
|
width,
|
||||||
|
height,
|
||||||
hPadding,
|
hPadding,
|
||||||
vPadding,
|
vPadding,
|
||||||
|
descent,
|
||||||
|
lineHeight,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
) {
|
) {
|
||||||
const combWidth = numberToString(width / this.data.maxLen);
|
const combWidth = width / this.data.maxLen;
|
||||||
|
// Empty or it has a trailing whitespace.
|
||||||
|
const colors = this.getBorderAndBackgroundAppearances(annotationStorage);
|
||||||
|
|
||||||
const buf = [];
|
const buf = [];
|
||||||
const positions = font.getCharPositions(text);
|
const positions = font.getCharPositions(text);
|
||||||
for (const [start, end] of positions) {
|
for (const [start, end] of positions) {
|
||||||
buf.push(`(${escapeString(text.substring(start, end))}) Tj`);
|
buf.push(`(${escapeString(text.substring(start, end))}) Tj`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty or it has a trailing whitespace.
|
const renderedComb = buf.join(` ${numberToString(combWidth)} 0 Td `);
|
||||||
const colors = this.getBorderAndBackgroundAppearances(annotationStorage);
|
|
||||||
const renderedComb = buf.join(` ${combWidth} 0 Td `);
|
|
||||||
return (
|
return (
|
||||||
`/Tx BMC q ${colors}BT ` +
|
`/Tx BMC q ${colors}BT ` +
|
||||||
defaultAppearance +
|
defaultAppearance +
|
||||||
` 1 0 0 1 ${hPadding} ${vPadding} Tm ${renderedComb}` +
|
` 1 0 0 1 ${numberToString(hPadding)} ${numberToString(
|
||||||
|
vPadding + descent
|
||||||
|
)} Tm ${renderedComb}` +
|
||||||
" ET Q EMC"
|
" ET Q EMC"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getMultilineAppearance(
|
_getMultilineAppearance(
|
||||||
defaultAppearance,
|
defaultAppearance,
|
||||||
text,
|
lines,
|
||||||
font,
|
font,
|
||||||
fontSize,
|
fontSize,
|
||||||
width,
|
width,
|
||||||
@ -2329,15 +2507,20 @@ class TextWidgetAnnotation extends WidgetAnnotation {
|
|||||||
alignment,
|
alignment,
|
||||||
hPadding,
|
hPadding,
|
||||||
vPadding,
|
vPadding,
|
||||||
|
descent,
|
||||||
|
lineHeight,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
) {
|
) {
|
||||||
const lines = text.split(/\r\n?|\n/);
|
|
||||||
const buf = [];
|
const buf = [];
|
||||||
const totalWidth = width - 2 * hPadding;
|
const totalWidth = width - 2 * hPadding;
|
||||||
for (const line of lines) {
|
const prevInfo = { shift: 0 };
|
||||||
|
for (let i = 0, ii = lines.length; i < ii; i++) {
|
||||||
|
const line = lines[i];
|
||||||
const chunks = this._splitLine(line, font, fontSize, totalWidth);
|
const chunks = this._splitLine(line, font, fontSize, totalWidth);
|
||||||
for (const chunk of chunks) {
|
for (let j = 0, jj = chunks.length; j < jj; j++) {
|
||||||
const padding = buf.length === 0 ? hPadding : 0;
|
const chunk = chunks[j];
|
||||||
|
const vShift =
|
||||||
|
i === 0 && j === 0 ? -vPadding - (lineHeight - descent) : -lineHeight;
|
||||||
buf.push(
|
buf.push(
|
||||||
this._renderText(
|
this._renderText(
|
||||||
chunk,
|
chunk,
|
||||||
@ -2345,29 +2528,28 @@ class TextWidgetAnnotation extends WidgetAnnotation {
|
|||||||
fontSize,
|
fontSize,
|
||||||
width,
|
width,
|
||||||
alignment,
|
alignment,
|
||||||
padding,
|
prevInfo,
|
||||||
-fontSize // <0 because a line is below the previous one
|
hPadding,
|
||||||
|
vShift
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderedText = buf.join("\n");
|
|
||||||
|
|
||||||
// Empty or it has a trailing whitespace.
|
// Empty or it has a trailing whitespace.
|
||||||
const colors = this.getBorderAndBackgroundAppearances(annotationStorage);
|
const colors = this.getBorderAndBackgroundAppearances(annotationStorage);
|
||||||
|
const renderedText = buf.join("\n");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
`/Tx BMC q ${colors}BT ` +
|
`/Tx BMC q ${colors}BT ` +
|
||||||
defaultAppearance +
|
defaultAppearance +
|
||||||
` 1 0 0 1 0 ${height} Tm ${renderedText}` +
|
` 1 0 0 1 0 ${numberToString(height)} Tm ${renderedText}` +
|
||||||
" ET Q EMC"
|
" ET Q EMC"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_splitLine(line, font, fontSize, width, cache = {}) {
|
_splitLine(line, font, fontSize, width, cache = {}) {
|
||||||
// TODO: need to handle chars which are not in the font.
|
line = cache.line || line;
|
||||||
line = cache.line || font.encodeString(line).join("");
|
|
||||||
|
|
||||||
const glyphs = cache.glyphs || font.charsToGlyphs(line);
|
const glyphs = cache.glyphs || font.charsToGlyphs(line);
|
||||||
|
|
||||||
@ -3031,9 +3213,9 @@ class ChoiceWidgetAnnotation extends WidgetAnnotation {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getAppearance(evaluator, task, annotationStorage) {
|
async _getAppearance(evaluator, task, intent, annotationStorage) {
|
||||||
if (this.data.combo) {
|
if (this.data.combo) {
|
||||||
return super._getAppearance(evaluator, task, annotationStorage);
|
return super._getAppearance(evaluator, task, intent, annotationStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
let exportedValue, rotation;
|
let exportedValue, rotation;
|
||||||
@ -3061,8 +3243,8 @@ class ChoiceWidgetAnnotation extends WidgetAnnotation {
|
|||||||
exportedValue = [exportedValue];
|
exportedValue = [exportedValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultPadding = 2;
|
const defaultPadding = 1;
|
||||||
const hPadding = defaultPadding;
|
const defaultHPadding = 2;
|
||||||
let totalHeight = this.data.rect[3] - this.data.rect[1];
|
let totalHeight = this.data.rect[3] - this.data.rect[1];
|
||||||
let totalWidth = this.data.rect[2] - this.data.rect[0];
|
let totalWidth = this.data.rect[2] - this.data.rect[0];
|
||||||
|
|
||||||
@ -3113,7 +3295,7 @@ class ChoiceWidgetAnnotation extends WidgetAnnotation {
|
|||||||
|
|
||||||
[defaultAppearance, fontSize] = this._computeFontSize(
|
[defaultAppearance, fontSize] = this._computeFontSize(
|
||||||
lineHeight,
|
lineHeight,
|
||||||
totalWidth - 2 * hPadding,
|
totalWidth - 2 * defaultHPadding,
|
||||||
value,
|
value,
|
||||||
font,
|
font,
|
||||||
-1
|
-1
|
||||||
@ -3159,9 +3341,9 @@ class ChoiceWidgetAnnotation extends WidgetAnnotation {
|
|||||||
}
|
}
|
||||||
buf.push("BT", defaultAppearance, `1 0 0 1 0 ${totalHeight} Tm`);
|
buf.push("BT", defaultAppearance, `1 0 0 1 0 ${totalHeight} Tm`);
|
||||||
|
|
||||||
|
const prevInfo = { shift: 0 };
|
||||||
for (let i = firstIndex; i < end; i++) {
|
for (let i = firstIndex; i < end; i++) {
|
||||||
const { displayValue } = this.data.options[i];
|
const { displayValue } = this.data.options[i];
|
||||||
const hpadding = i === firstIndex ? hPadding : 0;
|
|
||||||
const vpadding = i === firstIndex ? vPadding : 0;
|
const vpadding = i === firstIndex ? vPadding : 0;
|
||||||
buf.push(
|
buf.push(
|
||||||
this._renderText(
|
this._renderText(
|
||||||
@ -3170,7 +3352,8 @@ class ChoiceWidgetAnnotation extends WidgetAnnotation {
|
|||||||
fontSize,
|
fontSize,
|
||||||
totalWidth,
|
totalWidth,
|
||||||
0,
|
0,
|
||||||
hpadding,
|
prevInfo,
|
||||||
|
defaultHPadding,
|
||||||
-lineHeight + vpadding
|
-lineHeight + vpadding
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -3326,6 +3509,26 @@ class FreeTextAnnotation extends MarkupAnnotation {
|
|||||||
super(parameters);
|
super(parameters);
|
||||||
|
|
||||||
this.data.annotationType = AnnotationType.FREETEXT;
|
this.data.annotationType = AnnotationType.FREETEXT;
|
||||||
|
this.setDefaultAppearance(parameters);
|
||||||
|
if (!this.appearance && this._isOffscreenCanvasSupported) {
|
||||||
|
const fakeUnicodeFont = new FakeUnicodeFont(
|
||||||
|
parameters.xref,
|
||||||
|
"sans-serif"
|
||||||
|
);
|
||||||
|
const fontData = this.data.defaultAppearanceData;
|
||||||
|
this.appearance = fakeUnicodeFont.createAppearance(
|
||||||
|
this._contents.str,
|
||||||
|
this.rectangle,
|
||||||
|
this.rotation,
|
||||||
|
fontData.fontSize || 10,
|
||||||
|
fontData.fontColor
|
||||||
|
);
|
||||||
|
this._streams.push(this.appearance, FakeUnicodeFont.toUnicodeStream);
|
||||||
|
} else if (!this._isOffscreenCanvasSupported) {
|
||||||
|
warn(
|
||||||
|
"FreeTextAnnotation: OffscreenCanvas is not supported, annotation may not render correctly."
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasTextContent() {
|
get hasTextContent() {
|
||||||
@ -3341,22 +3544,27 @@ class FreeTextAnnotation extends MarkupAnnotation {
|
|||||||
freetext.set("Rect", rect);
|
freetext.set("Rect", rect);
|
||||||
const da = `/Helv ${fontSize} Tf ${getPdfColor(color, /* isFill */ true)}`;
|
const da = `/Helv ${fontSize} Tf ${getPdfColor(color, /* isFill */ true)}`;
|
||||||
freetext.set("DA", da);
|
freetext.set("DA", da);
|
||||||
freetext.set("Contents", value);
|
freetext.set(
|
||||||
|
"Contents",
|
||||||
|
isAscii(value) ? value : stringToUTF16BEString(value)
|
||||||
|
);
|
||||||
freetext.set("F", 4);
|
freetext.set("F", 4);
|
||||||
freetext.set("Border", [0, 0, 0]);
|
freetext.set("Border", [0, 0, 0]);
|
||||||
freetext.set("Rotate", rotation);
|
freetext.set("Rotate", rotation);
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
freetext.set("T", stringToUTF8String(user));
|
freetext.set("T", isAscii(user) ? user : stringToUTF16BEString(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
const n = new Dict(xref);
|
if (apRef || ap) {
|
||||||
freetext.set("AP", n);
|
const n = new Dict(xref);
|
||||||
|
freetext.set("AP", n);
|
||||||
|
|
||||||
if (apRef) {
|
if (apRef) {
|
||||||
n.set("N", apRef);
|
n.set("N", apRef);
|
||||||
} else {
|
} else {
|
||||||
n.set("N", ap);
|
n.set("N", ap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return freetext;
|
return freetext;
|
||||||
@ -3404,7 +3612,12 @@ class FreeTextAnnotation extends MarkupAnnotation {
|
|||||||
let totalWidth = -Infinity;
|
let totalWidth = -Infinity;
|
||||||
const encodedLines = [];
|
const encodedLines = [];
|
||||||
for (let line of lines) {
|
for (let line of lines) {
|
||||||
line = helv.encodeString(line).join("");
|
const encoded = helv.encodeString(line);
|
||||||
|
if (encoded.length > 1) {
|
||||||
|
// The font doesn't contain all the chars.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
line = encoded.join("");
|
||||||
encodedLines.push(line);
|
encodedLines.push(line);
|
||||||
let lineWidth = 0;
|
let lineWidth = 0;
|
||||||
const glyphs = helv.charsToGlyphs(line);
|
const glyphs = helv.charsToGlyphs(line);
|
||||||
@ -3454,7 +3667,7 @@ class FreeTextAnnotation extends MarkupAnnotation {
|
|||||||
appearanceStreamDict.set("Resources", resources);
|
appearanceStreamDict.set("Resources", resources);
|
||||||
|
|
||||||
if (rotation) {
|
if (rotation) {
|
||||||
const matrix = WidgetAnnotation._getRotationMatrix(rotation, w, h);
|
const matrix = getRotationMatrix(rotation, w, h);
|
||||||
appearanceStreamDict.set("Matrix", matrix);
|
appearanceStreamDict.set("Matrix", matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3897,7 +4110,7 @@ class InkAnnotation extends MarkupAnnotation {
|
|||||||
appearanceStreamDict.set("Length", appearance.length);
|
appearanceStreamDict.set("Length", appearance.length);
|
||||||
|
|
||||||
if (rotation) {
|
if (rotation) {
|
||||||
const matrix = WidgetAnnotation._getRotationMatrix(rotation, w, h);
|
const matrix = getRotationMatrix(rotation, w, h);
|
||||||
appearanceStreamDict.set("Matrix", matrix);
|
appearanceStreamDict.set("Matrix", matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,6 +572,43 @@ function getNewAnnotationsMap(annotationStorage) {
|
|||||||
return newAnnotationsByPage.size > 0 ? newAnnotationsByPage : null;
|
return newAnnotationsByPage.size > 0 ? newAnnotationsByPage : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stringToUTF16HexString(str) {
|
||||||
|
const buf = [];
|
||||||
|
for (let i = 0, ii = str.length; i < ii; i++) {
|
||||||
|
const char = str.charCodeAt(i);
|
||||||
|
buf.push(
|
||||||
|
((char >> 8) & 0xff).toString(16).padStart(2, "0"),
|
||||||
|
(char & 0xff).toString(16).padStart(2, "0")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return buf.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringToUTF16String(str) {
|
||||||
|
const buf = [];
|
||||||
|
for (let i = 0, ii = str.length; i < ii; i++) {
|
||||||
|
const char = str.charCodeAt(i);
|
||||||
|
buf.push(
|
||||||
|
String.fromCharCode((char >> 8) & 0xff),
|
||||||
|
String.fromCharCode(char & 0xff)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return buf.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRotationMatrix(rotation, width, height) {
|
||||||
|
switch (rotation) {
|
||||||
|
case 90:
|
||||||
|
return [0, 1, -1, 0, width, 0];
|
||||||
|
case 180:
|
||||||
|
return [-1, 0, 0, -1, width, height];
|
||||||
|
case 270:
|
||||||
|
return [0, -1, 1, 0, 0, height];
|
||||||
|
default:
|
||||||
|
throw new Error("Invalid rotation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
collectActions,
|
collectActions,
|
||||||
DocStats,
|
DocStats,
|
||||||
@ -581,6 +618,7 @@ export {
|
|||||||
getInheritableProperty,
|
getInheritableProperty,
|
||||||
getLookupTableFactory,
|
getLookupTableFactory,
|
||||||
getNewAnnotationsMap,
|
getNewAnnotationsMap,
|
||||||
|
getRotationMatrix,
|
||||||
isWhiteSpace,
|
isWhiteSpace,
|
||||||
log2,
|
log2,
|
||||||
MissingDataException,
|
MissingDataException,
|
||||||
@ -592,6 +630,8 @@ export {
|
|||||||
readUint16,
|
readUint16,
|
||||||
readUint32,
|
readUint32,
|
||||||
recoverJsURL,
|
recoverJsURL,
|
||||||
|
stringToUTF16HexString,
|
||||||
|
stringToUTF16String,
|
||||||
toRomanNumerals,
|
toRomanNumerals,
|
||||||
validateCSSFont,
|
validateCSSFont,
|
||||||
XRefEntryException,
|
XRefEntryException,
|
||||||
|
@ -13,11 +13,16 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { escapePDFName, numberToString } from "./core_utils.js";
|
import { Dict, Name } from "./primitives.js";
|
||||||
import { OPS, warn } from "../shared/util.js";
|
import {
|
||||||
|
escapePDFName,
|
||||||
|
getRotationMatrix,
|
||||||
|
numberToString,
|
||||||
|
stringToUTF16HexString,
|
||||||
|
} from "./core_utils.js";
|
||||||
|
import { LINE_DESCENT_FACTOR, LINE_FACTOR, OPS, warn } from "../shared/util.js";
|
||||||
import { ColorSpace } from "./colorspace.js";
|
import { ColorSpace } from "./colorspace.js";
|
||||||
import { EvaluatorPreprocessor } from "./evaluator.js";
|
import { EvaluatorPreprocessor } from "./evaluator.js";
|
||||||
import { Name } from "./primitives.js";
|
|
||||||
import { StringStream } from "./stream.js";
|
import { StringStream } from "./stream.js";
|
||||||
|
|
||||||
class DefaultAppearanceEvaluator extends EvaluatorPreprocessor {
|
class DefaultAppearanceEvaluator extends EvaluatorPreprocessor {
|
||||||
@ -101,4 +106,250 @@ function createDefaultAppearance({ fontSize, fontName, fontColor }) {
|
|||||||
)}`;
|
)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { createDefaultAppearance, getPdfColor, parseDefaultAppearance };
|
class FakeUnicodeFont {
|
||||||
|
constructor(xref, fontFamily) {
|
||||||
|
this.xref = xref;
|
||||||
|
this.widths = null;
|
||||||
|
this.firstChar = Infinity;
|
||||||
|
this.lastChar = -Infinity;
|
||||||
|
this.fontFamily = fontFamily;
|
||||||
|
|
||||||
|
const canvas = new OffscreenCanvas(1, 1);
|
||||||
|
this.ctxMeasure = canvas.getContext("2d");
|
||||||
|
|
||||||
|
if (!FakeUnicodeFont._fontNameId) {
|
||||||
|
FakeUnicodeFont._fontNameId = 1;
|
||||||
|
}
|
||||||
|
this.fontName = Name.get(
|
||||||
|
`InvalidPDFjsFont_${fontFamily}_${FakeUnicodeFont._fontNameId++}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get toUnicodeRef() {
|
||||||
|
if (!FakeUnicodeFont._toUnicodeRef) {
|
||||||
|
const toUnicode = `/CIDInit /ProcSet findresource begin
|
||||||
|
12 dict begin
|
||||||
|
begincmap
|
||||||
|
/CIDSystemInfo
|
||||||
|
<< /Registry (Adobe)
|
||||||
|
/Ordering (UCS) /Supplement 0 >> def
|
||||||
|
/CMapName /Adobe-Identity-UCS def
|
||||||
|
/CMapType 2 def
|
||||||
|
1 begincodespacerange
|
||||||
|
<0000> <FFFF>
|
||||||
|
endcodespacerange
|
||||||
|
1 beginbfrange
|
||||||
|
<0000> <FFFF> <0000>
|
||||||
|
endbfrange
|
||||||
|
endcmap CMapName currentdict /CMap defineresource pop end end`;
|
||||||
|
const toUnicodeStream = (FakeUnicodeFont.toUnicodeStream =
|
||||||
|
new StringStream(toUnicode));
|
||||||
|
const toUnicodeDict = new Dict(this.xref);
|
||||||
|
toUnicodeStream.dict = toUnicodeDict;
|
||||||
|
toUnicodeDict.set("Length", toUnicode.length);
|
||||||
|
FakeUnicodeFont._toUnicodeRef =
|
||||||
|
this.xref.getNewPersistentRef(toUnicodeStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FakeUnicodeFont._toUnicodeRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
get fontDescriptorRef() {
|
||||||
|
if (!FakeUnicodeFont._fontDescriptorRef) {
|
||||||
|
const fontDescriptor = new Dict(this.xref);
|
||||||
|
fontDescriptor.set("Type", Name.get("FontDescriptor"));
|
||||||
|
fontDescriptor.set("FontName", this.fontName);
|
||||||
|
fontDescriptor.set("FontFamily", "MyriadPro Regular");
|
||||||
|
fontDescriptor.set("FontBBox", [0, 0, 0, 0]);
|
||||||
|
fontDescriptor.set("FontStretch", Name.get("Normal"));
|
||||||
|
fontDescriptor.set("FontWeight", 400);
|
||||||
|
fontDescriptor.set("ItalicAngle", 0);
|
||||||
|
|
||||||
|
FakeUnicodeFont._fontDescriptorRef =
|
||||||
|
this.xref.getNewPersistentRef(fontDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FakeUnicodeFont._fontDescriptorRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
get descendantFontRef() {
|
||||||
|
const descendantFont = new Dict(this.xref);
|
||||||
|
descendantFont.set("BaseFont", this.fontName);
|
||||||
|
descendantFont.set("Type", Name.get("Font"));
|
||||||
|
descendantFont.set("Subtype", Name.get("CIDFontType0"));
|
||||||
|
descendantFont.set("CIDToGIDMap", Name.get("Identity"));
|
||||||
|
descendantFont.set("FirstChar", this.firstChar);
|
||||||
|
descendantFont.set("LastChar", this.lastChar);
|
||||||
|
descendantFont.set("FontDescriptor", this.fontDescriptorRef);
|
||||||
|
descendantFont.set("DW", 1000);
|
||||||
|
|
||||||
|
const widths = [];
|
||||||
|
const chars = [...this.widths.entries()].sort();
|
||||||
|
let currentChar = null;
|
||||||
|
let currentWidths = null;
|
||||||
|
for (const [char, width] of chars) {
|
||||||
|
if (!currentChar) {
|
||||||
|
currentChar = char;
|
||||||
|
currentWidths = [width];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (char === currentChar + currentWidths.length) {
|
||||||
|
currentWidths.push(width);
|
||||||
|
} else {
|
||||||
|
widths.push(currentChar, currentWidths);
|
||||||
|
currentChar = char;
|
||||||
|
currentWidths = [width];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentChar) {
|
||||||
|
widths.push(currentChar, currentWidths);
|
||||||
|
}
|
||||||
|
|
||||||
|
descendantFont.set("W", widths);
|
||||||
|
|
||||||
|
const cidSystemInfo = new Dict(this.xref);
|
||||||
|
cidSystemInfo.set("Ordering", "Identity");
|
||||||
|
cidSystemInfo.set("Registry", "Adobe");
|
||||||
|
cidSystemInfo.set("Supplement", 0);
|
||||||
|
descendantFont.set("CIDSystemInfo", cidSystemInfo);
|
||||||
|
|
||||||
|
return this.xref.getNewPersistentRef(descendantFont);
|
||||||
|
}
|
||||||
|
|
||||||
|
get baseFontRef() {
|
||||||
|
const baseFont = new Dict(this.xref);
|
||||||
|
baseFont.set("BaseFont", this.fontName);
|
||||||
|
baseFont.set("Type", Name.get("Font"));
|
||||||
|
baseFont.set("Subtype", Name.get("Type0"));
|
||||||
|
baseFont.set("Encoding", Name.get("Identity-H"));
|
||||||
|
baseFont.set("DescendantFonts", [this.descendantFontRef]);
|
||||||
|
baseFont.set("ToUnicode", this.toUnicodeRef);
|
||||||
|
|
||||||
|
return this.xref.getNewPersistentRef(baseFont);
|
||||||
|
}
|
||||||
|
|
||||||
|
get resources() {
|
||||||
|
const resources = new Dict(this.xref);
|
||||||
|
const font = new Dict(this.xref);
|
||||||
|
font.set(this.fontName.name, this.baseFontRef);
|
||||||
|
resources.set("Font", font);
|
||||||
|
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
_createContext() {
|
||||||
|
this.widths = new Map();
|
||||||
|
this.ctxMeasure.font = `1000px ${this.fontFamily}`;
|
||||||
|
|
||||||
|
return this.ctxMeasure;
|
||||||
|
}
|
||||||
|
|
||||||
|
createFontResources(text) {
|
||||||
|
const ctx = this._createContext();
|
||||||
|
for (const line of text.split(/\r\n?|\n/)) {
|
||||||
|
for (const char of line.split("")) {
|
||||||
|
const code = char.charCodeAt(0);
|
||||||
|
if (this.widths.has(code)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const metrics = ctx.measureText(char);
|
||||||
|
const width = Math.ceil(metrics.width);
|
||||||
|
this.widths.set(code, width);
|
||||||
|
this.firstChar = Math.min(code, this.firstChar);
|
||||||
|
this.lastChar = Math.max(code, this.lastChar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
createAppearance(text, rect, rotation, fontSize, bgColor) {
|
||||||
|
const ctx = this._createContext();
|
||||||
|
const lines = [];
|
||||||
|
let maxWidth = -Infinity;
|
||||||
|
for (const line of text.split(/\r\n?|\n/)) {
|
||||||
|
lines.push(line);
|
||||||
|
// The line width isn't the sum of the char widths, because in some
|
||||||
|
// languages, like arabic, it'd be wrong because of ligatures.
|
||||||
|
const lineWidth = ctx.measureText(line).width;
|
||||||
|
maxWidth = Math.max(maxWidth, lineWidth);
|
||||||
|
for (const char of line.split("")) {
|
||||||
|
const code = char.charCodeAt(0);
|
||||||
|
let width = this.widths.get(code);
|
||||||
|
if (width === undefined) {
|
||||||
|
const metrics = ctx.measureText(char);
|
||||||
|
width = Math.ceil(metrics.width);
|
||||||
|
this.widths.set(code, width);
|
||||||
|
this.firstChar = Math.min(code, this.firstChar);
|
||||||
|
this.lastChar = Math.max(code, this.lastChar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maxWidth *= fontSize / 1000;
|
||||||
|
|
||||||
|
const [x1, y1, x2, y2] = rect;
|
||||||
|
let w = x2 - x1;
|
||||||
|
let h = y2 - y1;
|
||||||
|
|
||||||
|
if (rotation % 180 !== 0) {
|
||||||
|
[w, h] = [h, w];
|
||||||
|
}
|
||||||
|
|
||||||
|
let hscale = 1;
|
||||||
|
if (maxWidth > w) {
|
||||||
|
hscale = w / maxWidth;
|
||||||
|
}
|
||||||
|
let vscale = 1;
|
||||||
|
const lineHeight = LINE_FACTOR * fontSize;
|
||||||
|
const lineDescent = LINE_DESCENT_FACTOR * fontSize;
|
||||||
|
const maxHeight = lineHeight * lines.length;
|
||||||
|
if (maxHeight > h) {
|
||||||
|
vscale = h / maxHeight;
|
||||||
|
}
|
||||||
|
const fscale = Math.min(hscale, vscale);
|
||||||
|
const newFontSize = fontSize * fscale;
|
||||||
|
|
||||||
|
const buffer = [
|
||||||
|
"q",
|
||||||
|
`0 0 ${numberToString(w)} ${numberToString(h)} re W n`,
|
||||||
|
`BT`,
|
||||||
|
`1 0 0 1 0 ${numberToString(h + lineDescent)} Tm 0 Tc ${getPdfColor(
|
||||||
|
bgColor,
|
||||||
|
/* isFill */ true
|
||||||
|
)}`,
|
||||||
|
`/${this.fontName.name} ${numberToString(newFontSize)} Tf`,
|
||||||
|
];
|
||||||
|
|
||||||
|
const vShift = numberToString(lineHeight);
|
||||||
|
for (const line of lines) {
|
||||||
|
buffer.push(`0 -${vShift} Td <${stringToUTF16HexString(line)}> Tj`);
|
||||||
|
}
|
||||||
|
buffer.push("ET", "Q");
|
||||||
|
const appearance = buffer.join("\n");
|
||||||
|
|
||||||
|
const appearanceStreamDict = new Dict(this.xref);
|
||||||
|
appearanceStreamDict.set("Subtype", Name.get("Form"));
|
||||||
|
appearanceStreamDict.set("Type", Name.get("XObject"));
|
||||||
|
appearanceStreamDict.set("BBox", [0, 0, w, h]);
|
||||||
|
appearanceStreamDict.set("Length", appearance.length);
|
||||||
|
appearanceStreamDict.set("Resources", this.resources);
|
||||||
|
|
||||||
|
if (rotation) {
|
||||||
|
const matrix = getRotationMatrix(rotation, w, h);
|
||||||
|
appearanceStreamDict.set("Matrix", matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ap = new StringStream(appearance);
|
||||||
|
ap.dict = appearanceStreamDict;
|
||||||
|
|
||||||
|
return ap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
createDefaultAppearance,
|
||||||
|
FakeUnicodeFont,
|
||||||
|
getPdfColor,
|
||||||
|
parseDefaultAppearance,
|
||||||
|
};
|
||||||
|
@ -93,6 +93,7 @@ const EXPORT_DATA_PROPERTIES = [
|
|||||||
"fallbackName",
|
"fallbackName",
|
||||||
"fontMatrix",
|
"fontMatrix",
|
||||||
"fontType",
|
"fontType",
|
||||||
|
"isInvalidPDFjsFont",
|
||||||
"isType3Font",
|
"isType3Font",
|
||||||
"italic",
|
"italic",
|
||||||
"loadedName",
|
"loadedName",
|
||||||
@ -952,13 +953,17 @@ class Font {
|
|||||||
this.type = type;
|
this.type = type;
|
||||||
this.subtype = subtype;
|
this.subtype = subtype;
|
||||||
|
|
||||||
let fallbackName = "sans-serif";
|
const matches = name.match(/^InvalidPDFjsFont_(.*)_\d+$/);
|
||||||
if (this.isMonospace) {
|
this.isInvalidPDFjsFont = !!matches;
|
||||||
fallbackName = "monospace";
|
if (this.isInvalidPDFjsFont) {
|
||||||
|
this.fallbackName = matches[1];
|
||||||
|
} else if (this.isMonospace) {
|
||||||
|
this.fallbackName = "monospace";
|
||||||
} else if (this.isSerifFont) {
|
} else if (this.isSerifFont) {
|
||||||
fallbackName = "serif";
|
this.fallbackName = "serif";
|
||||||
|
} else {
|
||||||
|
this.fallbackName = "sans-serif";
|
||||||
}
|
}
|
||||||
this.fallbackName = fallbackName;
|
|
||||||
|
|
||||||
this.differences = properties.differences;
|
this.differences = properties.differences;
|
||||||
this.widths = properties.widths;
|
this.widths = properties.widths;
|
||||||
|
@ -625,6 +625,11 @@ class WorkerMessageHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const needAppearances =
|
||||||
|
acroFormRef &&
|
||||||
|
acroForm instanceof Dict &&
|
||||||
|
newRefs.some(ref => ref.needAppearances);
|
||||||
|
|
||||||
const xfa = (acroForm instanceof Dict && acroForm.get("XFA")) || null;
|
const xfa = (acroForm instanceof Dict && acroForm.get("XFA")) || null;
|
||||||
let xfaDatasetsRef = null;
|
let xfaDatasetsRef = null;
|
||||||
let hasXfaDatasetsEntry = false;
|
let hasXfaDatasetsEntry = false;
|
||||||
@ -632,15 +637,13 @@ class WorkerMessageHandler {
|
|||||||
for (let i = 0, ii = xfa.length; i < ii; i += 2) {
|
for (let i = 0, ii = xfa.length; i < ii; i += 2) {
|
||||||
if (xfa[i] === "datasets") {
|
if (xfa[i] === "datasets") {
|
||||||
xfaDatasetsRef = xfa[i + 1];
|
xfaDatasetsRef = xfa[i + 1];
|
||||||
acroFormRef = null;
|
|
||||||
hasXfaDatasetsEntry = true;
|
hasXfaDatasetsEntry = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (xfaDatasetsRef === null) {
|
if (xfaDatasetsRef === null) {
|
||||||
xfaDatasetsRef = xref.getNewRef();
|
xfaDatasetsRef = xref.getNewTemporaryRef();
|
||||||
}
|
}
|
||||||
} else if (xfa) {
|
} else if (xfa) {
|
||||||
acroFormRef = null;
|
|
||||||
// TODO: Support XFA streams.
|
// TODO: Support XFA streams.
|
||||||
warn("Unsupported XFA type.");
|
warn("Unsupported XFA type.");
|
||||||
}
|
}
|
||||||
@ -661,7 +664,7 @@ class WorkerMessageHandler {
|
|||||||
newXrefInfo = {
|
newXrefInfo = {
|
||||||
rootRef: xref.trailer.getRaw("Root") || null,
|
rootRef: xref.trailer.getRaw("Root") || null,
|
||||||
encryptRef: xref.trailer.getRaw("Encrypt") || null,
|
encryptRef: xref.trailer.getRaw("Encrypt") || null,
|
||||||
newRef: xref.getNewRef(),
|
newRef: xref.getNewTemporaryRef(),
|
||||||
infoRef: xref.trailer.getRaw("Info") || null,
|
infoRef: xref.trailer.getRaw("Info") || null,
|
||||||
info: infoObj,
|
info: infoObj,
|
||||||
fileIds: xref.trailer.get("ID") || null,
|
fileIds: xref.trailer.get("ID") || null,
|
||||||
@ -669,20 +672,24 @@ class WorkerMessageHandler {
|
|||||||
filename,
|
filename,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
xref.resetNewRef();
|
|
||||||
|
|
||||||
return incrementalUpdate({
|
try {
|
||||||
originalData: stream.bytes,
|
return incrementalUpdate({
|
||||||
xrefInfo: newXrefInfo,
|
originalData: stream.bytes,
|
||||||
newRefs,
|
xrefInfo: newXrefInfo,
|
||||||
xref,
|
newRefs,
|
||||||
hasXfa: !!xfa,
|
xref,
|
||||||
xfaDatasetsRef,
|
hasXfa: !!xfa,
|
||||||
hasXfaDatasetsEntry,
|
xfaDatasetsRef,
|
||||||
acroFormRef,
|
hasXfaDatasetsEntry,
|
||||||
acroForm,
|
needAppearances,
|
||||||
xfaData,
|
acroFormRef,
|
||||||
});
|
acroForm,
|
||||||
|
xfaData,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
xref.resetNewTemporaryRef();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -46,7 +46,7 @@ function writeStream(stream, buffer, transform) {
|
|||||||
if (transform !== null) {
|
if (transform !== null) {
|
||||||
string = transform.encryptString(string);
|
string = transform.encryptString(string);
|
||||||
}
|
}
|
||||||
buffer.push(string, "\nendstream\n");
|
buffer.push(string, "\nendstream");
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeArray(array, buffer, transform) {
|
function writeArray(array, buffer, transform) {
|
||||||
@ -150,54 +150,58 @@ function writeXFADataForAcroform(str, newRefs) {
|
|||||||
return buffer.join("");
|
return buffer.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateXFA({
|
function updateAcroform({
|
||||||
xfaData,
|
|
||||||
xfaDatasetsRef,
|
|
||||||
hasXfaDatasetsEntry,
|
|
||||||
acroFormRef,
|
|
||||||
acroForm,
|
|
||||||
newRefs,
|
|
||||||
xref,
|
xref,
|
||||||
xrefInfo,
|
acroForm,
|
||||||
|
acroFormRef,
|
||||||
|
hasXfa,
|
||||||
|
hasXfaDatasetsEntry,
|
||||||
|
xfaDatasetsRef,
|
||||||
|
needAppearances,
|
||||||
|
newRefs,
|
||||||
}) {
|
}) {
|
||||||
if (xref === null) {
|
if (hasXfa && !hasXfaDatasetsEntry && !xfaDatasetsRef) {
|
||||||
|
warn("XFA - Cannot save it");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!needAppearances && (!hasXfa || !xfaDatasetsRef)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasXfaDatasetsEntry) {
|
// Clone the acroForm.
|
||||||
if (!acroFormRef) {
|
const dict = new Dict(xref);
|
||||||
warn("XFA - Cannot save it");
|
for (const key of acroForm.getKeys()) {
|
||||||
return;
|
dict.set(key, acroForm.getRaw(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasXfa && !hasXfaDatasetsEntry) {
|
||||||
// We've a XFA array which doesn't contain a datasets entry.
|
// We've a XFA array which doesn't contain a datasets entry.
|
||||||
// So we'll update the AcroForm dictionary to have an XFA containing
|
// So we'll update the AcroForm dictionary to have an XFA containing
|
||||||
// the datasets.
|
// the datasets.
|
||||||
const oldXfa = acroForm.get("XFA");
|
const newXfa = acroForm.get("XFA").slice();
|
||||||
const newXfa = oldXfa.slice();
|
|
||||||
newXfa.splice(2, 0, "datasets");
|
newXfa.splice(2, 0, "datasets");
|
||||||
newXfa.splice(3, 0, xfaDatasetsRef);
|
newXfa.splice(3, 0, xfaDatasetsRef);
|
||||||
|
|
||||||
acroForm.set("XFA", newXfa);
|
dict.set("XFA", newXfa);
|
||||||
|
|
||||||
const encrypt = xref.encrypt;
|
|
||||||
let transform = null;
|
|
||||||
if (encrypt) {
|
|
||||||
transform = encrypt.createCipherTransform(
|
|
||||||
acroFormRef.num,
|
|
||||||
acroFormRef.gen
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const buffer = [`${acroFormRef.num} ${acroFormRef.gen} obj\n`];
|
|
||||||
writeDict(acroForm, buffer, transform);
|
|
||||||
buffer.push("\n");
|
|
||||||
|
|
||||||
acroForm.set("XFA", oldXfa);
|
|
||||||
|
|
||||||
newRefs.push({ ref: acroFormRef, data: buffer.join("") });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (needAppearances) {
|
||||||
|
dict.set("NeedAppearances", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const encrypt = xref.encrypt;
|
||||||
|
let transform = null;
|
||||||
|
if (encrypt) {
|
||||||
|
transform = encrypt.createCipherTransform(acroFormRef.num, acroFormRef.gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = [];
|
||||||
|
writeObject(acroFormRef, dict, buffer, transform);
|
||||||
|
|
||||||
|
newRefs.push({ ref: acroFormRef, data: buffer.join("") });
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateXFA({ xfaData, xfaDatasetsRef, newRefs, xref }) {
|
||||||
if (xfaData === null) {
|
if (xfaData === null) {
|
||||||
const datasets = xref.fetchIfRef(xfaDatasetsRef);
|
const datasets = xref.fetchIfRef(xfaDatasetsRef);
|
||||||
xfaData = writeXFADataForAcroform(datasets.getString(), newRefs);
|
xfaData = writeXFADataForAcroform(datasets.getString(), newRefs);
|
||||||
@ -228,20 +232,28 @@ function incrementalUpdate({
|
|||||||
hasXfa = false,
|
hasXfa = false,
|
||||||
xfaDatasetsRef = null,
|
xfaDatasetsRef = null,
|
||||||
hasXfaDatasetsEntry = false,
|
hasXfaDatasetsEntry = false,
|
||||||
|
needAppearances,
|
||||||
acroFormRef = null,
|
acroFormRef = null,
|
||||||
acroForm = null,
|
acroForm = null,
|
||||||
xfaData = null,
|
xfaData = null,
|
||||||
}) {
|
}) {
|
||||||
|
updateAcroform({
|
||||||
|
xref,
|
||||||
|
acroForm,
|
||||||
|
acroFormRef,
|
||||||
|
hasXfa,
|
||||||
|
hasXfaDatasetsEntry,
|
||||||
|
xfaDatasetsRef,
|
||||||
|
needAppearances,
|
||||||
|
newRefs,
|
||||||
|
});
|
||||||
|
|
||||||
if (hasXfa) {
|
if (hasXfa) {
|
||||||
updateXFA({
|
updateXFA({
|
||||||
xfaData,
|
xfaData,
|
||||||
xfaDatasetsRef,
|
xfaDatasetsRef,
|
||||||
hasXfaDatasetsEntry,
|
|
||||||
acroFormRef,
|
|
||||||
acroForm,
|
|
||||||
newRefs,
|
newRefs,
|
||||||
xref,
|
xref,
|
||||||
xrefInfo,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,18 +42,34 @@ class XRef {
|
|||||||
this._cacheMap = new Map(); // Prepare the XRef cache.
|
this._cacheMap = new Map(); // Prepare the XRef cache.
|
||||||
this._pendingRefs = new RefSet();
|
this._pendingRefs = new RefSet();
|
||||||
this.stats = new DocStats(pdfManager.msgHandler);
|
this.stats = new DocStats(pdfManager.msgHandler);
|
||||||
this._newRefNum = null;
|
this._newPersistentRefNum = null;
|
||||||
|
this._newTemporaryRefNum = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNewRef() {
|
getNewPersistentRef(obj) {
|
||||||
if (this._newRefNum === null) {
|
// When printing we don't care that much about the ref number by itself, it
|
||||||
this._newRefNum = this.entries.length || 1;
|
// can increase for ever and it allows to keep some re-usable refs.
|
||||||
|
if (this._newPersistentRefNum === null) {
|
||||||
|
this._newPersistentRefNum = this.entries.length || 1;
|
||||||
}
|
}
|
||||||
return Ref.get(this._newRefNum++, 0);
|
const num = this._newPersistentRefNum++;
|
||||||
|
this._cacheMap.set(num, obj);
|
||||||
|
return Ref.get(num, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
resetNewRef() {
|
getNewTemporaryRef() {
|
||||||
this._newRefNum = null;
|
// When saving we want to have some minimal numbers.
|
||||||
|
// Those refs are only created in order to be written in the final pdf
|
||||||
|
// stream.
|
||||||
|
if (this._newTemporaryRefNum === null) {
|
||||||
|
this._newTemporaryRefNum = this.entries.length || 1;
|
||||||
|
}
|
||||||
|
return Ref.get(this._newTemporaryRefNum++, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetNewTemporaryRef() {
|
||||||
|
// Called once saving is finished.
|
||||||
|
this._newTemporaryRefNum = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setStartXRef(startXRef) {
|
setStartXRef(startXRef) {
|
||||||
|
@ -2275,6 +2275,21 @@ class CanvasGraphics {
|
|||||||
|
|
||||||
ctx.lineWidth = lineWidth;
|
ctx.lineWidth = lineWidth;
|
||||||
|
|
||||||
|
if (font.isInvalidPDFjsFont) {
|
||||||
|
const chars = [];
|
||||||
|
let width = 0;
|
||||||
|
for (const glyph of glyphs) {
|
||||||
|
chars.push(glyph.unicode);
|
||||||
|
width += glyph.width;
|
||||||
|
}
|
||||||
|
ctx.fillText(chars.join(""), 0, 0);
|
||||||
|
current.x += width * widthAdvanceScale * textHScale;
|
||||||
|
ctx.restore();
|
||||||
|
this.compose();
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
let x = 0,
|
let x = 0,
|
||||||
i;
|
i;
|
||||||
for (i = 0; i < glyphsLength; ++i) {
|
for (i = 0; i < glyphsLength; ++i) {
|
||||||
|
@ -305,12 +305,7 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
}
|
}
|
||||||
const buffer = [];
|
const buffer = [];
|
||||||
for (const div of divs) {
|
for (const div of divs) {
|
||||||
const first = div.firstChild;
|
buffer.push(div.innerText.replace(/\r\n?|\n/, ""));
|
||||||
if (first?.nodeName === "#text") {
|
|
||||||
buffer.push(first.data);
|
|
||||||
} else {
|
|
||||||
buffer.push("");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return buffer.join("\n");
|
return buffer.join("\n");
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ const FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
|
|||||||
// the font size. Acrobat seems to use this value.
|
// the font size. Acrobat seems to use this value.
|
||||||
const LINE_FACTOR = 1.35;
|
const LINE_FACTOR = 1.35;
|
||||||
const LINE_DESCENT_FACTOR = 0.35;
|
const LINE_DESCENT_FACTOR = 0.35;
|
||||||
|
const BASELINE_FACTOR = LINE_DESCENT_FACTOR / LINE_FACTOR;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refer to the `WorkerTransport.getRenderingIntent`-method in the API, to see
|
* Refer to the `WorkerTransport.getRenderingIntent`-method in the API, to see
|
||||||
@ -47,6 +48,7 @@ const RenderingIntentFlag = {
|
|||||||
ANY: 0x01,
|
ANY: 0x01,
|
||||||
DISPLAY: 0x02,
|
DISPLAY: 0x02,
|
||||||
PRINT: 0x04,
|
PRINT: 0x04,
|
||||||
|
SAVE: 0x08,
|
||||||
ANNOTATIONS_FORMS: 0x10,
|
ANNOTATIONS_FORMS: 0x10,
|
||||||
ANNOTATIONS_STORAGE: 0x20,
|
ANNOTATIONS_STORAGE: 0x20,
|
||||||
ANNOTATIONS_DISABLE: 0x40,
|
ANNOTATIONS_DISABLE: 0x40,
|
||||||
@ -1159,6 +1161,7 @@ export {
|
|||||||
arraysToBytes,
|
arraysToBytes,
|
||||||
assert,
|
assert,
|
||||||
BaseException,
|
BaseException,
|
||||||
|
BASELINE_FACTOR,
|
||||||
bytesToString,
|
bytesToString,
|
||||||
CMapCompressionType,
|
CMapCompressionType,
|
||||||
createPromiseCapability,
|
createPromiseCapability,
|
||||||
|
@ -479,7 +479,30 @@ class Driver {
|
|||||||
enableXfa: task.enableXfa,
|
enableXfa: task.enableXfa,
|
||||||
styleElement: xfaStyleElement,
|
styleElement: xfaStyleElement,
|
||||||
});
|
});
|
||||||
loadingTask.promise.then(
|
let promise = loadingTask.promise;
|
||||||
|
|
||||||
|
if (task.save) {
|
||||||
|
if (!task.annotationStorage) {
|
||||||
|
promise = Promise.reject(
|
||||||
|
new Error("Missing `annotationStorage` entry.")
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
promise = loadingTask.promise.then(async doc => {
|
||||||
|
for (const [key, value] of Object.entries(
|
||||||
|
task.annotationStorage
|
||||||
|
)) {
|
||||||
|
doc.annotationStorage.setValue(key, value);
|
||||||
|
}
|
||||||
|
const data = await doc.saveDocument();
|
||||||
|
await loadingTask.destroy();
|
||||||
|
delete task.annotationStorage;
|
||||||
|
|
||||||
|
return getDocument(data).promise;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.then(
|
||||||
async doc => {
|
async doc => {
|
||||||
if (task.enableXfa) {
|
if (task.enableXfa) {
|
||||||
task.fontRules = "";
|
task.fontRules = "";
|
||||||
|
2
test/pdfs/.gitignore
vendored
2
test/pdfs/.gitignore
vendored
@ -551,3 +551,5 @@
|
|||||||
!bug1795263.pdf
|
!bug1795263.pdf
|
||||||
!issue15597.pdf
|
!issue15597.pdf
|
||||||
!bug1796741.pdf
|
!bug1796741.pdf
|
||||||
|
!textfields.pdf
|
||||||
|
!freetext_no_appearance.pdf
|
||||||
|
BIN
test/pdfs/freetext_no_appearance.pdf
Executable file
BIN
test/pdfs/freetext_no_appearance.pdf
Executable file
Binary file not shown.
BIN
test/pdfs/textfields.pdf
Executable file
BIN
test/pdfs/textfields.pdf
Executable file
Binary file not shown.
@ -6973,5 +6973,225 @@
|
|||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "eq",
|
"type": "eq",
|
||||||
"print": true
|
"print": true
|
||||||
|
},
|
||||||
|
{ "id": "ascii_print_textfields",
|
||||||
|
"file": "pdfs/textfields.pdf",
|
||||||
|
"md5": "5f743ca838ff9b7a286dbe52002860b7",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"print": true,
|
||||||
|
"annotationStorage": {
|
||||||
|
"32R": {
|
||||||
|
"value": "Hello World"
|
||||||
|
},
|
||||||
|
"35R": {
|
||||||
|
"value": "Hello World"
|
||||||
|
},
|
||||||
|
"38R": {
|
||||||
|
"value": "Hello World"
|
||||||
|
},
|
||||||
|
"34R": {
|
||||||
|
"value": "Hello World"
|
||||||
|
},
|
||||||
|
"37R": {
|
||||||
|
"value": "Hello World"
|
||||||
|
},
|
||||||
|
"40R": {
|
||||||
|
"value": "Hello World"
|
||||||
|
},
|
||||||
|
"33R": {
|
||||||
|
"value": "Hello World\nDlrow Olleh\nHello World"
|
||||||
|
},
|
||||||
|
"36R": {
|
||||||
|
"value": "Hello World\nDlrow Olleh\nHello World"
|
||||||
|
},
|
||||||
|
"39R": {
|
||||||
|
"value": "Hello World\nDlrow Olleh\nHello World"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ "id": "arabic_print_textfields",
|
||||||
|
"file": "pdfs/textfields.pdf",
|
||||||
|
"md5": "5f743ca838ff9b7a286dbe52002860b7",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"print": true,
|
||||||
|
"annotationStorage": {
|
||||||
|
"32R": {
|
||||||
|
"value": "مرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"35R": {
|
||||||
|
"value": "مرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"38R": {
|
||||||
|
"value": "مرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"34R": {
|
||||||
|
"value": "مرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"37R": {
|
||||||
|
"value": "مرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"40R": {
|
||||||
|
"value": "مرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"33R": {
|
||||||
|
"value": "مرحبا بالعالم\nملاعلاب ابحرم\nمرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"36R": {
|
||||||
|
"value": "مرحبا بالعالم\nملاعلاب ابحرم\nمرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"39R": {
|
||||||
|
"value": "مرحبا بالعالم\nملاعلاب ابحرم\nمرحبا بالعالم"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ "id": "ascii_save_print_textfields",
|
||||||
|
"file": "pdfs/textfields.pdf",
|
||||||
|
"md5": "5f743ca838ff9b7a286dbe52002860b7",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"save": true,
|
||||||
|
"print": true,
|
||||||
|
"annotationStorage": {
|
||||||
|
"32R": {
|
||||||
|
"value": "Hello World"
|
||||||
|
},
|
||||||
|
"35R": {
|
||||||
|
"value": "Hello World"
|
||||||
|
},
|
||||||
|
"38R": {
|
||||||
|
"value": "Hello World"
|
||||||
|
},
|
||||||
|
"34R": {
|
||||||
|
"value": "Hello World"
|
||||||
|
},
|
||||||
|
"37R": {
|
||||||
|
"value": "Hello World"
|
||||||
|
},
|
||||||
|
"40R": {
|
||||||
|
"value": "Hello World"
|
||||||
|
},
|
||||||
|
"33R": {
|
||||||
|
"value": "Hello World\nDlrow Olleh\nHello World"
|
||||||
|
},
|
||||||
|
"36R": {
|
||||||
|
"value": "Hello World\nDlrow Olleh\nHello World"
|
||||||
|
},
|
||||||
|
"39R": {
|
||||||
|
"value": "Hello World\nDlrow Olleh\nHello World"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ "id": "arabic_save_print_textfields",
|
||||||
|
"file": "pdfs/textfields.pdf",
|
||||||
|
"md5": "5f743ca838ff9b7a286dbe52002860b7",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"save": true,
|
||||||
|
"print": true,
|
||||||
|
"annotationStorage": {
|
||||||
|
"32R": {
|
||||||
|
"value": "مرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"35R": {
|
||||||
|
"value": "مرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"38R": {
|
||||||
|
"value": "مرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"34R": {
|
||||||
|
"value": "مرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"37R": {
|
||||||
|
"value": "مرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"40R": {
|
||||||
|
"value": "مرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"33R": {
|
||||||
|
"value": "مرحبا بالعالم\nملاعلاب ابحرم\nمرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"36R": {
|
||||||
|
"value": "مرحبا بالعالم\nملاعلاب ابحرم\nمرحبا بالعالم"
|
||||||
|
},
|
||||||
|
"39R": {
|
||||||
|
"value": "مرحبا بالعالم\nملاعلاب ابحرم\nمرحبا بالعالم"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ "id": "freetext_no_appearance",
|
||||||
|
"file": "pdfs/freetext_no_appearance.pdf",
|
||||||
|
"md5": "1dc519c06f1dc6f6e594f168080dcde9",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
|
{ "id": "freetext_print_no_appearance",
|
||||||
|
"file": "pdfs/freetext_no_appearance.pdf",
|
||||||
|
"md5": "1dc519c06f1dc6f6e594f168080dcde9",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"print": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "tracemonkey-multi-lang-editors",
|
||||||
|
"file": "pdfs/tracemonkey.pdf",
|
||||||
|
"md5": "9a192d8b1a7dc652a19835f6f08098bd",
|
||||||
|
"rounds": 1,
|
||||||
|
"lastPage": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"print": true,
|
||||||
|
"annotationStorage": {
|
||||||
|
"pdfjs_internal_editor_0": {
|
||||||
|
"annotationType": 3,
|
||||||
|
"color": [255, 0, 0],
|
||||||
|
"fontSize": 10,
|
||||||
|
"value": "Hello World",
|
||||||
|
"pageIndex": 0,
|
||||||
|
"rect": [67.5, 143, 119, 156.5],
|
||||||
|
"rotation": 0
|
||||||
|
},
|
||||||
|
"pdfjs_internal_editor_1": {
|
||||||
|
"annotationType": 3,
|
||||||
|
"color": [0, 255, 0],
|
||||||
|
"fontSize": 10,
|
||||||
|
"value": "مرحبا بالعالم",
|
||||||
|
"pageIndex": 0,
|
||||||
|
"rect": [67.5, 243, 119, 256.5],
|
||||||
|
"rotation": 0
|
||||||
|
},
|
||||||
|
"pdfjs_internal_editor_2": {
|
||||||
|
"annotationType": 3,
|
||||||
|
"color": [0, 0, 255],
|
||||||
|
"fontSize": 10,
|
||||||
|
"value": "你好世界",
|
||||||
|
"pageIndex": 0,
|
||||||
|
"rect": [67.5, 343, 119, 356.5],
|
||||||
|
"rotation": 0
|
||||||
|
},
|
||||||
|
"pdfjs_internal_editor_3": {
|
||||||
|
"annotationType": 3,
|
||||||
|
"color": [255, 0, 255],
|
||||||
|
"fontSize": 10,
|
||||||
|
"value": "Hello World 你好世界 مرحبا بالعالم",
|
||||||
|
"pageIndex": 0,
|
||||||
|
"rect": [67.5, 443, 222, 456.5],
|
||||||
|
"rotation": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "issue12233-arabic-print",
|
||||||
|
"file": "pdfs/issue12233.pdf",
|
||||||
|
"md5": "6099fc695fe018ce444752929d86f9c8",
|
||||||
|
"link": true,
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"print": true,
|
||||||
|
"annotationStorage": {
|
||||||
|
"23R": {
|
||||||
|
"value": "مرحبا بالعالم"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -56,6 +56,9 @@ describe("annotation", function () {
|
|||||||
acroForm: new Dict(),
|
acroForm: new Dict(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
this.evaluatorOptions = {
|
||||||
|
isOffscreenCanvasSupported: false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure(obj, prop, args) {
|
ensure(obj, prop, args) {
|
||||||
@ -1611,11 +1614,12 @@ describe("annotation", function () {
|
|||||||
const appearance = await annotation._getAppearance(
|
const appearance = await annotation._getAppearance(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
|
RenderingIntentFlag.PRINT,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 0 Tm" +
|
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 0 Tm" +
|
||||||
" 2 3.04 Td (test\\\\print) Tj ET Q EMC"
|
" 2 3.07 Td (test\\\\print) Tj ET Q EMC"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1645,13 +1649,14 @@ describe("annotation", function () {
|
|||||||
const appearance = await annotation._getAppearance(
|
const appearance = await annotation._getAppearance(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
|
RenderingIntentFlag.PRINT,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
const utf16String =
|
const utf16String =
|
||||||
"\x30\x53\x30\x93\x30\x6b\x30\x61\x30\x6f\x4e\x16\x75\x4c\x30\x6e";
|
"\x30\x53\x30\x93\x30\x6b\x30\x61\x30\x6f\x4e\x16\x75\x4c\x30\x6e";
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
"/Tx BMC q BT /Goth 5 Tf 1 0 0 1 0 0 Tm" +
|
"/Tx BMC q BT /Goth 5 Tf 1 0 0 1 0 0 Tm" +
|
||||||
` 2 2 Td (${utf16String}) Tj ET Q EMC`
|
` 2 3.07 Td (${utf16String}) Tj ET Q EMC`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1728,11 +1733,12 @@ describe("annotation", function () {
|
|||||||
const appearance = await annotation._getAppearance(
|
const appearance = await annotation._getAppearance(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
|
RenderingIntentFlag.PRINT,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
"/Tx BMC q BT /Helv 5.92 Tf 0 g 1 0 0 1 0 0 Tm" +
|
"/Tx BMC q BT /Helv 5.92 Tf 0 g 1 0 0 1 0 0 Tm" +
|
||||||
" 2 3.23 Td (test \\(print\\)) Tj ET Q EMC"
|
" 2 3.07 Td (test \\(print\\)) Tj ET Q EMC"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1762,13 +1768,14 @@ describe("annotation", function () {
|
|||||||
const appearance = await annotation._getAppearance(
|
const appearance = await annotation._getAppearance(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
|
RenderingIntentFlag.PRINT,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
const utf16String =
|
const utf16String =
|
||||||
"\x30\x53\x30\x93\x30\x6b\x30\x61\x30\x6f\x4e\x16\x75\x4c\x30\x6e";
|
"\x30\x53\x30\x93\x30\x6b\x30\x61\x30\x6f\x4e\x16\x75\x4c\x30\x6e";
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
"/Tx BMC q BT /Goth 3.5 Tf 0 g 1 0 0 1 0 0 Tm" +
|
"/Tx BMC q BT /Goth 5.92 Tf 0 g 1 0 0 1 0 0 Tm" +
|
||||||
` 2 2 Td (${utf16String}) Tj ET Q EMC`
|
` 2 3.07 Td (${utf16String}) Tj ET Q EMC`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1795,6 +1802,7 @@ describe("annotation", function () {
|
|||||||
const appearance = await annotation._getAppearance(
|
const appearance = await annotation._getAppearance(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
|
RenderingIntentFlag.PRINT,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
expect(appearance).toEqual(null);
|
expect(appearance).toEqual(null);
|
||||||
@ -1827,17 +1835,18 @@ describe("annotation", function () {
|
|||||||
const appearance = await annotation._getAppearance(
|
const appearance = await annotation._getAppearance(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
|
RenderingIntentFlag.PRINT,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 10 Tm " +
|
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 10 Tm " +
|
||||||
"2 -5 Td (a aa aaa ) Tj\n" +
|
"2 -6.93 Td (a aa aaa ) Tj\n" +
|
||||||
"0 -5 Td (aaaa aaaaa ) Tj\n" +
|
"0 -8 Td (aaaa aaaaa ) Tj\n" +
|
||||||
"0 -5 Td (aaaaaa ) Tj\n" +
|
"0 -8 Td (aaaaaa ) Tj\n" +
|
||||||
"0 -5 Td (pneumonoultr) Tj\n" +
|
"0 -8 Td (pneumonoultr) Tj\n" +
|
||||||
"0 -5 Td (amicroscopi) Tj\n" +
|
"0 -8 Td (amicroscopi) Tj\n" +
|
||||||
"0 -5 Td (csilicovolca) Tj\n" +
|
"0 -8 Td (csilicovolca) Tj\n" +
|
||||||
"0 -5 Td (noconiosis) Tj ET Q EMC"
|
"0 -8 Td (noconiosis) Tj ET Q EMC"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1868,12 +1877,13 @@ describe("annotation", function () {
|
|||||||
const appearance = await annotation._getAppearance(
|
const appearance = await annotation._getAppearance(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
|
RenderingIntentFlag.PRINT,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
"/Tx BMC q BT /Goth 5 Tf 1 0 0 1 0 10 Tm " +
|
"/Tx BMC q BT /Goth 5 Tf 1 0 0 1 0 10 Tm " +
|
||||||
"2 -5 Td (\x30\x53\x30\x93\x30\x6b\x30\x61\x30\x6f) Tj\n" +
|
"2 -6.93 Td (\x30\x53\x30\x93\x30\x6b\x30\x61\x30\x6f) Tj\n" +
|
||||||
"0 -5 Td (\x4e\x16\x75\x4c\x30\x6e) Tj ET Q EMC"
|
"0 -8 Td (\x4e\x16\x75\x4c\x30\x6e) Tj ET Q EMC"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1890,25 +1900,25 @@ describe("annotation", function () {
|
|||||||
partialEvaluator.xref = xref;
|
partialEvaluator.xref = xref;
|
||||||
const expectedAppearance =
|
const expectedAppearance =
|
||||||
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 10 Tm " +
|
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 10 Tm " +
|
||||||
"2 -5 Td " +
|
"2 -6.93 Td " +
|
||||||
"(Lorem ipsum dolor sit amet, consectetur adipiscing elit.) Tj\n" +
|
"(Lorem ipsum dolor sit amet, consectetur adipiscing elit.) Tj\n" +
|
||||||
"0 -5 Td " +
|
"0 -8 Td " +
|
||||||
"(Aliquam vitae felis ac lectus bibendum ultricies quis non) Tj\n" +
|
"(Aliquam vitae felis ac lectus bibendum ultricies quis non) Tj\n" +
|
||||||
"0 -5 Td " +
|
"0 -8 Td " +
|
||||||
"( diam.) Tj\n" +
|
"( diam.) Tj\n" +
|
||||||
"0 -5 Td " +
|
"0 -8 Td " +
|
||||||
"(Morbi id porttitor quam, a iaculis dui.) Tj\n" +
|
"(Morbi id porttitor quam, a iaculis dui.) Tj\n" +
|
||||||
"0 -5 Td " +
|
"0 -8 Td " +
|
||||||
"(Pellentesque habitant morbi tristique senectus et netus ) Tj\n" +
|
"(Pellentesque habitant morbi tristique senectus et netus ) Tj\n" +
|
||||||
"0 -5 Td " +
|
"0 -8 Td " +
|
||||||
"(et malesuada fames ac turpis egestas.) Tj\n" +
|
"(et malesuada fames ac turpis egestas.) Tj\n" +
|
||||||
"0 -5 Td () Tj\n" +
|
"0 -8 Td () Tj\n" +
|
||||||
"0 -5 Td () Tj\n" +
|
"0 -8 Td () Tj\n" +
|
||||||
"0 -5 Td " +
|
"0 -8 Td " +
|
||||||
"(Nulla consectetur, ligula in tincidunt placerat, velit ) Tj\n" +
|
"(Nulla consectetur, ligula in tincidunt placerat, velit ) Tj\n" +
|
||||||
"0 -5 Td " +
|
"0 -8 Td " +
|
||||||
"(augue consectetur orci, sed mattis libero nunc ut massa.) Tj\n" +
|
"(augue consectetur orci, sed mattis libero nunc ut massa.) Tj\n" +
|
||||||
"0 -5 Td " +
|
"0 -8 Td " +
|
||||||
"(Etiam facilisis tempus interdum.) Tj ET Q EMC";
|
"(Etiam facilisis tempus interdum.) Tj ET Q EMC";
|
||||||
|
|
||||||
const annotation = await AnnotationFactory.create(
|
const annotation = await AnnotationFactory.create(
|
||||||
@ -1933,8 +1943,10 @@ describe("annotation", function () {
|
|||||||
const appearance = await annotation._getAppearance(
|
const appearance = await annotation._getAppearance(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
|
RenderingIntentFlag.PRINT,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(appearance).toEqual(expectedAppearance);
|
expect(appearance).toEqual(expectedAppearance);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1962,10 +1974,11 @@ describe("annotation", function () {
|
|||||||
const appearance = await annotation._getAppearance(
|
const appearance = await annotation._getAppearance(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
|
RenderingIntentFlag.PRINT,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 2 3.035 Tm" +
|
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 2 3.07 Tm" +
|
||||||
" (a) Tj 8 0 Td (a) Tj 8 0 Td (\\() Tj" +
|
" (a) Tj 8 0 Td (a) Tj 8 0 Td (\\() Tj" +
|
||||||
" 8 0 Td (a) Tj 8 0 Td (a) Tj" +
|
" 8 0 Td (a) Tj 8 0 Td (a) Tj" +
|
||||||
" 8 0 Td (\\)) Tj 8 0 Td (a) Tj" +
|
" 8 0 Td (\\)) Tj 8 0 Td (a) Tj" +
|
||||||
@ -2002,10 +2015,11 @@ describe("annotation", function () {
|
|||||||
const appearance = await annotation._getAppearance(
|
const appearance = await annotation._getAppearance(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
|
RenderingIntentFlag.PRINT,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
"/Tx BMC q BT /Goth 5 Tf 1 0 0 1 2 2 Tm" +
|
"/Tx BMC q BT /Goth 5 Tf 1 0 0 1 2 3.07 Tm" +
|
||||||
" (\x30\x53) Tj 8 0 Td (\x30\x93) Tj 8 0 Td (\x30\x6b) Tj" +
|
" (\x30\x53) Tj 8 0 Td (\x30\x93) Tj 8 0 Td (\x30\x6b) Tj" +
|
||||||
" 8 0 Td (\x30\x61) Tj 8 0 Td (\x30\x6f) Tj" +
|
" 8 0 Td (\x30\x61) Tj 8 0 Td (\x30\x6f) Tj" +
|
||||||
" 8 0 Td (\x4e\x16) Tj 8 0 Td (\x75\x4c) Tj" +
|
" 8 0 Td (\x4e\x16) Tj 8 0 Td (\x75\x4c) Tj" +
|
||||||
@ -2051,7 +2065,7 @@ describe("annotation", function () {
|
|||||||
expect(newData.data).toEqual(
|
expect(newData.data).toEqual(
|
||||||
"2 0 obj\n<< /Length 74 /Subtype /Form /Resources " +
|
"2 0 obj\n<< /Length 74 /Subtype /Form /Resources " +
|
||||||
"<< /Font << /Helv 314 0 R>>>> /BBox [0 0 32 10]>> stream\n" +
|
"<< /Font << /Helv 314 0 R>>>> /BBox [0 0 32 10]>> stream\n" +
|
||||||
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 0 Tm 2 3.04 Td (hello world) Tj " +
|
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 0 Tm 2 3.07 Td (hello world) Tj " +
|
||||||
"ET Q EMC\nendstream\nendobj\n"
|
"ET Q EMC\nendstream\nendobj\n"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -2092,12 +2106,12 @@ describe("annotation", function () {
|
|||||||
"123 0 obj\n" +
|
"123 0 obj\n" +
|
||||||
"<< /Type /Annot /Subtype /Widget /FT /Tx /DA (/Helv 5 Tf) /DR " +
|
"<< /Type /Annot /Subtype /Widget /FT /Tx /DA (/Helv 5 Tf) /DR " +
|
||||||
"<< /Font << /Helv 314 0 R>>>> /Rect [0 0 32 10] " +
|
"<< /Font << /Helv 314 0 R>>>> /Rect [0 0 32 10] " +
|
||||||
"/V (hello world) /AP << /N 2 0 R>> /M (date) /MK << /R 90>>>>\nendobj\n"
|
"/V (hello world) /MK << /R 90>> /AP << /N 2 0 R>> /M (date)>>\nendobj\n"
|
||||||
);
|
);
|
||||||
expect(newData.data).toEqual(
|
expect(newData.data).toEqual(
|
||||||
"2 0 obj\n<< /Length 74 /Subtype /Form /Resources " +
|
"2 0 obj\n<< /Length 74 /Subtype /Form /Resources " +
|
||||||
"<< /Font << /Helv 314 0 R>>>> /BBox [0 0 32 10] /Matrix [0 1 -1 0 32 0]>> stream\n" +
|
"<< /Font << /Helv 314 0 R>>>> /BBox [0 0 32 10] /Matrix [0 1 -1 0 32 0]>> stream\n" +
|
||||||
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 0 Tm 2 3.04 Td (hello world) Tj " +
|
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 0 Tm 2 2.94 Td (hello world) Tj " +
|
||||||
"ET Q EMC\nendstream\nendobj\n"
|
"ET Q EMC\nendstream\nendobj\n"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -2226,9 +2240,9 @@ describe("annotation", function () {
|
|||||||
`/V (\xfe\xff${utf16String}) /AP << /N 2 0 R>> /M (date)>>\nendobj\n`
|
`/V (\xfe\xff${utf16String}) /AP << /N 2 0 R>> /M (date)>>\nendobj\n`
|
||||||
);
|
);
|
||||||
expect(newData.data).toEqual(
|
expect(newData.data).toEqual(
|
||||||
"2 0 obj\n<< /Length 76 /Subtype /Form /Resources " +
|
"2 0 obj\n<< /Length 79 /Subtype /Form /Resources " +
|
||||||
"<< /Font << /Helv 314 0 R /Goth 159 0 R>>>> /BBox [0 0 32 10]>> stream\n" +
|
"<< /Font << /Helv 314 0 R /Goth 159 0 R>>>> /BBox [0 0 32 10]>> stream\n" +
|
||||||
`/Tx BMC q BT /Goth 5 Tf 1 0 0 1 0 0 Tm 2 2 Td (${utf16String}) Tj ` +
|
`/Tx BMC q BT /Goth 5 Tf 1 0 0 1 0 0 Tm 2 3.07 Td (${utf16String}) Tj ` +
|
||||||
"ET Q EMC\nendstream\nendobj\n"
|
"ET Q EMC\nendstream\nendobj\n"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -3457,6 +3471,7 @@ describe("annotation", function () {
|
|||||||
const appearance = await annotation._getAppearance(
|
const appearance = await annotation._getAppearance(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
|
RenderingIntentFlag.PRINT,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
@ -3501,6 +3516,7 @@ describe("annotation", function () {
|
|||||||
const appearance = await annotation._getAppearance(
|
const appearance = await annotation._getAppearance(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
|
RenderingIntentFlag.PRINT,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
@ -3549,6 +3565,7 @@ describe("annotation", function () {
|
|||||||
const appearance = await annotation._getAppearance(
|
const appearance = await annotation._getAppearance(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
|
RenderingIntentFlag.PRINT,
|
||||||
annotationStorage
|
annotationStorage
|
||||||
);
|
);
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
@ -3605,7 +3622,7 @@ describe("annotation", function () {
|
|||||||
"<< /Type /Annot /Subtype /Widget /FT /Ch /DA (/Helv 5 Tf) /DR " +
|
"<< /Type /Annot /Subtype /Widget /FT /Ch /DA (/Helv 5 Tf) /DR " +
|
||||||
"<< /Font << /Helv 314 0 R>>>> " +
|
"<< /Font << /Helv 314 0 R>>>> " +
|
||||||
"/Rect [0 0 32 10] /Opt [(A) (B) (C)] /V (C) " +
|
"/Rect [0 0 32 10] /Opt [(A) (B) (C)] /V (C) " +
|
||||||
"/AP << /N 2 0 R>> /M (date) /MK << /R 270>>>>\nendobj\n"
|
"/MK << /R 270>> /AP << /N 2 0 R>> /M (date)>>\nendobj\n"
|
||||||
);
|
);
|
||||||
expect(newData.data).toEqual(
|
expect(newData.data).toEqual(
|
||||||
[
|
[
|
||||||
@ -4052,7 +4069,6 @@ describe("annotation", function () {
|
|||||||
"ET\n" +
|
"ET\n" +
|
||||||
"Q\n" +
|
"Q\n" +
|
||||||
"endstream\n" +
|
"endstream\n" +
|
||||||
"\n" +
|
|
||||||
"endobj\n"
|
"endobj\n"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -4245,7 +4261,6 @@ describe("annotation", function () {
|
|||||||
"922 923 924 925 926 927 c\n" +
|
"922 923 924 925 926 927 c\n" +
|
||||||
"S\n" +
|
"S\n" +
|
||||||
"endstream\n" +
|
"endstream\n" +
|
||||||
"\n" +
|
|
||||||
"endobj\n"
|
"endobj\n"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -4309,7 +4324,6 @@ describe("annotation", function () {
|
|||||||
"922 923 924 925 926 927 c\n" +
|
"922 923 924 925 926 927 c\n" +
|
||||||
"S\n" +
|
"S\n" +
|
||||||
"endstream\n" +
|
"endstream\n" +
|
||||||
"\n" +
|
|
||||||
"endobj\n"
|
"endobj\n"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -66,6 +66,9 @@ describe("document", function () {
|
|||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
|
get evaluatorOptions() {
|
||||||
|
return { isOffscreenCanvasSupported: false };
|
||||||
|
},
|
||||||
};
|
};
|
||||||
const pdfDocument = new PDFDocument(pdfManager, stream);
|
const pdfDocument = new PDFDocument(pdfManager, stream);
|
||||||
pdfDocument.xref = xref;
|
pdfDocument.xref = xref;
|
||||||
|
@ -78,7 +78,8 @@ class XRefMock {
|
|||||||
constructor(array) {
|
constructor(array) {
|
||||||
this._map = Object.create(null);
|
this._map = Object.create(null);
|
||||||
this.stats = new DocStats({ send: () => {} });
|
this.stats = new DocStats({ send: () => {} });
|
||||||
this._newRefNum = null;
|
this._newTemporaryRefNum = null;
|
||||||
|
this._newPersistentRefNum = null;
|
||||||
this.stream = new NullStream();
|
this.stream = new NullStream();
|
||||||
|
|
||||||
for (const key in array) {
|
for (const key in array) {
|
||||||
@ -87,15 +88,24 @@ class XRefMock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getNewRef() {
|
getNewPersistentRef(obj) {
|
||||||
if (this._newRefNum === null) {
|
if (this._newPersistentRefNum === null) {
|
||||||
this._newRefNum = Object.keys(this._map).length || 1;
|
this._newPersistentRefNum = Object.keys(this._map).length || 1;
|
||||||
}
|
}
|
||||||
return Ref.get(this._newRefNum++, 0);
|
const ref = Ref.get(this._newPersistentRefNum++, 0);
|
||||||
|
this._map[ref.toString()] = obj;
|
||||||
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
resetNewRef() {
|
getNewTemporaryRef() {
|
||||||
this.newRef = null;
|
if (this._newTemporaryRefNum === null) {
|
||||||
|
this._newTemporaryRefNum = Object.keys(this._map).length || 1;
|
||||||
|
}
|
||||||
|
return Ref.get(this._newTemporaryRefNum++, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetNewTemporaryRef() {
|
||||||
|
this._newTemporaryRefNum = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(ref) {
|
fetch(ref) {
|
||||||
|
@ -128,7 +128,7 @@ describe("Writer", function () {
|
|||||||
"/E (\\(hello\\\\world\\)) /F [1.23 4.5 6] " +
|
"/E (\\(hello\\\\world\\)) /F [1.23 4.5 6] " +
|
||||||
"/G << /H 123 /I << /Length 8>> stream\n" +
|
"/G << /H 123 /I << /Length 8>> stream\n" +
|
||||||
"a stream\n" +
|
"a stream\n" +
|
||||||
"endstream\n>> /J true /K false " +
|
"endstream>> /J true /K false " +
|
||||||
"/NullArr [null 10] /NullVal null>>";
|
"/NullArr [null 10] /NullVal null>>";
|
||||||
|
|
||||||
expect(buffer.join("")).toEqual(expected);
|
expect(buffer.join("")).toEqual(expected);
|
||||||
@ -194,6 +194,7 @@ describe("Writer", function () {
|
|||||||
"\n" +
|
"\n" +
|
||||||
"789 0 obj\n" +
|
"789 0 obj\n" +
|
||||||
"<< /XFA [(preamble) 123 0 R (datasets) 101112 0 R (postamble) 456 0 R]>>\n" +
|
"<< /XFA [(preamble) 123 0 R (datasets) 101112 0 R (postamble) 456 0 R]>>\n" +
|
||||||
|
"endobj\n" +
|
||||||
"101112 0 obj\n" +
|
"101112 0 obj\n" +
|
||||||
"<< /Type /EmbeddedFile /Length 20>>\n" +
|
"<< /Type /EmbeddedFile /Length 20>>\n" +
|
||||||
"stream\n" +
|
"stream\n" +
|
||||||
@ -202,11 +203,11 @@ describe("Writer", function () {
|
|||||||
"endobj\n" +
|
"endobj\n" +
|
||||||
"131415 0 obj\n" +
|
"131415 0 obj\n" +
|
||||||
"<< /Size 131416 /Prev 314 /Type /XRef /Index [0 1 789 1 101112 1 131415 1] /W [1 1 2] /Length 16>> stream\n" +
|
"<< /Size 131416 /Prev 314 /Type /XRef /Index [0 1 789 1 101112 1 131415 1] /W [1 1 2] /Length 16>> stream\n" +
|
||||||
"\u0000\u0001ÿÿ\u0001\u0001\u0000\u0000\u0001T\u0000\u0000\u0001²\u0000\u0000\n" +
|
"\u0000\u0001ÿÿ\u0001\u0001\u0000\u0000\u0001[\u0000\u0000\u0001¹\u0000\u0000\n" +
|
||||||
"endstream\n" +
|
"endstream\n" +
|
||||||
"endobj\n" +
|
"endobj\n" +
|
||||||
"startxref\n" +
|
"startxref\n" +
|
||||||
"178\n" +
|
"185\n" +
|
||||||
"%%EOF\n";
|
"%%EOF\n";
|
||||||
|
|
||||||
expect(data).toEqual(expected);
|
expect(data).toEqual(expected);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user