2
0

text.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * Javascript code in this page
  4. *
  5. * Copyright 2021 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.TextMeasure = void 0;
  27. var _fonts = require("./fonts.js");
  28. const WIDTH_FACTOR = 1.01;
  29. class FontInfo {
  30. constructor(xfaFont, margin, lineHeight, fontFinder) {
  31. this.lineHeight = lineHeight;
  32. this.paraMargin = margin || {
  33. top: 0,
  34. bottom: 0,
  35. left: 0,
  36. right: 0
  37. };
  38. if (!xfaFont) {
  39. [this.pdfFont, this.xfaFont] = this.defaultFont(fontFinder);
  40. return;
  41. }
  42. this.xfaFont = {
  43. typeface: xfaFont.typeface,
  44. posture: xfaFont.posture,
  45. weight: xfaFont.weight,
  46. size: xfaFont.size,
  47. letterSpacing: xfaFont.letterSpacing
  48. };
  49. const typeface = fontFinder.find(xfaFont.typeface);
  50. if (!typeface) {
  51. [this.pdfFont, this.xfaFont] = this.defaultFont(fontFinder);
  52. return;
  53. }
  54. this.pdfFont = (0, _fonts.selectFont)(xfaFont, typeface);
  55. if (!this.pdfFont) {
  56. [this.pdfFont, this.xfaFont] = this.defaultFont(fontFinder);
  57. }
  58. }
  59. defaultFont(fontFinder) {
  60. const font = fontFinder.find("Helvetica", false) || fontFinder.find("Myriad Pro", false) || fontFinder.find("Arial", false) || fontFinder.getDefault();
  61. if (font && font.regular) {
  62. const pdfFont = font.regular;
  63. const info = pdfFont.cssFontInfo;
  64. const xfaFont = {
  65. typeface: info.fontFamily,
  66. posture: "normal",
  67. weight: "normal",
  68. size: 10,
  69. letterSpacing: 0
  70. };
  71. return [pdfFont, xfaFont];
  72. }
  73. const xfaFont = {
  74. typeface: "Courier",
  75. posture: "normal",
  76. weight: "normal",
  77. size: 10,
  78. letterSpacing: 0
  79. };
  80. return [null, xfaFont];
  81. }
  82. }
  83. class FontSelector {
  84. constructor(defaultXfaFont, defaultParaMargin, defaultLineHeight, fontFinder) {
  85. this.fontFinder = fontFinder;
  86. this.stack = [new FontInfo(defaultXfaFont, defaultParaMargin, defaultLineHeight, fontFinder)];
  87. }
  88. pushData(xfaFont, margin, lineHeight) {
  89. const lastFont = this.stack[this.stack.length - 1];
  90. for (const name of ["typeface", "posture", "weight", "size", "letterSpacing"]) {
  91. if (!xfaFont[name]) {
  92. xfaFont[name] = lastFont.xfaFont[name];
  93. }
  94. }
  95. for (const name of ["top", "bottom", "left", "right"]) {
  96. if (isNaN(margin[name])) {
  97. margin[name] = lastFont.paraMargin[name];
  98. }
  99. }
  100. const fontInfo = new FontInfo(xfaFont, margin, lineHeight || lastFont.lineHeight, this.fontFinder);
  101. if (!fontInfo.pdfFont) {
  102. fontInfo.pdfFont = lastFont.pdfFont;
  103. }
  104. this.stack.push(fontInfo);
  105. }
  106. popFont() {
  107. this.stack.pop();
  108. }
  109. topFont() {
  110. return this.stack[this.stack.length - 1];
  111. }
  112. }
  113. class TextMeasure {
  114. constructor(defaultXfaFont, defaultParaMargin, defaultLineHeight, fonts) {
  115. this.glyphs = [];
  116. this.fontSelector = new FontSelector(defaultXfaFont, defaultParaMargin, defaultLineHeight, fonts);
  117. this.extraHeight = 0;
  118. }
  119. pushData(xfaFont, margin, lineHeight) {
  120. this.fontSelector.pushData(xfaFont, margin, lineHeight);
  121. }
  122. popFont(xfaFont) {
  123. return this.fontSelector.popFont();
  124. }
  125. addPara() {
  126. const lastFont = this.fontSelector.topFont();
  127. this.extraHeight += lastFont.paraMargin.top + lastFont.paraMargin.bottom;
  128. }
  129. addString(str) {
  130. if (!str) {
  131. return;
  132. }
  133. const lastFont = this.fontSelector.topFont();
  134. const fontSize = lastFont.xfaFont.size;
  135. if (lastFont.pdfFont) {
  136. const letterSpacing = lastFont.xfaFont.letterSpacing;
  137. const pdfFont = lastFont.pdfFont;
  138. const lineHeight = lastFont.lineHeight || Math.ceil(Math.max(1.2, pdfFont.lineHeight) * fontSize);
  139. const scale = fontSize / 1000;
  140. for (const line of str.split(/[\u2029\n]/)) {
  141. const encodedLine = pdfFont.encodeString(line).join("");
  142. const glyphs = pdfFont.charsToGlyphs(encodedLine);
  143. for (const glyph of glyphs) {
  144. this.glyphs.push([glyph.width * scale + letterSpacing, lineHeight, glyph.unicode === " ", false]);
  145. }
  146. this.glyphs.push([0, 0, false, true]);
  147. }
  148. this.glyphs.pop();
  149. return;
  150. }
  151. for (const line of str.split(/[\u2029\n]/)) {
  152. for (const char of line.split("")) {
  153. this.glyphs.push([fontSize, fontSize, char === " ", false]);
  154. }
  155. this.glyphs.push([0, 0, false, true]);
  156. }
  157. this.glyphs.pop();
  158. }
  159. compute(maxWidth) {
  160. let lastSpacePos = -1,
  161. lastSpaceWidth = 0,
  162. width = 0,
  163. height = 0,
  164. currentLineWidth = 0,
  165. currentLineHeight = 0;
  166. let isBroken = false;
  167. for (let i = 0, ii = this.glyphs.length; i < ii; i++) {
  168. const [glyphWidth, glyphHeight, isSpace, isEOL] = this.glyphs[i];
  169. if (isEOL) {
  170. width = Math.max(width, currentLineWidth);
  171. currentLineWidth = 0;
  172. height += currentLineHeight;
  173. currentLineHeight = glyphHeight;
  174. lastSpacePos = -1;
  175. lastSpaceWidth = 0;
  176. continue;
  177. }
  178. if (isSpace) {
  179. if (currentLineWidth + glyphWidth > maxWidth) {
  180. width = Math.max(width, currentLineWidth);
  181. currentLineWidth = 0;
  182. height += currentLineHeight;
  183. currentLineHeight = glyphHeight;
  184. lastSpacePos = -1;
  185. lastSpaceWidth = 0;
  186. isBroken = true;
  187. } else {
  188. currentLineHeight = Math.max(glyphHeight, currentLineHeight);
  189. lastSpaceWidth = currentLineWidth;
  190. currentLineWidth += glyphWidth;
  191. lastSpacePos = i;
  192. }
  193. continue;
  194. }
  195. if (currentLineWidth + glyphWidth > maxWidth) {
  196. height += currentLineHeight;
  197. currentLineHeight = glyphHeight;
  198. if (lastSpacePos !== -1) {
  199. i = lastSpacePos;
  200. width = Math.max(width, lastSpaceWidth);
  201. currentLineWidth = 0;
  202. lastSpacePos = -1;
  203. lastSpaceWidth = 0;
  204. } else {
  205. width = Math.max(width, currentLineWidth);
  206. currentLineWidth = glyphWidth;
  207. }
  208. isBroken = true;
  209. continue;
  210. }
  211. currentLineWidth += glyphWidth;
  212. currentLineHeight = Math.max(glyphHeight, currentLineHeight);
  213. }
  214. width = Math.max(width, currentLineWidth);
  215. height += currentLineHeight + this.extraHeight;
  216. return {
  217. width: WIDTH_FACTOR * width,
  218. height,
  219. isBroken
  220. };
  221. }
  222. }
  223. exports.TextMeasure = TextMeasure;