default_appearance.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * JavaScript code in this page
  4. *
  5. * Copyright 2022 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * JavaScript code in this page
  21. */
  22. "use strict";
  23. Object.defineProperty(exports, "__esModule", {
  24. value: true
  25. });
  26. exports.FakeUnicodeFont = void 0;
  27. exports.createDefaultAppearance = createDefaultAppearance;
  28. exports.getPdfColor = getPdfColor;
  29. exports.parseDefaultAppearance = parseDefaultAppearance;
  30. var _primitives = require("./primitives.js");
  31. var _core_utils = require("./core_utils.js");
  32. var _util = require("../shared/util.js");
  33. var _colorspace = require("./colorspace.js");
  34. var _evaluator = require("./evaluator.js");
  35. var _stream = require("./stream.js");
  36. class DefaultAppearanceEvaluator extends _evaluator.EvaluatorPreprocessor {
  37. constructor(str) {
  38. super(new _stream.StringStream(str));
  39. }
  40. parse() {
  41. const operation = {
  42. fn: 0,
  43. args: []
  44. };
  45. const result = {
  46. fontSize: 0,
  47. fontName: "",
  48. fontColor: new Uint8ClampedArray(3)
  49. };
  50. try {
  51. while (true) {
  52. operation.args.length = 0;
  53. if (!this.read(operation)) {
  54. break;
  55. }
  56. if (this.savedStatesDepth !== 0) {
  57. continue;
  58. }
  59. const {
  60. fn,
  61. args
  62. } = operation;
  63. switch (fn | 0) {
  64. case _util.OPS.setFont:
  65. const [fontName, fontSize] = args;
  66. if (fontName instanceof _primitives.Name) {
  67. result.fontName = fontName.name;
  68. }
  69. if (typeof fontSize === "number" && fontSize > 0) {
  70. result.fontSize = fontSize;
  71. }
  72. break;
  73. case _util.OPS.setFillRGBColor:
  74. _colorspace.ColorSpace.singletons.rgb.getRgbItem(args, 0, result.fontColor, 0);
  75. break;
  76. case _util.OPS.setFillGray:
  77. _colorspace.ColorSpace.singletons.gray.getRgbItem(args, 0, result.fontColor, 0);
  78. break;
  79. case _util.OPS.setFillColorSpace:
  80. _colorspace.ColorSpace.singletons.cmyk.getRgbItem(args, 0, result.fontColor, 0);
  81. break;
  82. }
  83. }
  84. } catch (reason) {
  85. (0, _util.warn)(`parseDefaultAppearance - ignoring errors: "${reason}".`);
  86. }
  87. return result;
  88. }
  89. }
  90. function parseDefaultAppearance(str) {
  91. return new DefaultAppearanceEvaluator(str).parse();
  92. }
  93. function getPdfColor(color, isFill) {
  94. if (color[0] === color[1] && color[1] === color[2]) {
  95. const gray = color[0] / 255;
  96. return `${(0, _core_utils.numberToString)(gray)} ${isFill ? "g" : "G"}`;
  97. }
  98. return Array.from(color, c => (0, _core_utils.numberToString)(c / 255)).join(" ") + ` ${isFill ? "rg" : "RG"}`;
  99. }
  100. function createDefaultAppearance({
  101. fontSize,
  102. fontName,
  103. fontColor
  104. }) {
  105. return `/${(0, _core_utils.escapePDFName)(fontName)} ${fontSize} Tf ${getPdfColor(fontColor, true)}`;
  106. }
  107. class FakeUnicodeFont {
  108. constructor(xref, fontFamily) {
  109. this.xref = xref;
  110. this.widths = null;
  111. this.firstChar = Infinity;
  112. this.lastChar = -Infinity;
  113. this.fontFamily = fontFamily;
  114. const canvas = new OffscreenCanvas(1, 1);
  115. this.ctxMeasure = canvas.getContext("2d");
  116. if (!FakeUnicodeFont._fontNameId) {
  117. FakeUnicodeFont._fontNameId = 1;
  118. }
  119. this.fontName = _primitives.Name.get(`InvalidPDFjsFont_${fontFamily}_${FakeUnicodeFont._fontNameId++}`);
  120. }
  121. get toUnicodeRef() {
  122. if (!FakeUnicodeFont._toUnicodeRef) {
  123. const toUnicode = `/CIDInit /ProcSet findresource begin
  124. 12 dict begin
  125. begincmap
  126. /CIDSystemInfo
  127. << /Registry (Adobe)
  128. /Ordering (UCS) /Supplement 0 >> def
  129. /CMapName /Adobe-Identity-UCS def
  130. /CMapType 2 def
  131. 1 begincodespacerange
  132. <0000> <FFFF>
  133. endcodespacerange
  134. 1 beginbfrange
  135. <0000> <FFFF> <0000>
  136. endbfrange
  137. endcmap CMapName currentdict /CMap defineresource pop end end`;
  138. const toUnicodeStream = FakeUnicodeFont.toUnicodeStream = new _stream.StringStream(toUnicode);
  139. const toUnicodeDict = new _primitives.Dict(this.xref);
  140. toUnicodeStream.dict = toUnicodeDict;
  141. toUnicodeDict.set("Length", toUnicode.length);
  142. FakeUnicodeFont._toUnicodeRef = this.xref.getNewPersistentRef(toUnicodeStream);
  143. }
  144. return FakeUnicodeFont._toUnicodeRef;
  145. }
  146. get fontDescriptorRef() {
  147. if (!FakeUnicodeFont._fontDescriptorRef) {
  148. const fontDescriptor = new _primitives.Dict(this.xref);
  149. fontDescriptor.set("Type", _primitives.Name.get("FontDescriptor"));
  150. fontDescriptor.set("FontName", this.fontName);
  151. fontDescriptor.set("FontFamily", "MyriadPro Regular");
  152. fontDescriptor.set("FontBBox", [0, 0, 0, 0]);
  153. fontDescriptor.set("FontStretch", _primitives.Name.get("Normal"));
  154. fontDescriptor.set("FontWeight", 400);
  155. fontDescriptor.set("ItalicAngle", 0);
  156. FakeUnicodeFont._fontDescriptorRef = this.xref.getNewPersistentRef(fontDescriptor);
  157. }
  158. return FakeUnicodeFont._fontDescriptorRef;
  159. }
  160. get descendantFontRef() {
  161. const descendantFont = new _primitives.Dict(this.xref);
  162. descendantFont.set("BaseFont", this.fontName);
  163. descendantFont.set("Type", _primitives.Name.get("Font"));
  164. descendantFont.set("Subtype", _primitives.Name.get("CIDFontType0"));
  165. descendantFont.set("CIDToGIDMap", _primitives.Name.get("Identity"));
  166. descendantFont.set("FirstChar", this.firstChar);
  167. descendantFont.set("LastChar", this.lastChar);
  168. descendantFont.set("FontDescriptor", this.fontDescriptorRef);
  169. descendantFont.set("DW", 1000);
  170. const widths = [];
  171. const chars = [...this.widths.entries()].sort();
  172. let currentChar = null;
  173. let currentWidths = null;
  174. for (const [char, width] of chars) {
  175. if (!currentChar) {
  176. currentChar = char;
  177. currentWidths = [width];
  178. continue;
  179. }
  180. if (char === currentChar + currentWidths.length) {
  181. currentWidths.push(width);
  182. } else {
  183. widths.push(currentChar, currentWidths);
  184. currentChar = char;
  185. currentWidths = [width];
  186. }
  187. }
  188. if (currentChar) {
  189. widths.push(currentChar, currentWidths);
  190. }
  191. descendantFont.set("W", widths);
  192. const cidSystemInfo = new _primitives.Dict(this.xref);
  193. cidSystemInfo.set("Ordering", "Identity");
  194. cidSystemInfo.set("Registry", "Adobe");
  195. cidSystemInfo.set("Supplement", 0);
  196. descendantFont.set("CIDSystemInfo", cidSystemInfo);
  197. return this.xref.getNewPersistentRef(descendantFont);
  198. }
  199. get baseFontRef() {
  200. const baseFont = new _primitives.Dict(this.xref);
  201. baseFont.set("BaseFont", this.fontName);
  202. baseFont.set("Type", _primitives.Name.get("Font"));
  203. baseFont.set("Subtype", _primitives.Name.get("Type0"));
  204. baseFont.set("Encoding", _primitives.Name.get("Identity-H"));
  205. baseFont.set("DescendantFonts", [this.descendantFontRef]);
  206. baseFont.set("ToUnicode", this.toUnicodeRef);
  207. return this.xref.getNewPersistentRef(baseFont);
  208. }
  209. get resources() {
  210. const resources = new _primitives.Dict(this.xref);
  211. const font = new _primitives.Dict(this.xref);
  212. font.set(this.fontName.name, this.baseFontRef);
  213. resources.set("Font", font);
  214. return resources;
  215. }
  216. _createContext() {
  217. this.widths = new Map();
  218. this.ctxMeasure.font = `1000px ${this.fontFamily}`;
  219. return this.ctxMeasure;
  220. }
  221. createFontResources(text) {
  222. const ctx = this._createContext();
  223. for (const line of text.split(/\r\n?|\n/)) {
  224. for (const char of line.split("")) {
  225. const code = char.charCodeAt(0);
  226. if (this.widths.has(code)) {
  227. continue;
  228. }
  229. const metrics = ctx.measureText(char);
  230. const width = Math.ceil(metrics.width);
  231. this.widths.set(code, width);
  232. this.firstChar = Math.min(code, this.firstChar);
  233. this.lastChar = Math.max(code, this.lastChar);
  234. }
  235. }
  236. return this.resources;
  237. }
  238. createAppearance(text, rect, rotation, fontSize, bgColor) {
  239. const ctx = this._createContext();
  240. const lines = [];
  241. let maxWidth = -Infinity;
  242. for (const line of text.split(/\r\n?|\n/)) {
  243. lines.push(line);
  244. const lineWidth = ctx.measureText(line).width;
  245. maxWidth = Math.max(maxWidth, lineWidth);
  246. for (const char of line.split("")) {
  247. const code = char.charCodeAt(0);
  248. let width = this.widths.get(code);
  249. if (width === undefined) {
  250. const metrics = ctx.measureText(char);
  251. width = Math.ceil(metrics.width);
  252. this.widths.set(code, width);
  253. this.firstChar = Math.min(code, this.firstChar);
  254. this.lastChar = Math.max(code, this.lastChar);
  255. }
  256. }
  257. }
  258. maxWidth *= fontSize / 1000;
  259. const [x1, y1, x2, y2] = rect;
  260. let w = x2 - x1;
  261. let h = y2 - y1;
  262. if (rotation % 180 !== 0) {
  263. [w, h] = [h, w];
  264. }
  265. let hscale = 1;
  266. if (maxWidth > w) {
  267. hscale = w / maxWidth;
  268. }
  269. let vscale = 1;
  270. const lineHeight = _util.LINE_FACTOR * fontSize;
  271. const lineDescent = _util.LINE_DESCENT_FACTOR * fontSize;
  272. const maxHeight = lineHeight * lines.length;
  273. if (maxHeight > h) {
  274. vscale = h / maxHeight;
  275. }
  276. const fscale = Math.min(hscale, vscale);
  277. const newFontSize = fontSize * fscale;
  278. const buffer = ["q", `0 0 ${(0, _core_utils.numberToString)(w)} ${(0, _core_utils.numberToString)(h)} re W n`, `BT`, `1 0 0 1 0 ${(0, _core_utils.numberToString)(h + lineDescent)} Tm 0 Tc ${getPdfColor(bgColor, true)}`, `/${this.fontName.name} ${(0, _core_utils.numberToString)(newFontSize)} Tf`];
  279. const vShift = (0, _core_utils.numberToString)(lineHeight);
  280. for (const line of lines) {
  281. buffer.push(`0 -${vShift} Td <${(0, _core_utils.stringToUTF16HexString)(line)}> Tj`);
  282. }
  283. buffer.push("ET", "Q");
  284. const appearance = buffer.join("\n");
  285. const appearanceStreamDict = new _primitives.Dict(this.xref);
  286. appearanceStreamDict.set("Subtype", _primitives.Name.get("Form"));
  287. appearanceStreamDict.set("Type", _primitives.Name.get("XObject"));
  288. appearanceStreamDict.set("BBox", [0, 0, w, h]);
  289. appearanceStreamDict.set("Length", appearance.length);
  290. appearanceStreamDict.set("Resources", this.resources);
  291. if (rotation) {
  292. const matrix = (0, _core_utils.getRotationMatrix)(rotation, w, h);
  293. appearanceStreamDict.set("Matrix", matrix);
  294. }
  295. const ap = new _stream.StringStream(appearance);
  296. ap.dict = appearanceStreamDict;
  297. return ap;
  298. }
  299. }
  300. exports.FakeUnicodeFont = FakeUnicodeFont;