Merge pull request #12292 from calixteman/encoding
Fix encoding issues when printing/saving a form with non-ascii characters
This commit is contained in:
commit
5bde4b71f8
@ -23,10 +23,12 @@ import {
|
|||||||
assert,
|
assert,
|
||||||
escapeString,
|
escapeString,
|
||||||
getModificationDate,
|
getModificationDate,
|
||||||
|
isAscii,
|
||||||
isString,
|
isString,
|
||||||
OPS,
|
OPS,
|
||||||
shadow,
|
shadow,
|
||||||
stringToPDFString,
|
stringToPDFString,
|
||||||
|
stringToUTF16BEString,
|
||||||
unreachable,
|
unreachable,
|
||||||
Util,
|
Util,
|
||||||
warn,
|
warn,
|
||||||
@ -1222,7 +1224,7 @@ class WidgetAnnotation extends Annotation {
|
|||||||
appearance = newTransform.encryptString(appearance);
|
appearance = newTransform.encryptString(appearance);
|
||||||
}
|
}
|
||||||
|
|
||||||
dict.set("V", value);
|
dict.set("V", isAscii(value) ? value : stringToUTF16BEString(value));
|
||||||
dict.set("AP", AP);
|
dict.set("AP", AP);
|
||||||
dict.set("M", `D:${getModificationDate()}`);
|
dict.set("M", `D:${getModificationDate()}`);
|
||||||
|
|
||||||
@ -1298,16 +1300,6 @@ class WidgetAnnotation extends Annotation {
|
|||||||
const defaultAppearance = this.data.defaultAppearance;
|
const defaultAppearance = this.data.defaultAppearance;
|
||||||
const alignment = this.data.textAlignment;
|
const alignment = this.data.textAlignment;
|
||||||
|
|
||||||
if (this.data.comb) {
|
|
||||||
return this._getCombAppearance(
|
|
||||||
defaultAppearance,
|
|
||||||
value,
|
|
||||||
totalWidth,
|
|
||||||
hPadding,
|
|
||||||
vPadding
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.data.multiLine) {
|
if (this.data.multiLine) {
|
||||||
return this._getMultilineAppearance(
|
return this._getMultilineAppearance(
|
||||||
defaultAppearance,
|
defaultAppearance,
|
||||||
@ -1322,18 +1314,34 @@ class WidgetAnnotation extends Annotation {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: need to handle chars which are not in the font.
|
||||||
|
const encodedString = font.encodeString(value).join("");
|
||||||
|
|
||||||
|
if (this.data.comb) {
|
||||||
|
return this._getCombAppearance(
|
||||||
|
defaultAppearance,
|
||||||
|
font,
|
||||||
|
encodedString,
|
||||||
|
totalWidth,
|
||||||
|
hPadding,
|
||||||
|
vPadding
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
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 BT " +
|
"/Tx BMC q BT " +
|
||||||
defaultAppearance +
|
defaultAppearance +
|
||||||
` 1 0 0 1 ${hPadding} ${vPadding} Tm (${escapeString(value)}) Tj` +
|
` 1 0 0 1 ${hPadding} ${vPadding} Tm (${escapeString(
|
||||||
|
encodedString
|
||||||
|
)}) Tj` +
|
||||||
" ET Q EMC"
|
" ET Q EMC"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderedText = this._renderText(
|
const renderedText = this._renderText(
|
||||||
value,
|
encodedString,
|
||||||
font,
|
font,
|
||||||
fontSize,
|
fontSize,
|
||||||
totalWidth,
|
totalWidth,
|
||||||
@ -1373,10 +1381,21 @@ class WidgetAnnotation extends Annotation {
|
|||||||
|
|
||||||
_computeFontSize(font, fontName, fontSize, height) {
|
_computeFontSize(font, fontName, fontSize, height) {
|
||||||
if (fontSize === null || fontSize === 0) {
|
if (fontSize === null || fontSize === 0) {
|
||||||
const em = font.charsToGlyphs("M")[0].width / 1000;
|
let capHeight;
|
||||||
// According to https://en.wikipedia.org/wiki/Em_(typography)
|
if (font.capHeight) {
|
||||||
// an average cap height should be 70% of 1em
|
capHeight = font.capHeight;
|
||||||
const capHeight = 0.7 * em;
|
} else {
|
||||||
|
const glyphs = font.charsToGlyphs(font.encodeString("M").join(""));
|
||||||
|
if (glyphs.length === 1 && glyphs[0].width) {
|
||||||
|
const em = glyphs[0].width / 1000;
|
||||||
|
// According to https://en.wikipedia.org/wiki/Em_(typography)
|
||||||
|
// an average cap height should be 70% of 1em
|
||||||
|
capHeight = 0.7 * em;
|
||||||
|
} else {
|
||||||
|
capHeight = 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 1.5 * capHeight * fontSize seems to be a good value for lineHeight
|
// 1.5 * capHeight * fontSize seems to be a good value for lineHeight
|
||||||
fontSize = Math.max(1, Math.floor(height / (1.5 * capHeight)));
|
fontSize = Math.max(1, Math.floor(height / (1.5 * capHeight)));
|
||||||
|
|
||||||
@ -1510,11 +1529,12 @@ class TextWidgetAnnotation extends WidgetAnnotation {
|
|||||||
this.data.maxLen !== null;
|
this.data.maxLen !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getCombAppearance(defaultAppearance, text, width, hPadding, vPadding) {
|
_getCombAppearance(defaultAppearance, font, text, width, hPadding, vPadding) {
|
||||||
const combWidth = (width / this.data.maxLen).toFixed(2);
|
const combWidth = (width / this.data.maxLen).toFixed(2);
|
||||||
const buf = [];
|
const buf = [];
|
||||||
for (const character of text) {
|
const positions = font.getCharPositions(text);
|
||||||
buf.push(`(${escapeString(character)}) Tj`);
|
for (const [start, end] of positions) {
|
||||||
|
buf.push(`(${escapeString(text.substring(start, end))}) Tj`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderedComb = buf.join(` ${combWidth} 0 Td `);
|
const renderedComb = buf.join(` ${combWidth} 0 Td `);
|
||||||
@ -1568,49 +1588,61 @@ class TextWidgetAnnotation extends WidgetAnnotation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_splitLine(line, font, fontSize, width) {
|
_splitLine(line, font, fontSize, width) {
|
||||||
if (line.length <= 1) {
|
// TODO: need to handle chars which are not in the font.
|
||||||
|
line = font.encodeString(line).join("");
|
||||||
|
|
||||||
|
const glyphs = font.charsToGlyphs(line);
|
||||||
|
|
||||||
|
if (glyphs.length <= 1) {
|
||||||
// Nothing to split
|
// Nothing to split
|
||||||
return [line];
|
return [line];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const positions = font.getCharPositions(line);
|
||||||
const scale = fontSize / 1000;
|
const scale = fontSize / 1000;
|
||||||
const whitespace = font.charsToGlyphs(" ")[0].width * scale;
|
|
||||||
const chunks = [];
|
const chunks = [];
|
||||||
|
|
||||||
let lastSpacePos = -1,
|
let lastSpacePosInStringStart = -1,
|
||||||
|
lastSpacePosInStringEnd = -1,
|
||||||
|
lastSpacePos = -1,
|
||||||
startChunk = 0,
|
startChunk = 0,
|
||||||
currentWidth = 0;
|
currentWidth = 0;
|
||||||
|
|
||||||
for (let i = 0, ii = line.length; i < ii; i++) {
|
for (let i = 0, ii = glyphs.length; i < ii; i++) {
|
||||||
const character = line.charAt(i);
|
const [start, end] = positions[i];
|
||||||
if (character === " ") {
|
const glyph = glyphs[i];
|
||||||
if (currentWidth + whitespace > width) {
|
const glyphWidth = glyph.width * scale;
|
||||||
|
if (glyph.unicode === " ") {
|
||||||
|
if (currentWidth + glyphWidth > width) {
|
||||||
// We can break here
|
// We can break here
|
||||||
chunks.push(line.substring(startChunk, i));
|
chunks.push(line.substring(startChunk, start));
|
||||||
startChunk = i;
|
startChunk = start;
|
||||||
currentWidth = whitespace;
|
currentWidth = glyphWidth;
|
||||||
|
lastSpacePosInStringStart = -1;
|
||||||
lastSpacePos = -1;
|
lastSpacePos = -1;
|
||||||
} else {
|
} else {
|
||||||
currentWidth += whitespace;
|
currentWidth += glyphWidth;
|
||||||
|
lastSpacePosInStringStart = start;
|
||||||
|
lastSpacePosInStringEnd = end;
|
||||||
lastSpacePos = i;
|
lastSpacePos = i;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const charWidth = font.charsToGlyphs(character)[0].width * scale;
|
if (currentWidth + glyphWidth > width) {
|
||||||
if (currentWidth + charWidth > width) {
|
|
||||||
// We must break to the last white position (if available)
|
// We must break to the last white position (if available)
|
||||||
if (lastSpacePos !== -1) {
|
if (lastSpacePosInStringStart !== -1) {
|
||||||
chunks.push(line.substring(startChunk, lastSpacePos + 1));
|
chunks.push(line.substring(startChunk, lastSpacePosInStringEnd));
|
||||||
startChunk = i = lastSpacePos + 1;
|
startChunk = lastSpacePosInStringEnd;
|
||||||
lastSpacePos = -1;
|
i = lastSpacePos + 1;
|
||||||
|
lastSpacePosInStringStart = -1;
|
||||||
currentWidth = 0;
|
currentWidth = 0;
|
||||||
} else {
|
} else {
|
||||||
// Just break in the middle of the word
|
// Just break in the middle of the word
|
||||||
chunks.push(line.substring(startChunk, i));
|
chunks.push(line.substring(startChunk, start));
|
||||||
startChunk = i;
|
startChunk = start;
|
||||||
currentWidth = charWidth;
|
currentWidth = glyphWidth;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
currentWidth += charWidth;
|
currentWidth += glyphWidth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,6 +338,22 @@ class CMap {
|
|||||||
out.length = 1;
|
out.length = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCharCodeLength(charCode) {
|
||||||
|
const codespaceRanges = this.codespaceRanges;
|
||||||
|
for (let n = 0, nn = codespaceRanges.length; n < nn; n++) {
|
||||||
|
// Check each codespace range to see if it falls within.
|
||||||
|
const codespaceRange = codespaceRanges[n];
|
||||||
|
for (let k = 0, kk = codespaceRange.length; k < kk; ) {
|
||||||
|
const low = codespaceRange[k++];
|
||||||
|
const high = codespaceRange[k++];
|
||||||
|
if (charCode >= low && charCode <= high) {
|
||||||
|
return n + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
get length() {
|
get length() {
|
||||||
return this._map.length;
|
return this._map.length;
|
||||||
}
|
}
|
||||||
|
@ -590,6 +590,7 @@ var Font = (function FontClosure() {
|
|||||||
this.defaultWidth = properties.defaultWidth;
|
this.defaultWidth = properties.defaultWidth;
|
||||||
this.composite = properties.composite;
|
this.composite = properties.composite;
|
||||||
this.cMap = properties.cMap;
|
this.cMap = properties.cMap;
|
||||||
|
this.capHeight = properties.capHeight / PDF_GLYPH_SPACE_UNITS;
|
||||||
this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS;
|
this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS;
|
||||||
this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS;
|
this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS;
|
||||||
this.fontMatrix = properties.fontMatrix;
|
this.fontMatrix = properties.fontMatrix;
|
||||||
@ -3351,9 +3352,93 @@ var Font = (function FontClosure() {
|
|||||||
return (charsCache[charsCacheKey] = glyphs);
|
return (charsCache[charsCacheKey] = glyphs);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chars can have different sizes (depends on the encoding).
|
||||||
|
* @param {String} a string encoded with font encoding.
|
||||||
|
* @returns {Array<Array<number>>} the positions of each char in the string.
|
||||||
|
*/
|
||||||
|
getCharPositions(chars) {
|
||||||
|
// This function doesn't use a cache because
|
||||||
|
// it's called only when saving or printing.
|
||||||
|
const positions = [];
|
||||||
|
|
||||||
|
if (this.cMap) {
|
||||||
|
const c = Object.create(null);
|
||||||
|
let i = 0;
|
||||||
|
while (i < chars.length) {
|
||||||
|
this.cMap.readCharCode(chars, i, c);
|
||||||
|
const length = c.length;
|
||||||
|
positions.push([i, i + length]);
|
||||||
|
i += length;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = 0, ii = chars.length; i < ii; ++i) {
|
||||||
|
positions.push([i, i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return positions;
|
||||||
|
},
|
||||||
|
|
||||||
get glyphCacheValues() {
|
get glyphCacheValues() {
|
||||||
return Object.values(this.glyphCache);
|
return Object.values(this.glyphCache);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a js string using font encoding.
|
||||||
|
* The resulting array contains an encoded string at even positions
|
||||||
|
* (can be empty) and a non-encoded one at odd positions.
|
||||||
|
* @param {String} a js string.
|
||||||
|
* @returns {Array<String>} an array of encoded strings or non-encoded ones.
|
||||||
|
*/
|
||||||
|
encodeString(str) {
|
||||||
|
const buffers = [];
|
||||||
|
const currentBuf = [];
|
||||||
|
|
||||||
|
// buffers will contain: encoded, non-encoded, encoded, ...
|
||||||
|
// currentBuf is pushed in buffers each time there is a change.
|
||||||
|
// So when buffers.length is odd then the last string is an encoded one
|
||||||
|
// and currentBuf contains non-encoded chars.
|
||||||
|
const hasCurrentBufErrors = () => buffers.length % 2 === 1;
|
||||||
|
|
||||||
|
for (let i = 0, ii = str.length; i < ii; i++) {
|
||||||
|
const unicode = str.codePointAt(i);
|
||||||
|
if (unicode > 0xd7ff && (unicode < 0xe000 || unicode > 0xfffd)) {
|
||||||
|
// unicode is represented by two uint16
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (this.toUnicode) {
|
||||||
|
const char = String.fromCodePoint(unicode);
|
||||||
|
const charCode = this.toUnicode.charCodeOf(char);
|
||||||
|
if (charCode !== -1) {
|
||||||
|
if (hasCurrentBufErrors()) {
|
||||||
|
buffers.push(currentBuf.join(""));
|
||||||
|
currentBuf.length = 0;
|
||||||
|
}
|
||||||
|
const charCodeLength = this.cMap
|
||||||
|
? this.cMap.getCharCodeLength(charCode)
|
||||||
|
: 1;
|
||||||
|
for (let j = charCodeLength - 1; j >= 0; j--) {
|
||||||
|
currentBuf.push(
|
||||||
|
String.fromCharCode((charCode >> (8 * j)) & 0xff)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unicode can't be encoded
|
||||||
|
if (!hasCurrentBufErrors()) {
|
||||||
|
buffers.push(currentBuf.join(""));
|
||||||
|
currentBuf.length = 0;
|
||||||
|
}
|
||||||
|
currentBuf.push(String.fromCodePoint(unicode));
|
||||||
|
}
|
||||||
|
|
||||||
|
buffers.push(currentBuf.join(""));
|
||||||
|
|
||||||
|
return buffers;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return Font;
|
return Font;
|
||||||
@ -3371,6 +3456,9 @@ var ErrorFont = (function ErrorFontClosure() {
|
|||||||
charsToGlyphs: function ErrorFont_charsToGlyphs() {
|
charsToGlyphs: function ErrorFont_charsToGlyphs() {
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
|
encodeString: function ErrorFont_encodeString(chars) {
|
||||||
|
return [chars];
|
||||||
|
},
|
||||||
exportData(extraProperties = false) {
|
exportData(extraProperties = false) {
|
||||||
return { error: this.error };
|
return { error: this.error };
|
||||||
},
|
},
|
||||||
|
@ -842,6 +842,20 @@ function escapeString(str) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isAscii(str) {
|
||||||
|
return /^[\x00-\x7F]*$/.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringToUTF16BEString(str) {
|
||||||
|
const buf = ["\xFE\xFF"];
|
||||||
|
for (let i = 0, ii = str.length; i < ii; i++) {
|
||||||
|
const char = str.charCodeAt(i);
|
||||||
|
buf.push(String.fromCharCode((char >> 8) & 0xff));
|
||||||
|
buf.push(String.fromCharCode(char & 0xff));
|
||||||
|
}
|
||||||
|
return buf.join("");
|
||||||
|
}
|
||||||
|
|
||||||
function stringToUTF8String(str) {
|
function stringToUTF8String(str) {
|
||||||
return decodeURIComponent(escape(str));
|
return decodeURIComponent(escape(str));
|
||||||
}
|
}
|
||||||
@ -1044,6 +1058,7 @@ export {
|
|||||||
getModificationDate,
|
getModificationDate,
|
||||||
getVerbosityLevel,
|
getVerbosityLevel,
|
||||||
info,
|
info,
|
||||||
|
isAscii,
|
||||||
isArrayBuffer,
|
isArrayBuffer,
|
||||||
isArrayEqual,
|
isArrayEqual,
|
||||||
isBool,
|
isBool,
|
||||||
@ -1061,6 +1076,7 @@ export {
|
|||||||
string32,
|
string32,
|
||||||
stringToBytes,
|
stringToBytes,
|
||||||
stringToPDFString,
|
stringToPDFString,
|
||||||
|
stringToUTF16BEString,
|
||||||
stringToUTF8String,
|
stringToUTF8String,
|
||||||
utf8StringToString,
|
utf8StringToString,
|
||||||
warn,
|
warn,
|
||||||
|
@ -32,10 +32,18 @@ import {
|
|||||||
import { createIdFactory, XRefMock } from "./test_utils.js";
|
import { createIdFactory, XRefMock } from "./test_utils.js";
|
||||||
import { Dict, Name, Ref, RefSetCache } from "../../src/core/primitives.js";
|
import { Dict, Name, Ref, RefSetCache } from "../../src/core/primitives.js";
|
||||||
import { Lexer, Parser } from "../../src/core/parser.js";
|
import { Lexer, Parser } from "../../src/core/parser.js";
|
||||||
|
import { DOMCMapReaderFactory } from "../../src/display/display_utils.js";
|
||||||
|
import { isNodeJS } from "../../src/shared/is_node.js";
|
||||||
|
import { NodeCMapReaderFactory } from "../../src/display/node_utils.js";
|
||||||
import { PartialEvaluator } from "../../src/core/evaluator.js";
|
import { PartialEvaluator } from "../../src/core/evaluator.js";
|
||||||
import { StringStream } from "../../src/core/stream.js";
|
import { StringStream } from "../../src/core/stream.js";
|
||||||
import { WorkerTask } from "../../src/core/worker.js";
|
import { WorkerTask } from "../../src/core/worker.js";
|
||||||
|
|
||||||
|
const cMapUrl = {
|
||||||
|
dom: "../../external/bcmaps/",
|
||||||
|
node: "./external/bcmaps/",
|
||||||
|
};
|
||||||
|
|
||||||
describe("annotation", function () {
|
describe("annotation", function () {
|
||||||
class PDFManagerMock {
|
class PDFManagerMock {
|
||||||
constructor(params) {
|
constructor(params) {
|
||||||
@ -82,6 +90,30 @@ describe("annotation", function () {
|
|||||||
pdfManagerMock = new PDFManagerMock({
|
pdfManagerMock = new PDFManagerMock({
|
||||||
docBaseUrl: null,
|
docBaseUrl: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let CMapReaderFactory;
|
||||||
|
if (isNodeJS) {
|
||||||
|
CMapReaderFactory = new NodeCMapReaderFactory({
|
||||||
|
baseUrl: cMapUrl.node,
|
||||||
|
isCompressed: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
CMapReaderFactory = new DOMCMapReaderFactory({
|
||||||
|
baseUrl: cMapUrl.dom,
|
||||||
|
isCompressed: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const builtInCMapCache = new Map();
|
||||||
|
builtInCMapCache.set(
|
||||||
|
"UniJIS-UTF16-H",
|
||||||
|
CMapReaderFactory.fetch({ name: "UniJIS-UTF16-H" })
|
||||||
|
);
|
||||||
|
builtInCMapCache.set(
|
||||||
|
"Adobe-Japan1-UCS2",
|
||||||
|
CMapReaderFactory.fetch({ name: "Adobe-Japan1-UCS2" })
|
||||||
|
);
|
||||||
|
|
||||||
idFactoryMock = createIdFactory(/* pageIndex = */ 0);
|
idFactoryMock = createIdFactory(/* pageIndex = */ 0);
|
||||||
partialEvaluator = new PartialEvaluator({
|
partialEvaluator = new PartialEvaluator({
|
||||||
xref: new XRefMock(),
|
xref: new XRefMock(),
|
||||||
@ -89,7 +121,9 @@ describe("annotation", function () {
|
|||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
idFactory: createIdFactory(/* pageIndex = */ 0),
|
idFactory: createIdFactory(/* pageIndex = */ 0),
|
||||||
fontCache: new RefSetCache(),
|
fontCache: new RefSetCache(),
|
||||||
|
builtInCMapCache,
|
||||||
});
|
});
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1419,7 +1453,7 @@ describe("annotation", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("TextWidgetAnnotation", function () {
|
describe("TextWidgetAnnotation", function () {
|
||||||
let textWidgetDict, fontRefObj;
|
let textWidgetDict, helvRefObj, gothRefObj;
|
||||||
|
|
||||||
beforeEach(function (done) {
|
beforeEach(function (done) {
|
||||||
textWidgetDict = new Dict();
|
textWidgetDict = new Dict();
|
||||||
@ -1432,11 +1466,38 @@ describe("annotation", function () {
|
|||||||
helvDict.set("Type", Name.get("Font"));
|
helvDict.set("Type", Name.get("Font"));
|
||||||
helvDict.set("Subtype", Name.get("Type1"));
|
helvDict.set("Subtype", Name.get("Type1"));
|
||||||
|
|
||||||
const fontRef = Ref.get(314, 0);
|
const gothDict = new Dict();
|
||||||
fontRefObj = { ref: fontRef, data: helvDict };
|
gothDict.set("BaseFont", Name.get("MSGothic"));
|
||||||
|
gothDict.set("Type", Name.get("Font"));
|
||||||
|
gothDict.set("Subtype", Name.get("Type0"));
|
||||||
|
gothDict.set("Encoding", Name.get("UniJIS-UTF16-H"));
|
||||||
|
gothDict.set("Name", Name.get("MSGothic"));
|
||||||
|
|
||||||
|
const cidSysInfoDict = new Dict();
|
||||||
|
cidSysInfoDict.set("Ordering", "Japan1");
|
||||||
|
cidSysInfoDict.set("Registry", "Adobe");
|
||||||
|
cidSysInfoDict.set("Supplement", "5");
|
||||||
|
|
||||||
|
const fontDescriptorDict = new Dict();
|
||||||
|
fontDescriptorDict.set("FontName", Name.get("MSGothic"));
|
||||||
|
fontDescriptorDict.set("CapHeight", "680");
|
||||||
|
|
||||||
|
const gothDescendantDict = new Dict();
|
||||||
|
gothDescendantDict.set("BaseFont", Name.get("MSGothic"));
|
||||||
|
gothDescendantDict.set("CIDSystemInfo", cidSysInfoDict);
|
||||||
|
gothDescendantDict.set("Subtype", Name.get("CIDFontType2"));
|
||||||
|
gothDescendantDict.set("Type", Name.get("Font"));
|
||||||
|
gothDescendantDict.set("FontDescriptor", fontDescriptorDict);
|
||||||
|
|
||||||
|
gothDict.set("DescendantFonts", [gothDescendantDict]);
|
||||||
|
|
||||||
|
const helvRef = Ref.get(314, 0);
|
||||||
|
const gothRef = Ref.get(159, 0);
|
||||||
|
helvRefObj = { ref: helvRef, data: helvDict };
|
||||||
|
gothRefObj = { ref: gothRef, data: gothDict };
|
||||||
const resourceDict = new Dict();
|
const resourceDict = new Dict();
|
||||||
const fontDict = new Dict();
|
const fontDict = new Dict();
|
||||||
fontDict.set("Helv", fontRef);
|
fontDict.set("Helv", helvRef);
|
||||||
resourceDict.set("Font", fontDict);
|
resourceDict.set("Font", fontDict);
|
||||||
|
|
||||||
textWidgetDict.set("DA", "/Helv 5 Tf");
|
textWidgetDict.set("DA", "/Helv 5 Tf");
|
||||||
@ -1447,7 +1508,7 @@ describe("annotation", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
textWidgetDict = fontRefObj = null;
|
textWidgetDict = helvRefObj = gothRefObj = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle unknown text alignment, maximum length and flags", function (done) {
|
it("should handle unknown text alignment, maximum length and flags", function (done) {
|
||||||
@ -1614,7 +1675,7 @@ describe("annotation", function () {
|
|||||||
const textWidgetRef = Ref.get(271, 0);
|
const textWidgetRef = Ref.get(271, 0);
|
||||||
const xref = new XRefMock([
|
const xref = new XRefMock([
|
||||||
{ ref: textWidgetRef, data: textWidgetDict },
|
{ ref: textWidgetRef, data: textWidgetDict },
|
||||||
fontRefObj,
|
helvRefObj,
|
||||||
]);
|
]);
|
||||||
const task = new WorkerTask("test print");
|
const task = new WorkerTask("test print");
|
||||||
partialEvaluator.xref = xref;
|
partialEvaluator.xref = xref;
|
||||||
@ -1644,6 +1705,46 @@ describe("annotation", function () {
|
|||||||
}, done.fail);
|
}, done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render regular text in Japanese for printing", function (done) {
|
||||||
|
textWidgetDict.get("DR").get("Font").set("Goth", gothRefObj.ref);
|
||||||
|
textWidgetDict.set("DA", "/Goth 5 Tf");
|
||||||
|
|
||||||
|
const textWidgetRef = Ref.get(271, 0);
|
||||||
|
const xref = new XRefMock([
|
||||||
|
{ ref: textWidgetRef, data: textWidgetDict },
|
||||||
|
gothRefObj,
|
||||||
|
]);
|
||||||
|
const task = new WorkerTask("test print");
|
||||||
|
partialEvaluator.xref = xref;
|
||||||
|
|
||||||
|
AnnotationFactory.create(
|
||||||
|
xref,
|
||||||
|
textWidgetRef,
|
||||||
|
pdfManagerMock,
|
||||||
|
idFactoryMock
|
||||||
|
)
|
||||||
|
.then(annotation => {
|
||||||
|
const id = annotation.data.id;
|
||||||
|
const annotationStorage = {};
|
||||||
|
annotationStorage[id] = { value: "こんにちは世界の" };
|
||||||
|
return annotation._getAppearance(
|
||||||
|
partialEvaluator,
|
||||||
|
task,
|
||||||
|
annotationStorage
|
||||||
|
);
|
||||||
|
}, done.fail)
|
||||||
|
.then(appearance => {
|
||||||
|
const utf16String =
|
||||||
|
"\x30\x53\x30\x93\x30\x6b\x30\x61" +
|
||||||
|
"\x30\x6f\x4e\x16\x75\x4c\x30\x6e";
|
||||||
|
expect(appearance).toEqual(
|
||||||
|
"/Tx BMC q BT /Goth 5 Tf 1 0 0 1 0 0 Tm" +
|
||||||
|
` 2.00 2.00 Td (${utf16String}) Tj ET Q EMC`
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
}, done.fail);
|
||||||
|
});
|
||||||
|
|
||||||
it("should render regular text for printing using normal appearance", function (done) {
|
it("should render regular text for printing using normal appearance", function (done) {
|
||||||
const textWidgetRef = Ref.get(271, 0);
|
const textWidgetRef = Ref.get(271, 0);
|
||||||
|
|
||||||
@ -1658,7 +1759,7 @@ describe("annotation", function () {
|
|||||||
|
|
||||||
const xref = new XRefMock([
|
const xref = new XRefMock([
|
||||||
{ ref: textWidgetRef, data: textWidgetDict },
|
{ ref: textWidgetRef, data: textWidgetDict },
|
||||||
fontRefObj,
|
helvRefObj,
|
||||||
]);
|
]);
|
||||||
const task = new WorkerTask("test print");
|
const task = new WorkerTask("test print");
|
||||||
partialEvaluator.xref = xref;
|
partialEvaluator.xref = xref;
|
||||||
@ -1699,7 +1800,7 @@ describe("annotation", function () {
|
|||||||
const textWidgetRef = Ref.get(271, 0);
|
const textWidgetRef = Ref.get(271, 0);
|
||||||
const xref = new XRefMock([
|
const xref = new XRefMock([
|
||||||
{ ref: textWidgetRef, data: textWidgetDict },
|
{ ref: textWidgetRef, data: textWidgetDict },
|
||||||
fontRefObj,
|
helvRefObj,
|
||||||
]);
|
]);
|
||||||
const task = new WorkerTask("test print");
|
const task = new WorkerTask("test print");
|
||||||
partialEvaluator.xref = xref;
|
partialEvaluator.xref = xref;
|
||||||
@ -1729,13 +1830,53 @@ describe("annotation", function () {
|
|||||||
}, done.fail);
|
}, done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render auto-sized text in Japanese for printing", function (done) {
|
||||||
|
textWidgetDict.get("DR").get("Font").set("Goth", gothRefObj.ref);
|
||||||
|
textWidgetDict.set("DA", "/Goth 0 Tf");
|
||||||
|
|
||||||
|
const textWidgetRef = Ref.get(271, 0);
|
||||||
|
const xref = new XRefMock([
|
||||||
|
{ ref: textWidgetRef, data: textWidgetDict },
|
||||||
|
gothRefObj,
|
||||||
|
]);
|
||||||
|
const task = new WorkerTask("test print");
|
||||||
|
partialEvaluator.xref = xref;
|
||||||
|
|
||||||
|
AnnotationFactory.create(
|
||||||
|
xref,
|
||||||
|
textWidgetRef,
|
||||||
|
pdfManagerMock,
|
||||||
|
idFactoryMock
|
||||||
|
)
|
||||||
|
.then(annotation => {
|
||||||
|
const id = annotation.data.id;
|
||||||
|
const annotationStorage = {};
|
||||||
|
annotationStorage[id] = { value: "こんにちは世界の" };
|
||||||
|
return annotation._getAppearance(
|
||||||
|
partialEvaluator,
|
||||||
|
task,
|
||||||
|
annotationStorage
|
||||||
|
);
|
||||||
|
}, done.fail)
|
||||||
|
.then(appearance => {
|
||||||
|
const utf16String =
|
||||||
|
"\x30\x53\x30\x93\x30\x6b\x30\x61" +
|
||||||
|
"\x30\x6f\x4e\x16\x75\x4c\x30\x6e";
|
||||||
|
expect(appearance).toEqual(
|
||||||
|
"/Tx BMC q BT /Goth 9 Tf 1 0 0 1 0 0 Tm" +
|
||||||
|
` 2.00 2.00 Td (${utf16String}) Tj ET Q EMC`
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
}, done.fail);
|
||||||
|
});
|
||||||
|
|
||||||
it("should not render a password for printing", function (done) {
|
it("should not render a password for printing", function (done) {
|
||||||
textWidgetDict.set("Ff", AnnotationFieldFlag.PASSWORD);
|
textWidgetDict.set("Ff", AnnotationFieldFlag.PASSWORD);
|
||||||
|
|
||||||
const textWidgetRef = Ref.get(271, 0);
|
const textWidgetRef = Ref.get(271, 0);
|
||||||
const xref = new XRefMock([
|
const xref = new XRefMock([
|
||||||
{ ref: textWidgetRef, data: textWidgetDict },
|
{ ref: textWidgetRef, data: textWidgetDict },
|
||||||
fontRefObj,
|
helvRefObj,
|
||||||
]);
|
]);
|
||||||
const task = new WorkerTask("test print");
|
const task = new WorkerTask("test print");
|
||||||
partialEvaluator.xref = xref;
|
partialEvaluator.xref = xref;
|
||||||
@ -1768,7 +1909,7 @@ describe("annotation", function () {
|
|||||||
const textWidgetRef = Ref.get(271, 0);
|
const textWidgetRef = Ref.get(271, 0);
|
||||||
const xref = new XRefMock([
|
const xref = new XRefMock([
|
||||||
{ ref: textWidgetRef, data: textWidgetDict },
|
{ ref: textWidgetRef, data: textWidgetDict },
|
||||||
fontRefObj,
|
helvRefObj,
|
||||||
]);
|
]);
|
||||||
const task = new WorkerTask("test print");
|
const task = new WorkerTask("test print");
|
||||||
partialEvaluator.xref = xref;
|
partialEvaluator.xref = xref;
|
||||||
@ -1808,6 +1949,45 @@ describe("annotation", function () {
|
|||||||
}, done.fail);
|
}, done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render multiline text in Japanese for printing", function (done) {
|
||||||
|
textWidgetDict.set("Ff", AnnotationFieldFlag.MULTILINE);
|
||||||
|
textWidgetDict.get("DR").get("Font").set("Goth", gothRefObj.ref);
|
||||||
|
textWidgetDict.set("DA", "/Goth 5 Tf");
|
||||||
|
|
||||||
|
const textWidgetRef = Ref.get(271, 0);
|
||||||
|
const xref = new XRefMock([
|
||||||
|
{ ref: textWidgetRef, data: textWidgetDict },
|
||||||
|
gothRefObj,
|
||||||
|
]);
|
||||||
|
const task = new WorkerTask("test print");
|
||||||
|
partialEvaluator.xref = xref;
|
||||||
|
|
||||||
|
AnnotationFactory.create(
|
||||||
|
xref,
|
||||||
|
textWidgetRef,
|
||||||
|
pdfManagerMock,
|
||||||
|
idFactoryMock
|
||||||
|
)
|
||||||
|
.then(annotation => {
|
||||||
|
const id = annotation.data.id;
|
||||||
|
const annotationStorage = {};
|
||||||
|
annotationStorage[id] = { value: "こんにちは世界の" };
|
||||||
|
return annotation._getAppearance(
|
||||||
|
partialEvaluator,
|
||||||
|
task,
|
||||||
|
annotationStorage
|
||||||
|
);
|
||||||
|
}, done.fail)
|
||||||
|
.then(appearance => {
|
||||||
|
expect(appearance).toEqual(
|
||||||
|
"/Tx BMC q BT /Goth 5 Tf 1 0 0 1 0 10 Tm " +
|
||||||
|
"2.00 -5.00 Td (\x30\x53\x30\x93\x30\x6b\x30\x61\x30\x6f) Tj\n" +
|
||||||
|
"0.00 -5.00 Td (\x4e\x16\x75\x4c\x30\x6e) Tj ET Q EMC"
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
}, done.fail);
|
||||||
|
});
|
||||||
|
|
||||||
it("should render multiline text with various EOL for printing", function (done) {
|
it("should render multiline text with various EOL for printing", function (done) {
|
||||||
textWidgetDict.set("Ff", AnnotationFieldFlag.MULTILINE);
|
textWidgetDict.set("Ff", AnnotationFieldFlag.MULTILINE);
|
||||||
textWidgetDict.set("Rect", [0, 0, 128, 10]);
|
textWidgetDict.set("Rect", [0, 0, 128, 10]);
|
||||||
@ -1815,7 +1995,7 @@ describe("annotation", function () {
|
|||||||
const textWidgetRef = Ref.get(271, 0);
|
const textWidgetRef = Ref.get(271, 0);
|
||||||
const xref = new XRefMock([
|
const xref = new XRefMock([
|
||||||
{ ref: textWidgetRef, data: textWidgetDict },
|
{ ref: textWidgetRef, data: textWidgetDict },
|
||||||
fontRefObj,
|
helvRefObj,
|
||||||
]);
|
]);
|
||||||
const task = new WorkerTask("test print");
|
const task = new WorkerTask("test print");
|
||||||
partialEvaluator.xref = xref;
|
partialEvaluator.xref = xref;
|
||||||
@ -1881,7 +2061,7 @@ describe("annotation", function () {
|
|||||||
const textWidgetRef = Ref.get(271, 0);
|
const textWidgetRef = Ref.get(271, 0);
|
||||||
const xref = new XRefMock([
|
const xref = new XRefMock([
|
||||||
{ ref: textWidgetRef, data: textWidgetDict },
|
{ ref: textWidgetRef, data: textWidgetDict },
|
||||||
fontRefObj,
|
helvRefObj,
|
||||||
]);
|
]);
|
||||||
const task = new WorkerTask("test print");
|
const task = new WorkerTask("test print");
|
||||||
partialEvaluator.xref = xref;
|
partialEvaluator.xref = xref;
|
||||||
@ -1914,9 +2094,55 @@ describe("annotation", function () {
|
|||||||
}, done.fail);
|
}, done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render comb with Japanese text for printing", function (done) {
|
||||||
|
textWidgetDict.set("Ff", AnnotationFieldFlag.COMB);
|
||||||
|
textWidgetDict.set("MaxLen", 4);
|
||||||
|
textWidgetDict.get("DR").get("Font").set("Goth", gothRefObj.ref);
|
||||||
|
textWidgetDict.set("DA", "/Goth 5 Tf");
|
||||||
|
textWidgetDict.set("Rect", [0, 0, 32, 10]);
|
||||||
|
|
||||||
|
const textWidgetRef = Ref.get(271, 0);
|
||||||
|
const xref = new XRefMock([
|
||||||
|
{ ref: textWidgetRef, data: textWidgetDict },
|
||||||
|
gothRefObj,
|
||||||
|
]);
|
||||||
|
const task = new WorkerTask("test print");
|
||||||
|
partialEvaluator.xref = xref;
|
||||||
|
|
||||||
|
AnnotationFactory.create(
|
||||||
|
xref,
|
||||||
|
textWidgetRef,
|
||||||
|
pdfManagerMock,
|
||||||
|
idFactoryMock
|
||||||
|
)
|
||||||
|
.then(annotation => {
|
||||||
|
const id = annotation.data.id;
|
||||||
|
const annotationStorage = {};
|
||||||
|
annotationStorage[id] = { value: "こんにちは世界の" };
|
||||||
|
return annotation._getAppearance(
|
||||||
|
partialEvaluator,
|
||||||
|
task,
|
||||||
|
annotationStorage
|
||||||
|
);
|
||||||
|
}, done.fail)
|
||||||
|
.then(appearance => {
|
||||||
|
expect(appearance).toEqual(
|
||||||
|
"/Tx BMC q BT /Goth 5 Tf 1 0 0 1 2 2 Tm" +
|
||||||
|
" (\x30\x53) Tj 8.00 0 Td (\x30\x93) Tj 8.00 0 Td (\x30\x6b) Tj" +
|
||||||
|
" 8.00 0 Td (\x30\x61) Tj 8.00 0 Td (\x30\x6f) Tj" +
|
||||||
|
" 8.00 0 Td (\x4e\x16) Tj 8.00 0 Td (\x75\x4c) Tj" +
|
||||||
|
" 8.00 0 Td (\x30\x6e) Tj ET Q EMC"
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
}, done.fail);
|
||||||
|
});
|
||||||
|
|
||||||
it("should save text", function (done) {
|
it("should save text", function (done) {
|
||||||
const textWidgetRef = Ref.get(123, 0);
|
const textWidgetRef = Ref.get(123, 0);
|
||||||
const xref = new XRefMock([{ ref: textWidgetRef, data: textWidgetDict }]);
|
const xref = new XRefMock([
|
||||||
|
{ ref: textWidgetRef, data: textWidgetDict },
|
||||||
|
helvRefObj,
|
||||||
|
]);
|
||||||
partialEvaluator.xref = xref;
|
partialEvaluator.xref = xref;
|
||||||
const task = new WorkerTask("test save");
|
const task = new WorkerTask("test save");
|
||||||
|
|
||||||
@ -1935,17 +2161,17 @@ describe("annotation", function () {
|
|||||||
expect(data.length).toEqual(2);
|
expect(data.length).toEqual(2);
|
||||||
const [oldData, newData] = data;
|
const [oldData, newData] = data;
|
||||||
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
||||||
expect(newData.ref).toEqual(Ref.get(1, 0));
|
expect(newData.ref).toEqual(Ref.get(2, 0));
|
||||||
|
|
||||||
oldData.data = oldData.data.replace(/\(D:[0-9]+\)/, "(date)");
|
oldData.data = oldData.data.replace(/\(D:[0-9]+\)/, "(date)");
|
||||||
expect(oldData.data).toEqual(
|
expect(oldData.data).toEqual(
|
||||||
"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 1 0 R>> /M (date)>>\nendobj\n"
|
"/V (hello world) /AP << /N 2 0 R>> /M (date)>>\nendobj\n"
|
||||||
);
|
);
|
||||||
expect(newData.data).toEqual(
|
expect(newData.data).toEqual(
|
||||||
"1 0 obj\n<< /Length 77 /Subtype /Form /Resources " +
|
"2 0 obj\n<< /Length 77 /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.00 2.00 Td (hello world) Tj " +
|
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 0 Tm 2.00 2.00 Td (hello world) Tj " +
|
||||||
"ET Q EMC\nendstream\nendobj\n"
|
"ET Q EMC\nendstream\nendobj\n"
|
||||||
@ -2039,6 +2265,55 @@ describe("annotation", function () {
|
|||||||
done();
|
done();
|
||||||
}, done.fail);
|
}, done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should save Japanese text", function (done) {
|
||||||
|
textWidgetDict.get("DR").get("Font").set("Goth", gothRefObj.ref);
|
||||||
|
textWidgetDict.set("DA", "/Goth 5 Tf");
|
||||||
|
|
||||||
|
const textWidgetRef = Ref.get(123, 0);
|
||||||
|
const xref = new XRefMock([
|
||||||
|
{ ref: textWidgetRef, data: textWidgetDict },
|
||||||
|
gothRefObj,
|
||||||
|
]);
|
||||||
|
partialEvaluator.xref = xref;
|
||||||
|
const task = new WorkerTask("test save");
|
||||||
|
|
||||||
|
AnnotationFactory.create(
|
||||||
|
xref,
|
||||||
|
textWidgetRef,
|
||||||
|
pdfManagerMock,
|
||||||
|
idFactoryMock
|
||||||
|
)
|
||||||
|
.then(annotation => {
|
||||||
|
const annotationStorage = {};
|
||||||
|
annotationStorage[annotation.data.id] = { value: "こんにちは世界の" };
|
||||||
|
return annotation.save(partialEvaluator, task, annotationStorage);
|
||||||
|
}, done.fail)
|
||||||
|
.then(data => {
|
||||||
|
const utf16String =
|
||||||
|
"\x30\x53\x30\x93\x30\x6b\x30\x61" +
|
||||||
|
"\x30\x6f\x4e\x16\x75\x4c\x30\x6e";
|
||||||
|
expect(data.length).toEqual(2);
|
||||||
|
const [oldData, newData] = data;
|
||||||
|
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
||||||
|
expect(newData.ref).toEqual(Ref.get(2, 0));
|
||||||
|
|
||||||
|
oldData.data = oldData.data.replace(/\(D:[0-9]+\)/, "(date)");
|
||||||
|
expect(oldData.data).toEqual(
|
||||||
|
"123 0 obj\n" +
|
||||||
|
"<< /Type /Annot /Subtype /Widget /FT /Tx /DA (/Goth 5 Tf) /DR " +
|
||||||
|
"<< /Font << /Helv 314 0 R /Goth 159 0 R>>>> /Rect [0 0 32 10] " +
|
||||||
|
`/V (\xfe\xff${utf16String}) /AP << /N 2 0 R>> /M (date)>>\nendobj\n`
|
||||||
|
);
|
||||||
|
expect(newData.data).toEqual(
|
||||||
|
"2 0 obj\n<< /Length 82 /Subtype /Form /Resources " +
|
||||||
|
"<< /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.00 2.00 Td (${utf16String}) Tj ` +
|
||||||
|
"ET Q EMC\nendstream\nendobj\n"
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
}, done.fail);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("ButtonWidgetAnnotation", function () {
|
describe("ButtonWidgetAnnotation", function () {
|
||||||
|
@ -21,6 +21,7 @@ import {
|
|||||||
escapeString,
|
escapeString,
|
||||||
getModificationDate,
|
getModificationDate,
|
||||||
isArrayBuffer,
|
isArrayBuffer,
|
||||||
|
isAscii,
|
||||||
isBool,
|
isBool,
|
||||||
isNum,
|
isNum,
|
||||||
isSameOrigin,
|
isSameOrigin,
|
||||||
@ -29,6 +30,7 @@ import {
|
|||||||
string32,
|
string32,
|
||||||
stringToBytes,
|
stringToBytes,
|
||||||
stringToPDFString,
|
stringToPDFString,
|
||||||
|
stringToUTF16BEString,
|
||||||
} from "../../src/shared/util.js";
|
} from "../../src/shared/util.js";
|
||||||
|
|
||||||
describe("util", function () {
|
describe("util", function () {
|
||||||
@ -346,4 +348,26 @@ describe("util", function () {
|
|||||||
expect(encodeToXmlString(str)).toEqual(str);
|
expect(encodeToXmlString(str)).toEqual(str);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("isAscii", function () {
|
||||||
|
it("handles ascii/non-ascii strings", function () {
|
||||||
|
expect(isAscii("hello world")).toEqual(true);
|
||||||
|
expect(isAscii("こんにちは世界の")).toEqual(false);
|
||||||
|
expect(isAscii("hello world in Japanese is こんにちは世界の")).toEqual(
|
||||||
|
false
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("stringToUTF16BEString", function () {
|
||||||
|
it("should encode a string in UTF16BE with a BOM", function () {
|
||||||
|
expect(stringToUTF16BEString("hello world")).toEqual(
|
||||||
|
"\xfe\xff\0h\0e\0l\0l\0o\0 \0w\0o\0r\0l\0d"
|
||||||
|
);
|
||||||
|
expect(stringToUTF16BEString("こんにちは世界の")).toEqual(
|
||||||
|
"\xfe\xff\x30\x53\x30\x93\x30\x6b\x30\x61" +
|
||||||
|
"\x30\x6f\x4e\x16\x75\x4c\x30\x6e"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user