Skip to content

Commit 5e80a3f

Browse files
authored
Fix font name escaping and convert js string to pdf name object (#3149)
1 parent 286189e commit 5e80a3f

File tree

4 files changed

+108
-19
lines changed

4 files changed

+108
-19
lines changed

src/jspdf.js

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { RGBColor } from "./libs/rgbcolor.js";
88
import { btoa } from "./libs/AtobBtoa.js";
99
import { console } from "./libs/console.js";
1010
import { PDFSecurity } from "./libs/pdfsecurity.js";
11-
11+
import { toPDFName } from "./libs/pdfname.js";
1212
/**
1313
* jsPDF's Internal PubSub Implementation.
1414
* Backward compatible rewritten on 2014 by
@@ -1997,26 +1997,18 @@ function jsPDF(options) {
19971997
});
19981998

19991999
var putFont = function(font) {
2000-
var pdfEscapeWithNeededParanthesis = function(text, flags) {
2001-
var addParanthesis = text.indexOf(" ") !== -1; // no space in string
2002-
return addParanthesis
2003-
? "(" + pdfEscape(text, flags) + ")"
2004-
: pdfEscape(text, flags);
2005-
};
2006-
20072000
events.publish("putFont", {
20082001
font: font,
20092002
out: out,
20102003
newObject: newObject,
20112004
putStream: putStream,
2012-
pdfEscapeWithNeededParanthesis: pdfEscapeWithNeededParanthesis
20132005
});
20142006

20152007
if (font.isAlreadyPutted !== true) {
20162008
font.objectNumber = newObject();
20172009
out("<<");
20182010
out("/Type /Font");
2019-
out("/BaseFont /" + pdfEscapeWithNeededParanthesis(font.postScriptName));
2011+
out("/BaseFont /" + toPDFName(font.postScriptName));
20202012
out("/Subtype /Type1");
20212013
if (typeof font.encoding === "string") {
20222014
out("/Encoding /" + font.encoding);

src/libs/pdfname.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Convert string to `PDF Name Object`.
3+
* Detail: PDF Reference 1.3 - Chapter 3.2.4 Name Object
4+
* @param str
5+
*/
6+
function toPDFName(str) {
7+
// eslint-disable-next-line no-control-regex
8+
if(/[^\u0000-\u00ff]/.test(str)){ // non ascii string
9+
throw new Error('Invalid PDF Name Object: ' + str + ', Only accept ASCII characters.');
10+
}
11+
var result = "",
12+
strLength = str.length;
13+
for (var i = 0; i < strLength; i++) {
14+
var charCode = str.charCodeAt(i);
15+
if (
16+
charCode < 0x21 ||
17+
charCode === 0x23 /* # */ ||
18+
charCode === 0x25 /* % */ ||
19+
charCode === 0x28 /* ( */ ||
20+
charCode === 0x29 /* ) */ ||
21+
charCode === 0x2f /* / */ ||
22+
charCode === 0x3c /* < */ ||
23+
charCode === 0x3e /* > */ ||
24+
charCode === 0x5b /* [ */ ||
25+
charCode === 0x5d /* ] */ ||
26+
charCode === 0x7b /* { */ ||
27+
charCode === 0x7d /* } */ ||
28+
charCode > 0x7e
29+
) {
30+
// Char CharCode hexStr paddingHexStr Result
31+
// "\t" 9 9 09 #09
32+
// " " 32 20 20 #20
33+
// "©" 169 a9 a9 #a9
34+
var hexStr = charCode.toString(16),
35+
paddingHexStr = ("0" + hexStr).slice(-2);
36+
37+
result += "#" + paddingHexStr;
38+
} else {
39+
// Other ASCII printable characters between 0x21 <= X <= 0x7e
40+
result += str[i];
41+
}
42+
}
43+
return result;
44+
}
45+
46+
export { toPDFName };

src/modules/utf8.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { jsPDF } from "../jspdf.js";
2+
import { toPDFName } from "../libs/pdfname.js";
23

34
/**
45
* @name utf8
@@ -86,7 +87,6 @@ import { jsPDF } from "../jspdf.js";
8687
var out = options.out;
8788
var newObject = options.newObject;
8889
var putStream = options.putStream;
89-
var pdfEscapeWithNeededParanthesis = options.pdfEscapeWithNeededParanthesis;
9090

9191
if (
9292
font.metadata instanceof jsPDF.API.TTFFont &&
@@ -112,7 +112,7 @@ import { jsPDF } from "../jspdf.js";
112112
var fontDescriptor = newObject();
113113
out("<<");
114114
out("/Type /FontDescriptor");
115-
out("/FontName /" + pdfEscapeWithNeededParanthesis(font.fontName));
115+
out("/FontName /" + toPDFName(font.fontName));
116116
out("/FontFile2 " + fontTable + " 0 R");
117117
out("/FontBBox " + jsPDF.API.PDFObject.convert(font.metadata.bbox));
118118
out("/Flags " + font.metadata.flags);
@@ -127,7 +127,7 @@ import { jsPDF } from "../jspdf.js";
127127
var DescendantFont = newObject();
128128
out("<<");
129129
out("/Type /Font");
130-
out("/BaseFont /" + pdfEscapeWithNeededParanthesis(font.fontName));
130+
out("/BaseFont /" + toPDFName(font.fontName));
131131
out("/FontDescriptor " + fontDescriptor + " 0 R");
132132
out("/W " + jsPDF.API.PDFObject.convert(widths));
133133
out("/CIDToGIDMap /Identity");
@@ -147,7 +147,7 @@ import { jsPDF } from "../jspdf.js";
147147
out("/Type /Font");
148148
out("/Subtype /Type0");
149149
out("/ToUnicode " + cmap + " 0 R");
150-
out("/BaseFont /" + pdfEscapeWithNeededParanthesis(font.fontName));
150+
out("/BaseFont /" + toPDFName(font.fontName));
151151
out("/Encoding /" + font.encoding);
152152
out("/DescendantFonts [" + DescendantFont + " 0 R]");
153153
out(">>");
@@ -169,7 +169,6 @@ import { jsPDF } from "../jspdf.js";
169169
var out = options.out;
170170
var newObject = options.newObject;
171171
var putStream = options.putStream;
172-
var pdfEscapeWithNeededParanthesis = options.pdfEscapeWithNeededParanthesis;
173172

174173
if (
175174
font.metadata instanceof jsPDF.API.TTFFont &&
@@ -200,7 +199,7 @@ import { jsPDF } from "../jspdf.js";
200199
out("/FontFile2 " + fontTable + " 0 R");
201200
out("/Flags 96");
202201
out("/FontBBox " + jsPDF.API.PDFObject.convert(font.metadata.bbox));
203-
out("/FontName /" + pdfEscapeWithNeededParanthesis(font.fontName));
202+
out("/FontName /" + toPDFName(font.fontName));
204203
out("/ItalicAngle " + font.metadata.italicAngle);
205204
out("/Ascent " + font.metadata.ascender);
206205
out(">>");
@@ -215,7 +214,7 @@ import { jsPDF } from "../jspdf.js";
215214
"<</Subtype/TrueType/Type/Font/ToUnicode " +
216215
cmap +
217216
" 0 R/BaseFont/" +
218-
pdfEscapeWithNeededParanthesis(font.fontName) +
217+
toPDFName(font.fontName) +
219218
"/FontDescriptor " +
220219
fontDescriptor +
221220
" 0 R" +
@@ -281,10 +280,10 @@ import { jsPDF } from "../jspdf.js";
281280
if (Object.prototype.toString.call(text[s]) === '[object Array]') {
282281
cmapConfirm = fonts[key].metadata.cmap.unicode.codeMap[strText[s][0].charCodeAt(0)]; //Make sure the cmap has the corresponding glyph id
283282
} else {
284-
283+
285284
}
286285
//}
287-
286+
288287
} else {
289288
cmapConfirm = fonts[key].metadata.cmap.unicode.codeMap[strText[s].charCodeAt(0)]; //Make sure the cmap has the corresponding glyph id
290289
}*/

test/specs/pdfname.spec.mjs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { toPDFName } from "../../src/libs/pdfname.js";
2+
3+
describe("Lib: pdfname", ()=>{
4+
it("ASCII control characters to pdf name", ()=>{
5+
expect(toPDFName("\t")).toBe("#09");
6+
});
7+
8+
it("ASCII printable characters to pdf name", ()=>{
9+
expect(toPDFName(" ")).toBe("#20");
10+
expect(toPDFName("!")).toBe("!");
11+
expect(toPDFName("#")).toBe("#23");
12+
expect(toPDFName("$")).toBe("$");
13+
expect(toPDFName("%")).toBe("#25");
14+
expect(toPDFName("&")).toBe("&");
15+
expect(toPDFName("'")).toBe("'");
16+
expect(toPDFName("(")).toBe("#28");
17+
expect(toPDFName(")")).toBe("#29");
18+
expect(toPDFName("*")).toBe("*");
19+
expect(toPDFName("+")).toBe("+");
20+
expect(toPDFName(",")).toBe(",");
21+
expect(toPDFName("-")).toBe("-");
22+
expect(toPDFName(".")).toBe(".");
23+
expect(toPDFName("/")).toBe("#2f");
24+
expect(toPDFName("0123456789")).toBe("0123456789");
25+
expect(toPDFName(":")).toBe(":");
26+
expect(toPDFName(";")).toBe(";");
27+
expect(toPDFName("<")).toBe("#3c");
28+
expect(toPDFName("=")).toBe("=");
29+
expect(toPDFName(">")).toBe("#3e");
30+
expect(toPDFName("?")).toBe("?");
31+
expect(toPDFName("@")).toBe("@");
32+
expect(toPDFName("ABCDEFGHIJKLMNOPQRSTUVWXYZ")).toBe("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
33+
expect(toPDFName("[")).toBe("#5b");
34+
expect(toPDFName("\\")).toBe("\\");
35+
expect(toPDFName("]")).toBe("#5d");
36+
expect(toPDFName("^")).toBe("^");
37+
expect(toPDFName("_")).toBe("_");
38+
expect(toPDFName("`")).toBe("`");
39+
expect(toPDFName("abcdefghijklmnopqrstuvwxyz")).toBe("abcdefghijklmnopqrstuvwxyz");
40+
expect(toPDFName("{")).toBe("#7b");
41+
expect(toPDFName("|")).toBe("|");
42+
expect(toPDFName("}")).toBe("#7d");
43+
expect(toPDFName("~")).toBe("~");
44+
expect(toPDFName("\u007f")).toBe("#7f");
45+
});
46+
47+
it("The extended ASCII codes to pdf name", ()=>{
48+
expect(toPDFName("©")).toBe("#a9");
49+
expect(toPDFName("®")).toBe("#ae");
50+
expect(toPDFName("ÿ")).toBe("#ff");
51+
});
52+
});

0 commit comments

Comments
 (0)