Merge pull request #20064 from calixteman/dict_api

Add few methods to the Dict class in order to simplify the code when writing an annotation
This commit is contained in:
calixteman 2025-07-08 22:16:57 +02:00 committed by GitHub
commit 0e2b59e3d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 222 additions and 166 deletions

View File

@ -361,10 +361,10 @@ class AnnotationFactory {
case AnnotationEditorType.FREETEXT:
if (!baseFontRef) {
const baseFont = new Dict(xref);
baseFont.set("BaseFont", Name.get("Helvetica"));
baseFont.set("Type", Name.get("Font"));
baseFont.set("Subtype", Name.get("Type1"));
baseFont.set("Encoding", Name.get("WinAnsiEncoding"));
baseFont.setIfName("BaseFont", "Helvetica");
baseFont.setIfName("Type", "Font");
baseFont.setIfName("Subtype", "Type1");
baseFont.setIfName("Encoding", "WinAnsiEncoding");
baseFontRef = xref.getNewTemporaryRef();
changes.put(baseFontRef, {
data: baseFont,
@ -571,8 +571,8 @@ function getRgbColor(color, defaultColor = new Uint8ClampedArray(3)) {
}
}
function getPdfColorArray(color) {
return Array.from(color, c => c / 255);
function getPdfColorArray(color, defaultValue = null) {
return (color && Array.from(color, c => c / 255)) || defaultValue;
}
function getQuadPoints(dict, rect) {
@ -1741,7 +1741,7 @@ class MarkupAnnotation extends Annotation {
const formDict = new Dict(xref);
const appearanceStreamDict = new Dict(xref);
appearanceStreamDict.set("Subtype", Name.get("Form"));
appearanceStreamDict.setIfName("Subtype", "Form");
const appearanceStream = new StringStream(buffer.join(" "));
appearanceStream.dict = appearanceStreamDict;
@ -1749,14 +1749,10 @@ class MarkupAnnotation extends Annotation {
const gsDict = new Dict(xref);
if (blendMode) {
gsDict.set("BM", Name.get(blendMode));
}
if (typeof strokeAlpha === "number") {
gsDict.set("CA", strokeAlpha);
}
if (typeof fillAlpha === "number") {
gsDict.set("ca", fillAlpha);
gsDict.setIfName("BM", blendMode);
}
gsDict.setIfNumber("CA", strokeAlpha);
gsDict.setIfNumber("ca", fillAlpha);
const stateDict = new Dict(xref);
stateDict.set("GS0", gsDict);
@ -2107,12 +2103,8 @@ class WidgetAnnotation extends Annotation {
if (rotation) {
mk.set("R", rotation);
}
if (this.borderColor) {
mk.set("BC", getPdfColorArray(this.borderColor));
}
if (this.backgroundColor) {
mk.set("BG", getPdfColorArray(this.backgroundColor));
}
mk.setIfArray("BC", getPdfColorArray(this.borderColor));
mk.setIfArray("BG", getPdfColorArray(this.backgroundColor));
return mk.size > 0 ? mk : null;
}
@ -2248,7 +2240,7 @@ class WidgetAnnotation extends Annotation {
const resources = this._getSaveFieldResources(xref);
const appearanceStream = new StringStream(appearance);
const appearanceDict = (appearanceStream.dict = new Dict(xref));
appearanceDict.set("Subtype", Name.get("Form"));
appearanceDict.setIfName("Subtype", "Form");
appearanceDict.set("Resources", resources);
const bbox =
rotation % 180 === 0
@ -3272,8 +3264,8 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
const appearanceStreamDict = new Dict(params.xref);
appearanceStreamDict.set("FormType", 1);
appearanceStreamDict.set("Subtype", Name.get("Form"));
appearanceStreamDict.set("Type", Name.get("XObject"));
appearanceStreamDict.setIfName("Subtype", "Form");
appearanceStreamDict.setIfName("Type", "XObject");
appearanceStreamDict.set("BBox", bbox);
appearanceStreamDict.set("Matrix", [1, 0, 0, 1, 0, 0]);
appearanceStreamDict.set("Length", appearance.length);
@ -3460,10 +3452,10 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
get fallbackFontDict() {
const dict = new Dict();
dict.set("BaseFont", Name.get("ZapfDingbats"));
dict.set("Type", Name.get("FallbackType"));
dict.set("Subtype", Name.get("FallbackType"));
dict.set("Encoding", Name.get("ZapfDingbatsEncoding"));
dict.setIfName("BaseFont", "ZapfDingbats");
dict.setIfName("Type", "FallbackType");
dict.setIfName("Subtype", "FallbackType");
dict.setIfName("Encoding", "ZapfDingbatsEncoding");
return shadow(this, "fallbackFontDict", dict);
}
@ -3958,8 +3950,8 @@ class FreeTextAnnotation extends MarkupAnnotation {
const { color, fontSize, oldAnnotation, rect, rotation, user, value } =
annotation;
const freetext = oldAnnotation || new Dict(xref);
freetext.set("Type", Name.get("Annot"));
freetext.set("Subtype", Name.get("FreeText"));
freetext.setIfNotExists("Type", Name.get("Annot"));
freetext.setIfNotExists("Subtype", Name.get("FreeText"));
if (oldAnnotation) {
freetext.set("M", `D:${getModificationDate()}`);
// TODO: We should try to generate a new RC from the content we've.
@ -3968,27 +3960,19 @@ class FreeTextAnnotation extends MarkupAnnotation {
} else {
freetext.set("CreationDate", `D:${getModificationDate()}`);
}
freetext.set("Rect", rect);
freetext.setIfArray("Rect", rect);
const da = `/Helv ${fontSize} Tf ${getPdfColor(color, /* isFill */ true)}`;
freetext.set("DA", da);
freetext.set("Contents", stringToAsciiOrUTF16BE(value));
freetext.set("F", 4);
freetext.set("Border", [0, 0, 0]);
freetext.set("Rotate", rotation);
if (user) {
freetext.set("T", stringToAsciiOrUTF16BE(user));
}
freetext.setIfDefined("Contents", stringToAsciiOrUTF16BE(value));
freetext.setIfNotExists("F", 4);
freetext.setIfNotExists("Border", [0, 0, 0]);
freetext.setIfNumber("Rotate", rotation);
freetext.setIfDefined("T", stringToAsciiOrUTF16BE(user));
if (apRef || ap) {
const n = new Dict(xref);
freetext.set("AP", n);
if (apRef) {
n.set("N", apRef);
} else {
n.set("N", ap);
}
n.set("N", apRef || ap);
}
return freetext;
@ -3997,6 +3981,9 @@ class FreeTextAnnotation extends MarkupAnnotation {
static async createNewAppearanceStream(annotation, xref, params) {
const { baseFontRef, evaluator, task } = params;
const { color, fontSize, rect, rotation, value } = annotation;
if (!color) {
return null;
}
const resources = new Dict(xref);
const font = new Dict(xref);
@ -4005,10 +3992,10 @@ class FreeTextAnnotation extends MarkupAnnotation {
font.set("Helv", baseFontRef);
} else {
const baseFont = new Dict(xref);
baseFont.set("BaseFont", Name.get("Helvetica"));
baseFont.set("Type", Name.get("Font"));
baseFont.set("Subtype", Name.get("Type1"));
baseFont.set("Encoding", Name.get("WinAnsiEncoding"));
baseFont.setIfName("BaseFont", "Helvetica");
baseFont.setIfName("Type", "Font");
baseFont.setIfName("Subtype", "Type1");
baseFont.setIfName("Encoding", "WinAnsiEncoding");
font.set("Helv", baseFont);
}
resources.set("Font", font);
@ -4110,8 +4097,8 @@ class FreeTextAnnotation extends MarkupAnnotation {
const appearanceStreamDict = new Dict(xref);
appearanceStreamDict.set("FormType", 1);
appearanceStreamDict.set("Subtype", Name.get("Form"));
appearanceStreamDict.set("Type", Name.get("XObject"));
appearanceStreamDict.setIfName("Subtype", "Form");
appearanceStreamDict.setIfName("Type", "XObject");
appearanceStreamDict.set("BBox", rect);
appearanceStreamDict.set("Resources", resources);
appearanceStreamDict.set("Matrix", [1, 0, 0, 1, -rect[0], -rect[1]]);
@ -4142,13 +4129,13 @@ class LineAnnotation extends MarkupAnnotation {
if (!this.appearance) {
// The default stroke color is black.
const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];
const strokeColor = getPdfColorArray(this.color, [0, 0, 0]);
const strokeAlpha = dict.get("CA");
const interiorColor = getRgbColor(dict.getArray("IC"), null);
// The default fill color is transparent. Setting the fill colour is
// necessary if/when we want to add support for non-default line endings.
const fillColor = interiorColor ? getPdfColorArray(interiorColor) : null;
const fillColor = getPdfColorArray(interiorColor);
const fillAlpha = fillColor ? strokeAlpha : null;
const borderWidth = this.borderStyle.width || 1,
@ -4202,12 +4189,12 @@ class SquareAnnotation extends MarkupAnnotation {
if (!this.appearance) {
// The default stroke color is black.
const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];
const strokeColor = getPdfColorArray(this.color, [0, 0, 0]);
const strokeAlpha = dict.get("CA");
const interiorColor = getRgbColor(dict.getArray("IC"), null);
// The default fill color is transparent.
const fillColor = interiorColor ? getPdfColorArray(interiorColor) : null;
const fillColor = getPdfColorArray(interiorColor);
const fillAlpha = fillColor ? strokeAlpha : null;
if (this.borderStyle.width === 0 && !fillColor) {
@ -4249,12 +4236,12 @@ class CircleAnnotation extends MarkupAnnotation {
if (!this.appearance) {
// The default stroke color is black.
const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];
const strokeColor = getPdfColorArray(this.color, [0, 0, 0]);
const strokeAlpha = dict.get("CA");
const interiorColor = getRgbColor(dict.getArray("IC"), null);
// The default fill color is transparent.
const fillColor = interiorColor ? getPdfColorArray(interiorColor) : null;
const fillColor = getPdfColorArray(interiorColor);
const fillAlpha = fillColor ? strokeAlpha : null;
if (this.borderStyle.width === 0 && !fillColor) {
@ -4334,7 +4321,7 @@ class PolylineAnnotation extends MarkupAnnotation {
if (!this.appearance) {
// The default stroke color is black.
const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];
const strokeColor = getPdfColorArray(this.color, [0, 0, 0]);
const strokeAlpha = dict.get("CA");
let fillColor = getRgbColor(dict.getArray("IC"), null);
@ -4453,7 +4440,7 @@ class InkAnnotation extends MarkupAnnotation {
if (!this.appearance) {
// The default stroke color is black.
const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];
const strokeColor = getPdfColorArray(this.color, [0, 0, 0]);
const strokeAlpha = dict.get("CA");
const borderWidth = this.borderStyle.width || 1,
@ -4514,44 +4501,40 @@ class InkAnnotation extends MarkupAnnotation {
user,
} = annotation;
const ink = oldAnnotation || new Dict(xref);
ink.set("Type", Name.get("Annot"));
ink.set("Subtype", Name.get("Ink"));
ink.setIfNotExists("Type", Name.get("Annot"));
ink.setIfNotExists("Subtype", Name.get("Ink"));
ink.set(oldAnnotation ? "M" : "CreationDate", `D:${getModificationDate()}`);
ink.set("Rect", rect);
ink.set("InkList", outlines?.points || paths.points);
ink.set("F", 4);
ink.set("Rotate", rotation);
if (user) {
ink.set("T", stringToAsciiOrUTF16BE(user));
}
ink.setIfArray("Rect", rect);
ink.setIfArray("InkList", outlines?.points || paths?.points);
ink.setIfNotExists("F", 4);
ink.setIfNumber("Rotate", rotation);
ink.setIfDefined("T", stringToAsciiOrUTF16BE(user));
if (outlines) {
// Free highlight.
// There's nothing about this in the spec, but it's used when highlighting
// in Edge's viewer. Acrobat takes into account this parameter to indicate
// that the Ink is used for highlighting.
ink.set("IT", Name.get("InkHighlight"));
ink.setIfName("IT", "InkHighlight");
}
// Line thickness.
const bs = new Dict(xref);
ink.set("BS", bs);
bs.set("W", thickness);
if (thickness > 0) {
const bs = new Dict(xref);
ink.set("BS", bs);
bs.set("W", thickness);
}
// Color.
ink.set("C", getPdfColorArray(color));
ink.setIfArray("C", getPdfColorArray(color));
// Opacity.
ink.set("CA", opacity);
ink.setIfNumber("CA", opacity);
const n = new Dict(xref);
ink.set("AP", n);
if (apRef) {
n.set("N", apRef);
} else {
n.set("N", ap);
if (ap || apRef) {
const n = new Dict(xref);
ink.set("AP", n);
n.set("N", apRef || ap);
}
return ink;
@ -4566,6 +4549,9 @@ class InkAnnotation extends MarkupAnnotation {
);
}
const { color, rect, paths, thickness, opacity } = annotation;
if (!color) {
return null;
}
const appearanceBuffer = [
`${thickness} w 1 J 1 j`,
@ -4606,8 +4592,8 @@ class InkAnnotation extends MarkupAnnotation {
const appearanceStreamDict = new Dict(xref);
appearanceStreamDict.set("FormType", 1);
appearanceStreamDict.set("Subtype", Name.get("Form"));
appearanceStreamDict.set("Type", Name.get("XObject"));
appearanceStreamDict.setIfName("Subtype", "Form");
appearanceStreamDict.setIfName("Type", "XObject");
appearanceStreamDict.set("BBox", rect);
appearanceStreamDict.set("Length", appearance.length);
@ -4616,7 +4602,7 @@ class InkAnnotation extends MarkupAnnotation {
const extGState = new Dict(xref);
const r0 = new Dict(xref);
r0.set("CA", opacity);
r0.set("Type", Name.get("ExtGState"));
r0.setIfName("Type", "ExtGState");
extGState.set("R0", r0);
resources.set("ExtGState", extGState);
appearanceStreamDict.set("Resources", resources);
@ -4635,6 +4621,9 @@ class InkAnnotation extends MarkupAnnotation {
outlines: { outline },
opacity,
} = annotation;
if (!color) {
return null;
}
const appearanceBuffer = [
`${getPdfColor(color, /* isFill */ true)}`,
"/R0 gs",
@ -4662,8 +4651,8 @@ class InkAnnotation extends MarkupAnnotation {
const appearanceStreamDict = new Dict(xref);
appearanceStreamDict.set("FormType", 1);
appearanceStreamDict.set("Subtype", Name.get("Form"));
appearanceStreamDict.set("Type", Name.get("XObject"));
appearanceStreamDict.setIfName("Subtype", "Form");
appearanceStreamDict.setIfName("Type", "XObject");
appearanceStreamDict.set("BBox", rect);
appearanceStreamDict.set("Length", appearance.length);
@ -4673,11 +4662,11 @@ class InkAnnotation extends MarkupAnnotation {
appearanceStreamDict.set("Resources", resources);
const r0 = new Dict(xref);
extGState.set("R0", r0);
r0.set("BM", Name.get("Multiply"));
r0.setIfName("BM", "Multiply");
if (opacity !== 1) {
r0.set("ca", opacity);
r0.set("Type", Name.get("ExtGState"));
r0.setIfName("Type", "ExtGState");
}
const ap = new StringStream(appearance);
@ -4711,7 +4700,7 @@ class HighlightAnnotation extends MarkupAnnotation {
warn("HighlightAnnotation - ignoring built-in appearance stream.");
}
// Default color is yellow in Acrobat Reader
const fillColor = this.color ? getPdfColorArray(this.color) : [1, 1, 0];
const fillColor = getPdfColorArray(this.color, [1, 1, 0]);
const fillAlpha = dict.get("CA");
this._setDefaultAppearance({
@ -4743,29 +4732,19 @@ class HighlightAnnotation extends MarkupAnnotation {
static createNewDict(annotation, xref, { apRef, ap }) {
const { color, oldAnnotation, opacity, rect, rotation, user, quadPoints } =
annotation;
const date = `D:${getModificationDate()}`;
const highlight = oldAnnotation || new Dict(xref);
highlight.set("Type", Name.get("Annot"));
highlight.set("Subtype", Name.get("Highlight"));
highlight.set(
oldAnnotation ? "M" : "CreationDate",
`D:${getModificationDate()}`
);
highlight.set("CreationDate", `D:${getModificationDate()}`);
highlight.set("Rect", rect);
highlight.set("F", 4);
highlight.set("Border", [0, 0, 0]);
highlight.set("Rotate", rotation);
highlight.set("QuadPoints", quadPoints);
// Color.
highlight.set("C", getPdfColorArray(color));
// Opacity.
highlight.set("CA", opacity);
if (user) {
highlight.set("T", stringToAsciiOrUTF16BE(user));
}
highlight.setIfNotExists("Type", Name.get("Annot"));
highlight.setIfNotExists("Subtype", Name.get("Highlight"));
highlight.set(oldAnnotation ? "M" : "CreationDate", date);
highlight.setIfArray("Rect", rect);
highlight.setIfNotExists("F", 4);
highlight.setIfNotExists("Border", [0, 0, 0]);
highlight.setIfNumber("Rotate", rotation);
highlight.setIfArray("QuadPoints", quadPoints);
highlight.setIfArray("C", getPdfColorArray(color));
highlight.setIfNumber("CA", opacity);
highlight.setIfDefined("T", stringToAsciiOrUTF16BE(user));
if (apRef || ap) {
const n = new Dict(xref);
@ -4778,6 +4757,9 @@ class HighlightAnnotation extends MarkupAnnotation {
static async createNewAppearanceStream(annotation, xref, params) {
const { color, rect, outlines, opacity } = annotation;
if (!color) {
return null;
}
const appearanceBuffer = [
`${getPdfColor(color, /* isFill */ true)}`,
@ -4803,8 +4785,8 @@ class HighlightAnnotation extends MarkupAnnotation {
const appearanceStreamDict = new Dict(xref);
appearanceStreamDict.set("FormType", 1);
appearanceStreamDict.set("Subtype", Name.get("Form"));
appearanceStreamDict.set("Type", Name.get("XObject"));
appearanceStreamDict.setIfName("Subtype", "Form");
appearanceStreamDict.setIfName("Type", "XObject");
appearanceStreamDict.set("BBox", rect);
appearanceStreamDict.set("Length", appearance.length);
@ -4814,11 +4796,11 @@ class HighlightAnnotation extends MarkupAnnotation {
appearanceStreamDict.set("Resources", resources);
const r0 = new Dict(xref);
extGState.set("R0", r0);
r0.set("BM", Name.get("Multiply"));
r0.setIfName("BM", "Multiply");
if (opacity !== 1) {
r0.set("ca", opacity);
r0.set("Type", Name.get("ExtGState"));
r0.setIfName("Type", "ExtGState");
}
const ap = new StringStream(appearance);
@ -4839,9 +4821,7 @@ class UnderlineAnnotation extends MarkupAnnotation {
if (quadPoints) {
if (!this.appearance) {
// Default color is black
const strokeColor = this.color
? getPdfColorArray(this.color)
: [0, 0, 0];
const strokeColor = getPdfColorArray(this.color, [0, 0, 0]);
const strokeAlpha = dict.get("CA");
// The values 0.571 and 1.3 below corresponds to what Acrobat is doing.
@ -4881,9 +4861,7 @@ class SquigglyAnnotation extends MarkupAnnotation {
if (quadPoints) {
if (!this.appearance) {
// Default color is black
const strokeColor = this.color
? getPdfColorArray(this.color)
: [0, 0, 0];
const strokeColor = getPdfColorArray(this.color, [0, 0, 0]);
const strokeAlpha = dict.get("CA");
this._setDefaultAppearance({
@ -4929,9 +4907,7 @@ class StrikeOutAnnotation extends MarkupAnnotation {
if (quadPoints) {
if (!this.appearance) {
// Default color is black
const strokeColor = this.color
? getPdfColorArray(this.color)
: [0, 0, 0];
const strokeColor = getPdfColorArray(this.color, [0, 0, 0]);
const strokeAlpha = dict.get("CA");
this._setDefaultAppearance({
@ -5030,8 +5006,8 @@ class StampAnnotation extends MarkupAnnotation {
image.set("Type", xobjectName);
image.set("Subtype", imageName);
image.set("BitsPerComponent", 8);
image.set("ColorSpace", Name.get("DeviceRGB"));
image.set("Filter", Name.get("DCTDecode"));
image.setIfName("ColorSpace", "DeviceRGB");
image.setIfName("Filter", "DCTDecode");
image.set("BBox", [0, 0, width, height]);
image.set("Width", width);
image.set("Height", height);
@ -5053,7 +5029,7 @@ class StampAnnotation extends MarkupAnnotation {
smask.set("Type", xobjectName);
smask.set("Subtype", imageName);
smask.set("BitsPerComponent", 8);
smask.set("ColorSpace", Name.get("DeviceGray"));
smask.setIfName("ColorSpace", "DeviceGray");
smask.set("Width", width);
smask.set("Height", height);
@ -5071,31 +5047,21 @@ class StampAnnotation extends MarkupAnnotation {
static createNewDict(annotation, xref, { apRef, ap }) {
const { oldAnnotation, rect, rotation, user } = annotation;
const date = `D:${getModificationDate(annotation.date)}`;
const stamp = oldAnnotation || new Dict(xref);
stamp.set("Type", Name.get("Annot"));
stamp.set("Subtype", Name.get("Stamp"));
stamp.set(
oldAnnotation ? "M" : "CreationDate",
`D:${getModificationDate()}`
);
stamp.set("Rect", rect);
stamp.set("F", 4);
stamp.set("Border", [0, 0, 0]);
stamp.set("Rotate", rotation);
if (user) {
stamp.set("T", stringToAsciiOrUTF16BE(user));
}
stamp.setIfNotExists("Type", Name.get("Annot"));
stamp.setIfNotExists("Subtype", Name.get("Stamp"));
stamp.set(oldAnnotation ? "M" : "CreationDate", date);
stamp.setIfArray("Rect", rect);
stamp.setIfNotExists("F", 4);
stamp.setIfNotExists("Border", [0, 0, 0]);
stamp.setIfNumber("Rotate", rotation);
stamp.setIfDefined("T", stringToAsciiOrUTF16BE(user));
if (apRef || ap) {
const n = new Dict(xref);
stamp.set("AP", n);
if (apRef) {
n.set("N", apRef);
} else {
n.set("N", ap);
}
n.set("N", apRef || ap);
}
return stamp;
@ -5103,6 +5069,9 @@ class StampAnnotation extends MarkupAnnotation {
static async #createNewAppearanceStreamForDrawing(annotation, xref) {
const { areContours, color, rect, lines, thickness } = annotation;
if (!color) {
return null;
}
const appearanceBuffer = [
`${thickness} w 1 J 1 j`,
@ -5137,8 +5106,8 @@ class StampAnnotation extends MarkupAnnotation {
const appearanceStreamDict = new Dict(xref);
appearanceStreamDict.set("FormType", 1);
appearanceStreamDict.set("Subtype", Name.get("Form"));
appearanceStreamDict.set("Type", Name.get("XObject"));
appearanceStreamDict.setIfName("Subtype", "Form");
appearanceStreamDict.setIfName("Type", "XObject");
appearanceStreamDict.set("BBox", rect);
appearanceStreamDict.set("Length", appearance.length);
@ -5167,8 +5136,8 @@ class StampAnnotation extends MarkupAnnotation {
const appearanceStreamDict = new Dict(xref);
appearanceStreamDict.set("FormType", 1);
appearanceStreamDict.set("Subtype", Name.get("Form"));
appearanceStreamDict.set("Type", Name.get("XObject"));
appearanceStreamDict.setIfName("Subtype", "Form");
appearanceStreamDict.setIfName("Type", "XObject");
appearanceStreamDict.set("BBox", [0, 0, width, height]);
appearanceStreamDict.set("Resources", resources);

View File

@ -679,12 +679,19 @@ function getNewAnnotationsMap(annotationStorage) {
return newAnnotationsByPage.size > 0 ? newAnnotationsByPage : null;
}
// If the string is null or undefined then it is returned as is.
function stringToAsciiOrUTF16BE(str) {
if (str === null || str === undefined) {
return str;
}
return isAscii(str) ? str : stringToUTF16String(str, /* bigEndian = */ true);
}
function isAscii(str) {
return /^[\x00-\x7F]*$/.test(str);
if (typeof str !== "string") {
return false;
}
return !str || /^[\x00-\x7F]*$/.test(str);
}
function stringToUTF16HexString(str) {

View File

@ -267,11 +267,11 @@ class FakeUnicodeFont {
get fontDescriptorRef() {
if (!FakeUnicodeFont._fontDescriptorRef) {
const fontDescriptor = new Dict(this.xref);
fontDescriptor.set("Type", Name.get("FontDescriptor"));
fontDescriptor.setIfName("Type", "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.setIfName("FontStretch", "Normal");
fontDescriptor.set("FontWeight", 400);
fontDescriptor.set("ItalicAngle", 0);
@ -285,9 +285,9 @@ class FakeUnicodeFont {
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.setIfName("Type", "Font");
descendantFont.setIfName("Subtype", "CIDFontType0");
descendantFont.setIfName("CIDToGIDMap", "Identity");
descendantFont.set("FirstChar", this.firstChar);
descendantFont.set("LastChar", this.lastChar);
descendantFont.set("FontDescriptor", this.fontDescriptorRef);
@ -330,11 +330,11 @@ class FakeUnicodeFont {
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.setIfName("Type", "Font");
baseFont.setIfName("Subtype", "Type0");
baseFont.setIfName("Encoding", "Identity-H");
baseFont.set("DescendantFonts", [this.descendantFontRef]);
baseFont.set("ToUnicode", Name.get("Identity-H"));
baseFont.setIfName("ToUnicode", "Identity-H");
return this.xref.getNewPersistentRef(baseFont);
}
@ -463,7 +463,7 @@ class FakeUnicodeFont {
const r0 = new Dict(this.xref);
r0.set("ca", strokeAlpha);
r0.set("CA", strokeAlpha);
r0.set("Type", Name.get("ExtGState"));
r0.setIfName("Type", "ExtGState");
extGState.set("R0", r0);
resources.set("ExtGState", extGState);
}
@ -476,8 +476,8 @@ class FakeUnicodeFont {
const appearance = buffer.join("\n");
const appearanceStreamDict = new Dict(this.xref);
appearanceStreamDict.set("Subtype", Name.get("Form"));
appearanceStreamDict.set("Type", Name.get("XObject"));
appearanceStreamDict.setIfName("Subtype", "Form");
appearanceStreamDict.setIfName("Type", "XObject");
appearanceStreamDict.set("BBox", [0, 0, w, h]);
appearanceStreamDict.set("Length", appearance.length);
appearanceStreamDict.set("Resources", resources);

View File

@ -199,6 +199,38 @@ class Dict {
this._map.set(key, value);
}
setIfNotExists(key, value) {
if (!this.has(key)) {
this.set(key, value);
}
}
setIfNumber(key, value) {
if (typeof value === "number") {
this.set(key, value);
}
}
setIfArray(key, value) {
if (Array.isArray(value) || ArrayBuffer.isView(value)) {
this.set(key, value);
}
}
setIfDefined(key, value) {
if (value !== undefined && value !== null) {
this.set(key, value);
}
}
setIfName(key, value) {
if (typeof value === "string") {
this.set(key, Name.get(value));
} else if (value instanceof Name) {
this.set(key, value);
}
}
has(key) {
return this._map.has(key);
}

View File

@ -271,7 +271,7 @@ function updateXFA({ xfaData, xfaDatasetsRef, changes, xref }) {
}
const xfaDataStream = new StringStream(xfaData);
xfaDataStream.dict = new Dict(xref);
xfaDataStream.dict.set("Type", Name.get("EmbeddedFile"));
xfaDataStream.dict.setIfName("Type", "EmbeddedFile");
changes.put(xfaDatasetsRef, {
data: xfaDataStream,
@ -382,7 +382,7 @@ function getTrailerDict(xrefInfo, changes, useXrefStream) {
if (useXrefStream) {
changes.put(refForXrefTable, { data: "" });
newXref.set("Size", refForXrefTable.num + 1);
newXref.set("Type", Name.get("XRef"));
newXref.setIfName("Type", "XRef");
} else {
newXref.set("Size", refForXrefTable.num);
}

View File

@ -431,6 +431,10 @@ describe("core_utils", function () {
expect(isAscii("hello world in Japanese is こんにちは世界の")).toEqual(
false
);
expect(isAscii("")).toEqual(true);
expect(isAscii(123)).toEqual(false);
expect(isAscii(null)).toEqual(false);
expect(isAscii(undefined)).toEqual(false);
});
});

View File

@ -380,6 +380,50 @@ describe("primitives", function () {
"Global font three",
]);
});
it("should set the values if they're as expected", function () {
const dict = new Dict();
dict.set("key", "value");
dict.setIfNotExists("key", "new value");
expect(dict.get("key")).toEqual("value");
dict.setIfNotExists("key1", "value");
expect(dict.get("key1")).toEqual("value");
dict.setIfNumber("a", 123);
expect(dict.get("a")).toEqual(123);
dict.setIfNumber("b", "not a number");
expect(dict.has("b")).toBeFalse();
dict.setIfArray("c", [1, 2, 3]);
expect(dict.get("c")).toEqual([1, 2, 3]);
dict.setIfArray("d", new Uint8Array([4, 5, 6]));
expect(dict.get("d")).toEqual(new Uint8Array([4, 5, 6]));
dict.setIfArray("e", "not an array");
expect(dict.has("e")).toBeFalse();
dict.setIfDefined("f", "defined");
expect(dict.get("f")).toEqual("defined");
dict.setIfDefined("g", undefined);
expect(dict.has("g")).toBeFalse();
dict.setIfDefined("h", null);
expect(dict.has("h")).toBeFalse();
dict.setIfName("i", Name.get("name"));
expect(dict.get("i")).toEqual(Name.get("name"));
dict.setIfName("j", "name");
expect(dict.get("j")).toEqual(Name.get("name"));
dict.setIfName("k", 1234);
expect(dict.has("k")).toBeFalse();
});
});
describe("Ref", function () {