/ node_modules / opentype.js / .reify-cache / 7e7e9be75404e4ed8b8ed9f4733d82e2a1cd6d0b.js
7e7e9be75404e4ed8b8ed9f4733d82e2a1cd6d0b.js
  1  "use strict";var Path;module.watch(require('./path'),{default(v){Path=v}},0);var sfnt;module.watch(require('./tables/sfnt'),{default(v){sfnt=v}},1);var DefaultEncoding;module.watch(require('./encoding'),{DefaultEncoding(v){DefaultEncoding=v}},2);var glyphset;module.watch(require('./glyphset'),{default(v){glyphset=v}},3);var Substitution;module.watch(require('./substitution'),{default(v){Substitution=v}},4);var isBrowser,checkArgument,arrayBufferToNodeBuffer;module.watch(require('./util'),{isBrowser(v){isBrowser=v},checkArgument(v){checkArgument=v},arrayBufferToNodeBuffer(v){arrayBufferToNodeBuffer=v}},5);var HintingTrueType;module.watch(require('./hintingtt'),{default(v){HintingTrueType=v}},6);// The Font object
  2  
  3  
  4  
  5  
  6  
  7  
  8  
  9  
 10  
 11  /**
 12   * @typedef FontOptions
 13   * @type Object
 14   * @property {Boolean} empty - whether to create a new empty font
 15   * @property {string} familyName
 16   * @property {string} styleName
 17   * @property {string=} fullName
 18   * @property {string=} postScriptName
 19   * @property {string=} designer
 20   * @property {string=} designerURL
 21   * @property {string=} manufacturer
 22   * @property {string=} manufacturerURL
 23   * @property {string=} license
 24   * @property {string=} licenseURL
 25   * @property {string=} version
 26   * @property {string=} description
 27   * @property {string=} copyright
 28   * @property {string=} trademark
 29   * @property {Number} unitsPerEm
 30   * @property {Number} ascender
 31   * @property {Number} descender
 32   * @property {Number} createdTimestamp
 33   * @property {string=} weightClass
 34   * @property {string=} widthClass
 35   * @property {string=} fsSelection
 36   */
 37  
 38  /**
 39   * A Font represents a loaded OpenType font file.
 40   * It contains a set of glyphs and methods to draw text on a drawing context,
 41   * or to get a path representing the text.
 42   * @exports opentype.Font
 43   * @class
 44   * @param {FontOptions}
 45   * @constructor
 46   */
 47  function Font(options) {
 48      options = options || {};
 49  
 50      if (!options.empty) {
 51          // Check that we've provided the minimum set of names.
 52          checkArgument(options.familyName, 'When creating a new Font object, familyName is required.');
 53          checkArgument(options.styleName, 'When creating a new Font object, styleName is required.');
 54          checkArgument(options.unitsPerEm, 'When creating a new Font object, unitsPerEm is required.');
 55          checkArgument(options.ascender, 'When creating a new Font object, ascender is required.');
 56          checkArgument(options.descender, 'When creating a new Font object, descender is required.');
 57          checkArgument(options.descender < 0, 'Descender should be negative (e.g. -512).');
 58  
 59          // OS X will complain if the names are empty, so we put a single space everywhere by default.
 60          this.names = {
 61              fontFamily: {en: options.familyName || ' '},
 62              fontSubfamily: {en: options.styleName || ' '},
 63              fullName: {en: options.fullName || options.familyName + ' ' + options.styleName},
 64              postScriptName: {en: options.postScriptName || options.familyName + options.styleName},
 65              designer: {en: options.designer || ' '},
 66              designerURL: {en: options.designerURL || ' '},
 67              manufacturer: {en: options.manufacturer || ' '},
 68              manufacturerURL: {en: options.manufacturerURL || ' '},
 69              license: {en: options.license || ' '},
 70              licenseURL: {en: options.licenseURL || ' '},
 71              version: {en: options.version || 'Version 0.1'},
 72              description: {en: options.description || ' '},
 73              copyright: {en: options.copyright || ' '},
 74              trademark: {en: options.trademark || ' '}
 75          };
 76          this.unitsPerEm = options.unitsPerEm || 1000;
 77          this.ascender = options.ascender;
 78          this.descender = options.descender;
 79          this.createdTimestamp = options.createdTimestamp;
 80          this.tables = { os2: {
 81              usWeightClass: options.weightClass || this.usWeightClasses.MEDIUM,
 82              usWidthClass: options.widthClass || this.usWidthClasses.MEDIUM,
 83              fsSelection: options.fsSelection || this.fsSelectionValues.REGULAR
 84          } };
 85      }
 86  
 87      this.supported = true; // Deprecated: parseBuffer will throw an error if font is not supported.
 88      this.glyphs = new glyphset.GlyphSet(this, options.glyphs || []);
 89      this.encoding = new DefaultEncoding(this);
 90      this.substitution = new Substitution(this);
 91      this.tables = this.tables || {};
 92  
 93      Object.defineProperty(this, 'hinting', {
 94          get: function() {
 95              if (this._hinting) return this._hinting;
 96              if (this.outlinesFormat === 'truetype') {
 97                  return (this._hinting = new HintingTrueType(this));
 98              }
 99          }
100      });
101  }
102  
103  /**
104   * Check if the font has a glyph for the given character.
105   * @param  {string}
106   * @return {Boolean}
107   */
108  Font.prototype.hasChar = function(c) {
109      return this.encoding.charToGlyphIndex(c) !== null;
110  };
111  
112  /**
113   * Convert the given character to a single glyph index.
114   * Note that this function assumes that there is a one-to-one mapping between
115   * the given character and a glyph; for complex scripts this might not be the case.
116   * @param  {string}
117   * @return {Number}
118   */
119  Font.prototype.charToGlyphIndex = function(s) {
120      return this.encoding.charToGlyphIndex(s);
121  };
122  
123  /**
124   * Convert the given character to a single Glyph object.
125   * Note that this function assumes that there is a one-to-one mapping between
126   * the given character and a glyph; for complex scripts this might not be the case.
127   * @param  {string}
128   * @return {opentype.Glyph}
129   */
130  Font.prototype.charToGlyph = function(c) {
131      const glyphIndex = this.charToGlyphIndex(c);
132      let glyph = this.glyphs.get(glyphIndex);
133      if (!glyph) {
134          // .notdef
135          glyph = this.glyphs.get(0);
136      }
137  
138      return glyph;
139  };
140  
141  /**
142   * Convert the given text to a list of Glyph objects.
143   * Note that there is no strict one-to-one mapping between characters and
144   * glyphs, so the list of returned glyphs can be larger or smaller than the
145   * length of the given string.
146   * @param  {string}
147   * @param  {GlyphRenderOptions} [options]
148   * @return {opentype.Glyph[]}
149   */
150  Font.prototype.stringToGlyphs = function(s, options) {
151      options = options || this.defaultRenderOptions;
152      // Get glyph indexes
153      const indexes = [];
154      for (let i = 0; i < s.length; i += 1) {
155          const c = s[i];
156          indexes.push(this.charToGlyphIndex(c));
157      }
158      let length = indexes.length;
159  
160      // Apply substitutions on glyph indexes
161      if (options.features) {
162          const script = options.script || this.substitution.getDefaultScriptName();
163          let manyToOne = [];
164          if (options.features.liga) manyToOne = manyToOne.concat(this.substitution.getFeature('liga', script, options.language));
165          if (options.features.rlig) manyToOne = manyToOne.concat(this.substitution.getFeature('rlig', script, options.language));
166          for (let i = 0; i < length; i += 1) {
167              for (let j = 0; j < manyToOne.length; j++) {
168                  const ligature = manyToOne[j];
169                  const components = ligature.sub;
170                  const compCount = components.length;
171                  let k = 0;
172                  while (k < compCount && components[k] === indexes[i + k]) k++;
173                  if (k === compCount) {
174                      indexes.splice(i, compCount, ligature.by);
175                      length = length - compCount + 1;
176                  }
177              }
178          }
179      }
180  
181      // convert glyph indexes to glyph objects
182      const glyphs = new Array(length);
183      const notdef = this.glyphs.get(0);
184      for (let i = 0; i < length; i += 1) {
185          glyphs[i] = this.glyphs.get(indexes[i]) || notdef;
186      }
187      return glyphs;
188  };
189  
190  /**
191   * @param  {string}
192   * @return {Number}
193   */
194  Font.prototype.nameToGlyphIndex = function(name) {
195      return this.glyphNames.nameToGlyphIndex(name);
196  };
197  
198  /**
199   * @param  {string}
200   * @return {opentype.Glyph}
201   */
202  Font.prototype.nameToGlyph = function(name) {
203      const glyphIndex = this.nameToGlyphIndex(name);
204      let glyph = this.glyphs.get(glyphIndex);
205      if (!glyph) {
206          // .notdef
207          glyph = this.glyphs.get(0);
208      }
209  
210      return glyph;
211  };
212  
213  /**
214   * @param  {Number}
215   * @return {String}
216   */
217  Font.prototype.glyphIndexToName = function(gid) {
218      if (!this.glyphNames.glyphIndexToName) {
219          return '';
220      }
221  
222      return this.glyphNames.glyphIndexToName(gid);
223  };
224  
225  /**
226   * Retrieve the value of the kerning pair between the left glyph (or its index)
227   * and the right glyph (or its index). If no kerning pair is found, return 0.
228   * The kerning value gets added to the advance width when calculating the spacing
229   * between glyphs.
230   * @param  {opentype.Glyph} leftGlyph
231   * @param  {opentype.Glyph} rightGlyph
232   * @return {Number}
233   */
234  Font.prototype.getKerningValue = function(leftGlyph, rightGlyph) {
235      leftGlyph = leftGlyph.index || leftGlyph;
236      rightGlyph = rightGlyph.index || rightGlyph;
237      const gposKerning = this.getGposKerningValue;
238      return gposKerning ? gposKerning(leftGlyph, rightGlyph) :
239          (this.kerningPairs[leftGlyph + ',' + rightGlyph] || 0);
240  };
241  
242  /**
243   * @typedef GlyphRenderOptions
244   * @type Object
245   * @property {string} [script] - script used to determine which features to apply. By default, 'DFLT' or 'latn' is used.
246   *                               See https://www.microsoft.com/typography/otspec/scripttags.htm
247   * @property {string} [language='dflt'] - language system used to determine which features to apply.
248   *                                        See https://www.microsoft.com/typography/developers/opentype/languagetags.aspx
249   * @property {boolean} [kerning=true] - whether to include kerning values
250   * @property {object} [features] - OpenType Layout feature tags. Used to enable or disable the features of the given script/language system.
251   *                                 See https://www.microsoft.com/typography/otspec/featuretags.htm
252   */
253  Font.prototype.defaultRenderOptions = {
254      kerning: true,
255      features: {
256          liga: true,
257          rlig: true
258      }
259  };
260  
261  /**
262   * Helper function that invokes the given callback for each glyph in the given text.
263   * The callback gets `(glyph, x, y, fontSize, options)`.* @param  {string} text
264   * @param {string} text - The text to apply.
265   * @param  {number} [x=0] - Horizontal position of the beginning of the text.
266   * @param  {number} [y=0] - Vertical position of the *baseline* of the text.
267   * @param  {number} [fontSize=72] - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`.
268   * @param  {GlyphRenderOptions=} options
269   * @param  {Function} callback
270   */
271  Font.prototype.forEachGlyph = function(text, x, y, fontSize, options, callback) {
272      x = x !== undefined ? x : 0;
273      y = y !== undefined ? y : 0;
274      fontSize = fontSize !== undefined ? fontSize : 72;
275      options = options || this.defaultRenderOptions;
276      const fontScale = 1 / this.unitsPerEm * fontSize;
277      const glyphs = this.stringToGlyphs(text, options);
278      for (let i = 0; i < glyphs.length; i += 1) {
279          const glyph = glyphs[i];
280          callback.call(this, glyph, x, y, fontSize, options);
281          if (glyph.advanceWidth) {
282              x += glyph.advanceWidth * fontScale;
283          }
284  
285          if (options.kerning && i < glyphs.length - 1) {
286              const kerningValue = this.getKerningValue(glyph, glyphs[i + 1]);
287              x += kerningValue * fontScale;
288          }
289  
290          if (options.letterSpacing) {
291              x += options.letterSpacing * fontSize;
292          } else if (options.tracking) {
293              x += (options.tracking / 1000) * fontSize;
294          }
295      }
296      return x;
297  };
298  
299  /**
300   * Create a Path object that represents the given text.
301   * @param  {string} text - The text to create.
302   * @param  {number} [x=0] - Horizontal position of the beginning of the text.
303   * @param  {number} [y=0] - Vertical position of the *baseline* of the text.
304   * @param  {number} [fontSize=72] - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`.
305   * @param  {GlyphRenderOptions=} options
306   * @return {opentype.Path}
307   */
308  Font.prototype.getPath = function(text, x, y, fontSize, options) {
309      const fullPath = new Path();
310      this.forEachGlyph(text, x, y, fontSize, options, function(glyph, gX, gY, gFontSize) {
311          const glyphPath = glyph.getPath(gX, gY, gFontSize, options, this);
312          fullPath.extend(glyphPath);
313      });
314      return fullPath;
315  };
316  
317  /**
318   * Create an array of Path objects that represent the glyphs of a given text.
319   * @param  {string} text - The text to create.
320   * @param  {number} [x=0] - Horizontal position of the beginning of the text.
321   * @param  {number} [y=0] - Vertical position of the *baseline* of the text.
322   * @param  {number} [fontSize=72] - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`.
323   * @param  {GlyphRenderOptions=} options
324   * @return {opentype.Path[]}
325   */
326  Font.prototype.getPaths = function(text, x, y, fontSize, options) {
327      const glyphPaths = [];
328      this.forEachGlyph(text, x, y, fontSize, options, function(glyph, gX, gY, gFontSize) {
329          const glyphPath = glyph.getPath(gX, gY, gFontSize, options, this);
330          glyphPaths.push(glyphPath);
331      });
332  
333      return glyphPaths;
334  };
335  
336  /**
337   * Returns the advance width of a text.
338   *
339   * This is something different than Path.getBoundingBox() as for example a
340   * suffixed whitespace increases the advanceWidth but not the bounding box
341   * or an overhanging letter like a calligraphic 'f' might have a quite larger
342   * bounding box than its advance width.
343   *
344   * This corresponds to canvas2dContext.measureText(text).width
345   *
346   * @param  {string} text - The text to create.
347   * @param  {number} [fontSize=72] - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`.
348   * @param  {GlyphRenderOptions=} options
349   * @return advance width
350   */
351  Font.prototype.getAdvanceWidth = function(text, fontSize, options) {
352      return this.forEachGlyph(text, 0, 0, fontSize, options, function() {});
353  };
354  
355  /**
356   * Draw the text on the given drawing context.
357   * @param  {CanvasRenderingContext2D} ctx - A 2D drawing context, like Canvas.
358   * @param  {string} text - The text to create.
359   * @param  {number} [x=0] - Horizontal position of the beginning of the text.
360   * @param  {number} [y=0] - Vertical position of the *baseline* of the text.
361   * @param  {number} [fontSize=72] - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`.
362   * @param  {GlyphRenderOptions=} options
363   */
364  Font.prototype.draw = function(ctx, text, x, y, fontSize, options) {
365      this.getPath(text, x, y, fontSize, options).draw(ctx);
366  };
367  
368  /**
369   * Draw the points of all glyphs in the text.
370   * On-curve points will be drawn in blue, off-curve points will be drawn in red.
371   * @param {CanvasRenderingContext2D} ctx - A 2D drawing context, like Canvas.
372   * @param {string} text - The text to create.
373   * @param {number} [x=0] - Horizontal position of the beginning of the text.
374   * @param {number} [y=0] - Vertical position of the *baseline* of the text.
375   * @param {number} [fontSize=72] - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`.
376   * @param {GlyphRenderOptions=} options
377   */
378  Font.prototype.drawPoints = function(ctx, text, x, y, fontSize, options) {
379      this.forEachGlyph(text, x, y, fontSize, options, function(glyph, gX, gY, gFontSize) {
380          glyph.drawPoints(ctx, gX, gY, gFontSize);
381      });
382  };
383  
384  /**
385   * Draw lines indicating important font measurements for all glyphs in the text.
386   * Black lines indicate the origin of the coordinate system (point 0,0).
387   * Blue lines indicate the glyph bounding box.
388   * Green line indicates the advance width of the glyph.
389   * @param {CanvasRenderingContext2D} ctx - A 2D drawing context, like Canvas.
390   * @param {string} text - The text to create.
391   * @param {number} [x=0] - Horizontal position of the beginning of the text.
392   * @param {number} [y=0] - Vertical position of the *baseline* of the text.
393   * @param {number} [fontSize=72] - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`.
394   * @param {GlyphRenderOptions=} options
395   */
396  Font.prototype.drawMetrics = function(ctx, text, x, y, fontSize, options) {
397      this.forEachGlyph(text, x, y, fontSize, options, function(glyph, gX, gY, gFontSize) {
398          glyph.drawMetrics(ctx, gX, gY, gFontSize);
399      });
400  };
401  
402  /**
403   * @param  {string}
404   * @return {string}
405   */
406  Font.prototype.getEnglishName = function(name) {
407      const translations = this.names[name];
408      if (translations) {
409          return translations.en;
410      }
411  };
412  
413  /**
414   * Validate
415   */
416  Font.prototype.validate = function() {
417      const warnings = [];
418      const _this = this;
419  
420      function assert(predicate, message) {
421          if (!predicate) {
422              warnings.push(message);
423          }
424      }
425  
426      function assertNamePresent(name) {
427          const englishName = _this.getEnglishName(name);
428          assert(englishName && englishName.trim().length > 0,
429                 'No English ' + name + ' specified.');
430      }
431  
432      // Identification information
433      assertNamePresent('fontFamily');
434      assertNamePresent('weightName');
435      assertNamePresent('manufacturer');
436      assertNamePresent('copyright');
437      assertNamePresent('version');
438  
439      // Dimension information
440      assert(this.unitsPerEm > 0, 'No unitsPerEm specified.');
441  };
442  
443  /**
444   * Convert the font object to a SFNT data structure.
445   * This structure contains all the necessary tables and metadata to create a binary OTF file.
446   * @return {opentype.Table}
447   */
448  Font.prototype.toTables = function() {
449      return sfnt.fontToTable(this);
450  };
451  /**
452   * @deprecated Font.toBuffer is deprecated. Use Font.toArrayBuffer instead.
453   */
454  Font.prototype.toBuffer = function() {
455      console.warn('Font.toBuffer is deprecated. Use Font.toArrayBuffer instead.');
456      return this.toArrayBuffer();
457  };
458  /**
459   * Converts a `opentype.Font` into an `ArrayBuffer`
460   * @return {ArrayBuffer}
461   */
462  Font.prototype.toArrayBuffer = function() {
463      const sfntTable = this.toTables();
464      const bytes = sfntTable.encode();
465      const buffer = new ArrayBuffer(bytes.length);
466      const intArray = new Uint8Array(buffer);
467      for (let i = 0; i < bytes.length; i++) {
468          intArray[i] = bytes[i];
469      }
470  
471      return buffer;
472  };
473  
474  /**
475   * Initiate a download of the OpenType font.
476   */
477  Font.prototype.download = function(fileName) {
478      const familyName = this.getEnglishName('fontFamily');
479      const styleName = this.getEnglishName('fontSubfamily');
480      fileName = fileName || familyName.replace(/\s/g, '') + '-' + styleName + '.otf';
481      const arrayBuffer = this.toArrayBuffer();
482  
483      if (isBrowser()) {
484          window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
485          window.requestFileSystem(window.TEMPORARY, arrayBuffer.byteLength, function(fs) {
486              fs.root.getFile(fileName, {create: true}, function(fileEntry) {
487                  fileEntry.createWriter(function(writer) {
488                      const dataView = new DataView(arrayBuffer);
489                      const blob = new Blob([dataView], {type: 'font/opentype'});
490                      writer.write(blob);
491  
492                      writer.addEventListener('writeend', function() {
493                          // Navigating to the file will download it.
494                          location.href = fileEntry.toURL();
495                      }, false);
496                  });
497              });
498          },
499          function(err) {
500              throw new Error(err.name + ': ' + err.message);
501          });
502      } else {
503          const fs = require('fs');
504          const buffer = arrayBufferToNodeBuffer(arrayBuffer);
505          fs.writeFileSync(fileName, buffer);
506      }
507  };
508  /**
509   * @private
510   */
511  Font.prototype.fsSelectionValues = {
512      ITALIC:              0x001, //1
513      UNDERSCORE:          0x002, //2
514      NEGATIVE:            0x004, //4
515      OUTLINED:            0x008, //8
516      STRIKEOUT:           0x010, //16
517      BOLD:                0x020, //32
518      REGULAR:             0x040, //64
519      USER_TYPO_METRICS:   0x080, //128
520      WWS:                 0x100, //256
521      OBLIQUE:             0x200  //512
522  };
523  
524  /**
525   * @private
526   */
527  Font.prototype.usWidthClasses = {
528      ULTRA_CONDENSED: 1,
529      EXTRA_CONDENSED: 2,
530      CONDENSED: 3,
531      SEMI_CONDENSED: 4,
532      MEDIUM: 5,
533      SEMI_EXPANDED: 6,
534      EXPANDED: 7,
535      EXTRA_EXPANDED: 8,
536      ULTRA_EXPANDED: 9
537  };
538  
539  /**
540   * @private
541   */
542  Font.prototype.usWeightClasses = {
543      THIN: 100,
544      EXTRA_LIGHT: 200,
545      LIGHT: 300,
546      NORMAL: 400,
547      MEDIUM: 500,
548      SEMI_BOLD: 600,
549      BOLD: 700,
550      EXTRA_BOLD: 800,
551      BLACK:    900
552  };
553  
554  module.exportDefault(Font);