2012-09-01 07:48:21 +09:00
|
|
|
/* Copyright 2012 Mozilla Foundation
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
2011-06-12 10:00:45 +09:00
|
|
|
|
2017-04-02 23:14:30 +09:00
|
|
|
import {
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
assert,
|
|
|
|
bytesToString,
|
|
|
|
FONT_IDENTITY_MATRIX,
|
|
|
|
FontType,
|
|
|
|
FormatError,
|
|
|
|
info,
|
|
|
|
isNum,
|
|
|
|
shadow,
|
|
|
|
string32,
|
|
|
|
warn,
|
2020-01-02 20:00:16 +09:00
|
|
|
} from "../shared/util.js";
|
2021-05-02 23:46:49 +09:00
|
|
|
import { CFFCompiler, CFFParser } from "./cff_parser.js";
|
2021-05-02 23:11:01 +09:00
|
|
|
import {
|
|
|
|
FontFlags,
|
|
|
|
getFontType,
|
|
|
|
MacStandardGlyphOrdering,
|
2020-12-11 10:32:18 +09:00
|
|
|
normalizeFontName,
|
2021-05-02 23:11:01 +09:00
|
|
|
recoverGlyphName,
|
|
|
|
SEAC_ANALYSIS_ENABLED,
|
|
|
|
} from "./fonts_utils.js";
|
2020-01-02 20:00:16 +09:00
|
|
|
import { getDingbatsGlyphsUnicode, getGlyphsUnicode } from "./glyphlist.js";
|
2017-04-02 23:14:30 +09:00
|
|
|
import {
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
getEncoding,
|
|
|
|
MacRomanEncoding,
|
|
|
|
StandardEncoding,
|
|
|
|
SymbolSetEncoding,
|
|
|
|
ZapfDingbatsEncoding,
|
2020-01-02 20:00:16 +09:00
|
|
|
} from "./encodings.js";
|
2017-04-02 23:14:30 +09:00
|
|
|
import {
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
getGlyphMapForStandardFonts,
|
|
|
|
getNonStdFontMap,
|
|
|
|
getStdFontMap,
|
|
|
|
getSupplementalGlyphMapForArialBlack,
|
|
|
|
getSupplementalGlyphMapForCalibri,
|
2020-01-02 20:00:16 +09:00
|
|
|
} from "./standard_fonts.js";
|
2017-04-02 23:14:30 +09:00
|
|
|
import {
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
getUnicodeForGlyph,
|
|
|
|
getUnicodeRangeFor,
|
|
|
|
mapSpecialUnicodeValues,
|
2020-01-02 20:00:16 +09:00
|
|
|
} from "./unicode.js";
|
2021-05-02 19:04:34 +09:00
|
|
|
import { IdentityToUnicodeMap, ToUnicodeMap } from "./to_unicode_map.js";
|
2021-05-02 23:36:12 +09:00
|
|
|
import { CFFFont } from "./cff_font.js";
|
2020-01-02 20:00:16 +09:00
|
|
|
import { FontRendererFactory } from "./font_renderer.js";
|
2021-05-10 18:54:38 +09:00
|
|
|
import { GlyfTable } from "./glyf.js";
|
2020-01-02 20:00:16 +09:00
|
|
|
import { IdentityCMap } from "./cmap.js";
|
2021-05-02 18:47:56 +09:00
|
|
|
import { OpenTypeFileBuilder } from "./opentype_file_builder.js";
|
2021-05-02 23:46:49 +09:00
|
|
|
import { readUint32 } from "./core_utils.js";
|
2020-01-02 20:00:16 +09:00
|
|
|
import { Stream } from "./stream.js";
|
2021-05-02 23:46:49 +09:00
|
|
|
import { Type1Font } from "./type1_font.js";
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
|
|
|
|
// Unicode Private Use Areas:
|
2018-01-05 07:43:07 +09:00
|
|
|
const PRIVATE_USE_AREAS = [
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
[0xe000, 0xf8ff], // BMP (0)
|
|
|
|
[0x100000, 0x10fffd], // PUP (16)
|
2018-01-05 07:43:07 +09:00
|
|
|
];
|
2011-10-03 18:16:18 +09:00
|
|
|
|
2011-09-17 09:53:52 +09:00
|
|
|
// PDF Glyph Space Units are one Thousandth of a TextSpace Unit
|
|
|
|
// except for Type 3 fonts
|
2021-05-02 23:58:28 +09:00
|
|
|
const PDF_GLYPH_SPACE_UNITS = 1000;
|
2011-09-05 21:35:03 +09:00
|
|
|
|
2020-03-30 19:28:57 +09:00
|
|
|
const EXPORT_DATA_PROPERTIES = [
|
|
|
|
"ascent",
|
|
|
|
"bbox",
|
|
|
|
"black",
|
|
|
|
"bold",
|
|
|
|
"charProcOperatorList",
|
|
|
|
"composite",
|
2021-03-26 17:28:18 +09:00
|
|
|
"cssFontInfo",
|
2020-03-30 19:28:57 +09:00
|
|
|
"data",
|
|
|
|
"defaultVMetrics",
|
|
|
|
"defaultWidth",
|
|
|
|
"descent",
|
|
|
|
"fallbackName",
|
|
|
|
"fontMatrix",
|
|
|
|
"fontType",
|
|
|
|
"isMonospace",
|
|
|
|
"isSerifFont",
|
|
|
|
"isType3Font",
|
|
|
|
"italic",
|
|
|
|
"loadedName",
|
|
|
|
"mimetype",
|
|
|
|
"missingFile",
|
|
|
|
"name",
|
|
|
|
"remeasure",
|
|
|
|
"subtype",
|
|
|
|
"type",
|
|
|
|
"vertical",
|
[api-minor] Change `Font.exportData` to, by default, stop exporting properties which are completely unused on the main-thread and/or in the API (PR 11773 follow-up)
For years now, the `Font.exportData` method has (because of its previous implementation) been exporting many properties despite them being completely unused on the main-thread and/or in the API.
This is unfortunate, since among those properties there's a number of potentially very large data-structures, containing e.g. Arrays and Objects, which thus have to be first structured cloned and then stored on the main-thread.
With the changes in this patch, we'll thus by default save memory for *every* `Font` instance created (there can be a lot in longer documents). The memory savings obviously depends a lot on the actual font data, but some approximate figures are: For non-embedded fonts it can save a couple of kilobytes, for simple embedded fonts a handful of kilobytes, and for composite fonts the size of this auxiliary can even be larger than the actual font program itself.
All-in-all, there's no good reason to keep exporting these properties by default when they're unused. However, since we cannot be sure that every property is unused in custom implementations of the PDF.js library, this patch adds a new `getDocument` option (named `fontExtraProperties`) that still allows access to the following properties:
- "cMap": An internal data structure, only used with composite fonts and never really intended to be exposed on the main-thread and/or in the API.
Note also that the `CMap`/`IdentityCMap` classes are a lot more complex than simple Objects, but only their "internal" properties survive the structured cloning used to send data to the main-thread. Given that CMaps can often be *very* large, not exporting them can also save a fair bit of memory.
- "defaultEncoding": An internal property used with simple fonts, and used when building the glyph mapping on the worker-thread. Considering how complex that topic is, and given that not all font types are handled identically, exposing this on the main-thread and/or in the API most likely isn't useful.
- "differences": An internal property used with simple fonts, and used when building the glyph mapping on the worker-thread. Considering how complex that topic is, and given that not all font types are handled identically, exposing this on the main-thread and/or in the API most likely isn't useful.
- "isSymbolicFont": An internal property, used during font parsing and building of the glyph mapping on the worker-thread.
- "seacMap": An internal map, only potentially used with *some* Type1/CFF fonts and never intended to be exposed in the API. The existing `Font.{charToGlyph, charToGlyphs}` functionality already takes this data into account when handling text.
- "toFontChar": The glyph map, necessary for mapping characters to glyphs in the font, which is built upon the various encoding information contained in the font dictionary and/or font program. This is not directly used on the main-thread and/or in the API.
- "toUnicode": The unicode map, necessary for text-extraction to work correctly, which is built upon the ToUnicode/CMap information contained in the font dictionary, but not directly used on the main-thread and/or in the API.
- "vmetrics": An array of width data used with fonts which are composite *and* vertical, but not directly used on the main-thread and/or in the API.
- "widths": An array of width data used with most fonts, but not directly used on the main-thread and/or in the API.
2020-04-03 18:51:46 +09:00
|
|
|
];
|
|
|
|
|
|
|
|
const EXPORT_DATA_EXTRA_PROPERTIES = [
|
|
|
|
"cMap",
|
|
|
|
"defaultEncoding",
|
|
|
|
"differences",
|
|
|
|
"isSymbolicFont",
|
|
|
|
"seacMap",
|
|
|
|
"toFontChar",
|
|
|
|
"toUnicode",
|
2020-03-30 19:28:57 +09:00
|
|
|
"vmetrics",
|
|
|
|
"widths",
|
|
|
|
];
|
|
|
|
|
2013-01-04 09:39:06 +09:00
|
|
|
function adjustWidths(properties) {
|
2015-12-08 06:30:09 +09:00
|
|
|
if (!properties.fontMatrix) {
|
|
|
|
return;
|
|
|
|
}
|
2013-01-04 09:39:06 +09:00
|
|
|
if (properties.fontMatrix[0] === FONT_IDENTITY_MATRIX[0]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// adjusting width to fontMatrix scale
|
2021-05-02 23:58:28 +09:00
|
|
|
const scale = 0.001 / properties.fontMatrix[0];
|
|
|
|
const glyphsWidths = properties.widths;
|
|
|
|
for (const glyph in glyphsWidths) {
|
2013-01-04 09:39:06 +09:00
|
|
|
glyphsWidths[glyph] *= scale;
|
|
|
|
}
|
|
|
|
properties.defaultWidth *= scale;
|
|
|
|
}
|
|
|
|
|
For embedded Type1 fonts without included `ToUnicode`/`Encoding` data, attempt to improve text selection by using the `builtInEncoding` to amend the `toUnicode` map (issue 6901, issue 7182, issue 7217, bug 917796, bug 1242142)
Note that in order to prevent any possible issues, this patch does *not* try to amend the `toUnicode` data for Type1 fonts that contain either `ToUnicode` or `Encoding` entries in the font dictionary.
Fixes, or at least improves, issues/bugs such as e.g. 6658, 6901, 7182, 7217, bug 917796, bug 1242142.
2016-08-18 01:33:06 +09:00
|
|
|
function adjustToUnicode(properties, builtInEncoding) {
|
2020-12-11 10:32:18 +09:00
|
|
|
if (properties.isInternalFont) {
|
|
|
|
return;
|
|
|
|
}
|
For embedded Type1 fonts without included `ToUnicode`/`Encoding` data, attempt to improve text selection by using the `builtInEncoding` to amend the `toUnicode` map (issue 6901, issue 7182, issue 7217, bug 917796, bug 1242142)
Note that in order to prevent any possible issues, this patch does *not* try to amend the `toUnicode` data for Type1 fonts that contain either `ToUnicode` or `Encoding` entries in the font dictionary.
Fixes, or at least improves, issues/bugs such as e.g. 6658, 6901, 7182, 7217, bug 917796, bug 1242142.
2016-08-18 01:33:06 +09:00
|
|
|
if (properties.hasIncludedToUnicodeMap) {
|
|
|
|
return; // The font dictionary has a `ToUnicode` entry.
|
|
|
|
}
|
|
|
|
if (builtInEncoding === properties.defaultEncoding) {
|
|
|
|
return; // No point in trying to adjust `toUnicode` if the encodings match.
|
|
|
|
}
|
|
|
|
if (properties.toUnicode instanceof IdentityToUnicodeMap) {
|
|
|
|
return;
|
|
|
|
}
|
2021-05-02 23:58:28 +09:00
|
|
|
const toUnicode = [],
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
glyphsUnicodeMap = getGlyphsUnicode();
|
2021-05-02 23:58:28 +09:00
|
|
|
for (const charCode in builtInEncoding) {
|
2021-04-21 00:12:19 +09:00
|
|
|
if (
|
|
|
|
properties.hasEncoding &&
|
|
|
|
properties.differences[charCode] !== undefined
|
|
|
|
) {
|
|
|
|
continue; // The font dictionary has an `Encoding`/`Differences` entry.
|
|
|
|
}
|
2021-05-02 23:58:28 +09:00
|
|
|
const glyphName = builtInEncoding[charCode];
|
|
|
|
const unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap);
|
For embedded Type1 fonts without included `ToUnicode`/`Encoding` data, attempt to improve text selection by using the `builtInEncoding` to amend the `toUnicode` map (issue 6901, issue 7182, issue 7217, bug 917796, bug 1242142)
Note that in order to prevent any possible issues, this patch does *not* try to amend the `toUnicode` data for Type1 fonts that contain either `ToUnicode` or `Encoding` entries in the font dictionary.
Fixes, or at least improves, issues/bugs such as e.g. 6658, 6901, 7182, 7217, bug 917796, bug 1242142.
2016-08-18 01:33:06 +09:00
|
|
|
if (unicode !== -1) {
|
|
|
|
toUnicode[charCode] = String.fromCharCode(unicode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
properties.toUnicode.amend(toUnicode);
|
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
class Glyph {
|
|
|
|
constructor(
|
2021-06-05 01:48:30 +09:00
|
|
|
originalCharCode,
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
fontChar,
|
|
|
|
unicode,
|
|
|
|
accent,
|
|
|
|
width,
|
|
|
|
vmetric,
|
|
|
|
operatorListId,
|
|
|
|
isSpace,
|
|
|
|
isInFont
|
|
|
|
) {
|
2021-06-05 01:48:30 +09:00
|
|
|
this.originalCharCode = originalCharCode;
|
2014-03-13 21:56:12 +09:00
|
|
|
this.fontChar = fontChar;
|
|
|
|
this.unicode = unicode;
|
|
|
|
this.accent = accent;
|
|
|
|
this.width = width;
|
|
|
|
this.vmetric = vmetric;
|
2014-05-20 06:27:54 +09:00
|
|
|
this.operatorListId = operatorListId;
|
2015-11-02 23:54:15 +09:00
|
|
|
this.isSpace = isSpace;
|
2016-02-25 03:48:02 +09:00
|
|
|
this.isInFont = isInFont;
|
2014-03-13 21:56:12 +09:00
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
matchesForCache(
|
2021-06-05 01:48:30 +09:00
|
|
|
originalCharCode,
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
fontChar,
|
|
|
|
unicode,
|
|
|
|
accent,
|
|
|
|
width,
|
|
|
|
vmetric,
|
|
|
|
operatorListId,
|
|
|
|
isSpace,
|
|
|
|
isInFont
|
|
|
|
) {
|
|
|
|
return (
|
2021-06-05 01:48:30 +09:00
|
|
|
this.originalCharCode === originalCharCode &&
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
this.fontChar === fontChar &&
|
|
|
|
this.unicode === unicode &&
|
|
|
|
this.accent === accent &&
|
|
|
|
this.width === width &&
|
|
|
|
this.vmetric === vmetric &&
|
|
|
|
this.operatorListId === operatorListId &&
|
|
|
|
this.isSpace === isSpace &&
|
|
|
|
this.isInFont === isInFont
|
|
|
|
);
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function int16(b0, b1) {
|
|
|
|
return (b0 << 8) + b1;
|
|
|
|
}
|
|
|
|
|
|
|
|
function writeSignedInt16(bytes, index, value) {
|
|
|
|
bytes[index + 1] = value;
|
|
|
|
bytes[index] = value >>> 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
function signedInt16(b0, b1) {
|
|
|
|
const value = (b0 << 8) + b1;
|
|
|
|
return value & (1 << 15) ? value - 0x10000 : value;
|
|
|
|
}
|
|
|
|
|
|
|
|
function int32(b0, b1, b2, b3) {
|
|
|
|
return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
|
|
|
|
}
|
|
|
|
|
|
|
|
function string16(value) {
|
|
|
|
return String.fromCharCode((value >> 8) & 0xff, value & 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
function safeString16(value) {
|
|
|
|
// clamp value to the 16-bit int range
|
|
|
|
if (value > 0x7fff) {
|
|
|
|
value = 0x7fff;
|
|
|
|
} else if (value < -0x8000) {
|
|
|
|
value = -0x8000;
|
|
|
|
}
|
|
|
|
return String.fromCharCode((value >> 8) & 0xff, value & 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
function isTrueTypeFile(file) {
|
|
|
|
const header = file.peekBytes(4);
|
|
|
|
return (
|
|
|
|
readUint32(header, 0) === 0x00010000 || bytesToString(header) === "true"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function isTrueTypeCollectionFile(file) {
|
|
|
|
const header = file.peekBytes(4);
|
|
|
|
return bytesToString(header) === "ttcf";
|
|
|
|
}
|
|
|
|
|
|
|
|
function isOpenTypeFile(file) {
|
|
|
|
const header = file.peekBytes(4);
|
|
|
|
return bytesToString(header) === "OTTO";
|
|
|
|
}
|
|
|
|
|
|
|
|
function isType1File(file) {
|
|
|
|
const header = file.peekBytes(2);
|
|
|
|
// All Type1 font programs must begin with the comment '%!' (0x25 + 0x21).
|
|
|
|
if (header[0] === 0x25 && header[1] === 0x21) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// ... obviously some fonts violate that part of the specification,
|
|
|
|
// please refer to the comment in |Type1Font| below (pfb file header).
|
|
|
|
if (header[0] === 0x80 && header[1] === 0x01) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compared to other font formats, the header in CFF files is not constant
|
|
|
|
* but contains version numbers. To reduce the possibility of misclassifying
|
|
|
|
* font files as CFF, it's recommended to check for other font formats first.
|
|
|
|
*/
|
|
|
|
function isCFFFile(file) {
|
|
|
|
const header = file.peekBytes(4);
|
|
|
|
if (
|
|
|
|
/* major version, [1, 255] */ header[0] >= 1 &&
|
|
|
|
/* minor version, [0, 255]; header[1] */
|
|
|
|
/* header size, [0, 255]; header[2] */
|
|
|
|
/* offset(0) size, [1, 4] */ header[3] >= 1 &&
|
|
|
|
header[3] <= 4
|
|
|
|
) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getFontFileType(file, { type, subtype, composite }) {
|
|
|
|
let fileType, fileSubtype;
|
|
|
|
|
|
|
|
if (isTrueTypeFile(file) || isTrueTypeCollectionFile(file)) {
|
|
|
|
if (composite) {
|
|
|
|
fileType = "CIDFontType2";
|
|
|
|
} else {
|
|
|
|
fileType = "TrueType";
|
|
|
|
}
|
|
|
|
} else if (isOpenTypeFile(file)) {
|
|
|
|
if (composite) {
|
|
|
|
fileType = "CIDFontType2";
|
|
|
|
} else {
|
|
|
|
fileType = "OpenType";
|
|
|
|
}
|
|
|
|
} else if (isType1File(file)) {
|
|
|
|
if (composite) {
|
|
|
|
fileType = "CIDFontType0";
|
|
|
|
} else {
|
|
|
|
fileType = type === "MMType1" ? "MMType1" : "Type1";
|
|
|
|
}
|
|
|
|
} else if (isCFFFile(file)) {
|
|
|
|
if (composite) {
|
|
|
|
fileType = "CIDFontType0";
|
|
|
|
fileSubtype = "CIDFontType0C";
|
|
|
|
} else {
|
|
|
|
fileType = type === "MMType1" ? "MMType1" : "Type1";
|
|
|
|
fileSubtype = "Type1C";
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
warn("getFontFileType: Unable to detect correct font file Type/Subtype.");
|
|
|
|
fileType = type;
|
|
|
|
fileSubtype = subtype;
|
|
|
|
}
|
|
|
|
|
|
|
|
return [fileType, fileSubtype];
|
|
|
|
}
|
|
|
|
|
|
|
|
function buildToFontChar(encoding, glyphsUnicodeMap, differences) {
|
|
|
|
const toFontChar = [];
|
|
|
|
let unicode;
|
|
|
|
for (let i = 0, ii = encoding.length; i < ii; i++) {
|
|
|
|
unicode = getUnicodeForGlyph(encoding[i], glyphsUnicodeMap);
|
|
|
|
if (unicode !== -1) {
|
|
|
|
toFontChar[i] = unicode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const charCode in differences) {
|
|
|
|
unicode = getUnicodeForGlyph(differences[charCode], glyphsUnicodeMap);
|
|
|
|
if (unicode !== -1) {
|
|
|
|
toFontChar[+charCode] = unicode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return toFontChar;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Rebuilds the char code to glyph ID map by moving all char codes to the
|
|
|
|
* private use area. This is done to avoid issues with various problematic
|
|
|
|
* unicode areas where either a glyph won't be drawn or is deformed by a
|
|
|
|
* shaper.
|
|
|
|
* @returns {Object} Two properties:
|
|
|
|
* 'toFontChar' - maps original char codes(the value that will be read
|
|
|
|
* from commands such as show text) to the char codes that will be used in the
|
|
|
|
* font that we build
|
|
|
|
* 'charCodeToGlyphId' - maps the new font char codes to glyph ids
|
|
|
|
*/
|
|
|
|
function adjustMapping(charCodeToGlyphId, hasGlyph, newGlyphZeroId) {
|
|
|
|
const newMap = Object.create(null);
|
|
|
|
const toFontChar = [];
|
|
|
|
let privateUseAreaIndex = 0;
|
|
|
|
let nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];
|
|
|
|
let privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];
|
|
|
|
for (let originalCharCode in charCodeToGlyphId) {
|
|
|
|
originalCharCode |= 0;
|
|
|
|
let glyphId = charCodeToGlyphId[originalCharCode];
|
|
|
|
// For missing glyphs don't create the mappings so the glyph isn't
|
|
|
|
// drawn.
|
|
|
|
if (!hasGlyph(glyphId)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (nextAvailableFontCharCode > privateUseOffetEnd) {
|
|
|
|
privateUseAreaIndex++;
|
|
|
|
if (privateUseAreaIndex >= PRIVATE_USE_AREAS.length) {
|
|
|
|
warn("Ran out of space in font private use area.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];
|
|
|
|
privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];
|
|
|
|
}
|
|
|
|
const fontCharCode = nextAvailableFontCharCode++;
|
|
|
|
if (glyphId === 0) {
|
|
|
|
glyphId = newGlyphZeroId;
|
|
|
|
}
|
|
|
|
|
|
|
|
newMap[fontCharCode] = glyphId;
|
|
|
|
toFontChar[originalCharCode] = fontCharCode;
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
toFontChar,
|
|
|
|
charCodeToGlyphId: newMap,
|
|
|
|
nextAvailableFontCharCode,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function getRanges(glyphs, numGlyphs) {
|
|
|
|
// Array.sort() sorts by characters, not numerically, so convert to an
|
|
|
|
// array of characters.
|
|
|
|
const codes = [];
|
|
|
|
for (const charCode in glyphs) {
|
|
|
|
// Remove an invalid glyph ID mappings to make OTS happy.
|
|
|
|
if (glyphs[charCode] >= numGlyphs) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
codes.push({ fontCharCode: charCode | 0, glyphId: glyphs[charCode] });
|
|
|
|
}
|
|
|
|
// Some fonts have zero glyphs and are used only for text selection, but
|
|
|
|
// there needs to be at least one to build a valid cmap table.
|
|
|
|
if (codes.length === 0) {
|
|
|
|
codes.push({ fontCharCode: 0, glyphId: 0 });
|
|
|
|
}
|
|
|
|
codes.sort(function fontGetRangesSort(a, b) {
|
|
|
|
return a.fontCharCode - b.fontCharCode;
|
|
|
|
});
|
|
|
|
|
|
|
|
// Split the sorted codes into ranges.
|
|
|
|
const ranges = [];
|
|
|
|
const length = codes.length;
|
|
|
|
for (let n = 0; n < length; ) {
|
|
|
|
const start = codes[n].fontCharCode;
|
|
|
|
const codeIndices = [codes[n].glyphId];
|
|
|
|
++n;
|
|
|
|
let end = start;
|
|
|
|
while (n < length && end + 1 === codes[n].fontCharCode) {
|
|
|
|
codeIndices.push(codes[n].glyphId);
|
|
|
|
++end;
|
|
|
|
++n;
|
|
|
|
if (end === 0xffff) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ranges.push([start, end, codeIndices]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ranges;
|
|
|
|
}
|
|
|
|
|
|
|
|
function createCmapTable(glyphs, numGlyphs) {
|
|
|
|
const ranges = getRanges(glyphs, numGlyphs);
|
|
|
|
const numTables = ranges[ranges.length - 1][1] > 0xffff ? 2 : 1;
|
|
|
|
let cmap =
|
|
|
|
"\x00\x00" + // version
|
|
|
|
string16(numTables) + // numTables
|
|
|
|
"\x00\x03" + // platformID
|
|
|
|
"\x00\x01" + // encodingID
|
|
|
|
string32(4 + numTables * 8); // start of the table record
|
|
|
|
|
|
|
|
let i, ii, j, jj;
|
|
|
|
for (i = ranges.length - 1; i >= 0; --i) {
|
|
|
|
if (ranges[i][0] <= 0xffff) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const bmpLength = i + 1;
|
|
|
|
|
|
|
|
if (ranges[i][0] < 0xffff && ranges[i][1] === 0xffff) {
|
|
|
|
ranges[i][1] = 0xfffe;
|
|
|
|
}
|
|
|
|
const trailingRangesCount = ranges[i][1] < 0xffff ? 1 : 0;
|
|
|
|
const segCount = bmpLength + trailingRangesCount;
|
|
|
|
const searchParams = OpenTypeFileBuilder.getSearchParams(segCount, 2);
|
|
|
|
|
|
|
|
// Fill up the 4 parallel arrays describing the segments.
|
|
|
|
let startCount = "";
|
|
|
|
let endCount = "";
|
|
|
|
let idDeltas = "";
|
|
|
|
let idRangeOffsets = "";
|
|
|
|
let glyphsIds = "";
|
|
|
|
let bias = 0;
|
|
|
|
|
|
|
|
let range, start, end, codes;
|
|
|
|
for (i = 0, ii = bmpLength; i < ii; i++) {
|
|
|
|
range = ranges[i];
|
|
|
|
start = range[0];
|
|
|
|
end = range[1];
|
|
|
|
startCount += string16(start);
|
|
|
|
endCount += string16(end);
|
|
|
|
codes = range[2];
|
|
|
|
let contiguous = true;
|
|
|
|
for (j = 1, jj = codes.length; j < jj; ++j) {
|
|
|
|
if (codes[j] !== codes[j - 1] + 1) {
|
|
|
|
contiguous = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!contiguous) {
|
|
|
|
const offset = (segCount - i) * 2 + bias * 2;
|
|
|
|
bias += end - start + 1;
|
|
|
|
|
|
|
|
idDeltas += string16(0);
|
|
|
|
idRangeOffsets += string16(offset);
|
|
|
|
|
|
|
|
for (j = 0, jj = codes.length; j < jj; ++j) {
|
|
|
|
glyphsIds += string16(codes[j]);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const startCode = codes[0];
|
|
|
|
|
|
|
|
idDeltas += string16((startCode - start) & 0xffff);
|
|
|
|
idRangeOffsets += string16(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (trailingRangesCount > 0) {
|
|
|
|
endCount += "\xFF\xFF";
|
|
|
|
startCount += "\xFF\xFF";
|
|
|
|
idDeltas += "\x00\x01";
|
|
|
|
idRangeOffsets += "\x00\x00";
|
|
|
|
}
|
|
|
|
|
|
|
|
const format314 =
|
|
|
|
"\x00\x00" + // language
|
|
|
|
string16(2 * segCount) +
|
|
|
|
string16(searchParams.range) +
|
|
|
|
string16(searchParams.entry) +
|
|
|
|
string16(searchParams.rangeShift) +
|
|
|
|
endCount +
|
|
|
|
"\x00\x00" +
|
|
|
|
startCount +
|
|
|
|
idDeltas +
|
|
|
|
idRangeOffsets +
|
|
|
|
glyphsIds;
|
|
|
|
|
|
|
|
let format31012 = "";
|
|
|
|
let header31012 = "";
|
|
|
|
if (numTables > 1) {
|
|
|
|
cmap +=
|
|
|
|
"\x00\x03" + // platformID
|
|
|
|
"\x00\x0A" + // encodingID
|
|
|
|
string32(4 + numTables * 8 + 4 + format314.length); // start of the table record
|
|
|
|
format31012 = "";
|
|
|
|
for (i = 0, ii = ranges.length; i < ii; i++) {
|
|
|
|
range = ranges[i];
|
|
|
|
start = range[0];
|
|
|
|
codes = range[2];
|
|
|
|
let code = codes[0];
|
|
|
|
for (j = 1, jj = codes.length; j < jj; ++j) {
|
|
|
|
if (codes[j] !== codes[j - 1] + 1) {
|
|
|
|
end = range[0] + j - 1;
|
|
|
|
format31012 +=
|
|
|
|
string32(start) + // startCharCode
|
|
|
|
string32(end) + // endCharCode
|
|
|
|
string32(code); // startGlyphID
|
|
|
|
start = end + 1;
|
|
|
|
code = codes[j];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
format31012 +=
|
|
|
|
string32(start) + // startCharCode
|
|
|
|
string32(range[1]) + // endCharCode
|
|
|
|
string32(code); // startGlyphID
|
|
|
|
}
|
|
|
|
header31012 =
|
|
|
|
"\x00\x0C" + // format
|
|
|
|
"\x00\x00" + // reserved
|
|
|
|
string32(format31012.length + 16) + // length
|
|
|
|
"\x00\x00\x00\x00" + // language
|
|
|
|
string32(format31012.length / 12); // nGroups
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
cmap +
|
|
|
|
"\x00\x04" + // format
|
|
|
|
string16(format314.length + 4) + // length
|
|
|
|
format314 +
|
|
|
|
header31012 +
|
|
|
|
format31012
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function validateOS2Table(os2, file) {
|
|
|
|
file.pos = (file.start || 0) + os2.offset;
|
|
|
|
const version = file.getUint16();
|
|
|
|
// TODO verify all OS/2 tables fields, but currently we validate only those
|
|
|
|
// that give us issues
|
|
|
|
file.skip(60); // skipping type, misc sizes, panose, unicode ranges
|
|
|
|
const selection = file.getUint16();
|
|
|
|
if (version < 4 && selection & 0x0300) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const firstChar = file.getUint16();
|
|
|
|
const lastChar = file.getUint16();
|
|
|
|
if (firstChar > lastChar) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
file.skip(6); // skipping sTypoAscender/Descender/LineGap
|
|
|
|
const usWinAscent = file.getUint16();
|
|
|
|
if (usWinAscent === 0) {
|
|
|
|
// makes font unreadable by windows
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// OS/2 appears to be valid, resetting some fields
|
|
|
|
os2.data[8] = os2.data[9] = 0; // IE rejects fonts if fsType != 0
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function createOS2Table(properties, charstrings, override) {
|
|
|
|
override = override || {
|
|
|
|
unitsPerEm: 0,
|
|
|
|
yMax: 0,
|
|
|
|
yMin: 0,
|
|
|
|
ascent: 0,
|
|
|
|
descent: 0,
|
2014-03-13 21:56:12 +09:00
|
|
|
};
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
let ulUnicodeRange1 = 0;
|
|
|
|
let ulUnicodeRange2 = 0;
|
|
|
|
let ulUnicodeRange3 = 0;
|
|
|
|
let ulUnicodeRange4 = 0;
|
|
|
|
|
|
|
|
let firstCharIndex = null;
|
|
|
|
let lastCharIndex = 0;
|
|
|
|
|
|
|
|
if (charstrings) {
|
|
|
|
for (let code in charstrings) {
|
|
|
|
code |= 0;
|
|
|
|
if (firstCharIndex > code || !firstCharIndex) {
|
|
|
|
firstCharIndex = code;
|
|
|
|
}
|
|
|
|
if (lastCharIndex < code) {
|
|
|
|
lastCharIndex = code;
|
|
|
|
}
|
|
|
|
|
|
|
|
const position = getUnicodeRangeFor(code);
|
|
|
|
if (position < 32) {
|
|
|
|
ulUnicodeRange1 |= 1 << position;
|
|
|
|
} else if (position < 64) {
|
|
|
|
ulUnicodeRange2 |= 1 << (position - 32);
|
|
|
|
} else if (position < 96) {
|
|
|
|
ulUnicodeRange3 |= 1 << (position - 64);
|
|
|
|
} else if (position < 123) {
|
|
|
|
ulUnicodeRange4 |= 1 << (position - 96);
|
|
|
|
} else {
|
|
|
|
throw new FormatError(
|
|
|
|
"Unicode ranges Bits > 123 are reserved for internal usage"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (lastCharIndex > 0xffff) {
|
|
|
|
// OS2 only supports a 16 bit int. The spec says if supplementary
|
|
|
|
// characters are used the field should just be set to 0xFFFF.
|
|
|
|
lastCharIndex = 0xffff;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// TODO
|
|
|
|
firstCharIndex = 0;
|
|
|
|
lastCharIndex = 255;
|
|
|
|
}
|
|
|
|
|
|
|
|
const bbox = properties.bbox || [0, 0, 0, 0];
|
|
|
|
const unitsPerEm =
|
|
|
|
override.unitsPerEm ||
|
|
|
|
1 / (properties.fontMatrix || FONT_IDENTITY_MATRIX)[0];
|
|
|
|
|
|
|
|
// if the font units differ to the PDF glyph space units
|
|
|
|
// then scale up the values
|
|
|
|
const scale = properties.ascentScaled
|
|
|
|
? 1.0
|
|
|
|
: unitsPerEm / PDF_GLYPH_SPACE_UNITS;
|
|
|
|
|
|
|
|
const typoAscent =
|
|
|
|
override.ascent || Math.round(scale * (properties.ascent || bbox[3]));
|
|
|
|
let typoDescent =
|
|
|
|
override.descent || Math.round(scale * (properties.descent || bbox[1]));
|
|
|
|
if (typoDescent > 0 && properties.descent > 0 && bbox[1] < 0) {
|
|
|
|
typoDescent = -typoDescent; // fixing incorrect descent
|
|
|
|
}
|
|
|
|
const winAscent = override.yMax || typoAscent;
|
|
|
|
const winDescent = -override.yMin || -typoDescent;
|
|
|
|
|
|
|
|
return (
|
|
|
|
"\x00\x03" + // version
|
|
|
|
"\x02\x24" + // xAvgCharWidth
|
|
|
|
"\x01\xF4" + // usWeightClass
|
|
|
|
"\x00\x05" + // usWidthClass
|
|
|
|
"\x00\x00" + // fstype (0 to let the font loads via font-face on IE)
|
|
|
|
"\x02\x8A" + // ySubscriptXSize
|
|
|
|
"\x02\xBB" + // ySubscriptYSize
|
|
|
|
"\x00\x00" + // ySubscriptXOffset
|
|
|
|
"\x00\x8C" + // ySubscriptYOffset
|
|
|
|
"\x02\x8A" + // ySuperScriptXSize
|
|
|
|
"\x02\xBB" + // ySuperScriptYSize
|
|
|
|
"\x00\x00" + // ySuperScriptXOffset
|
|
|
|
"\x01\xDF" + // ySuperScriptYOffset
|
|
|
|
"\x00\x31" + // yStrikeOutSize
|
|
|
|
"\x01\x02" + // yStrikeOutPosition
|
|
|
|
"\x00\x00" + // sFamilyClass
|
|
|
|
"\x00\x00\x06" +
|
|
|
|
String.fromCharCode(properties.fixedPitch ? 0x09 : 0x00) +
|
|
|
|
"\x00\x00\x00\x00\x00\x00" + // Panose
|
|
|
|
string32(ulUnicodeRange1) + // ulUnicodeRange1 (Bits 0-31)
|
|
|
|
string32(ulUnicodeRange2) + // ulUnicodeRange2 (Bits 32-63)
|
|
|
|
string32(ulUnicodeRange3) + // ulUnicodeRange3 (Bits 64-95)
|
|
|
|
string32(ulUnicodeRange4) + // ulUnicodeRange4 (Bits 96-127)
|
|
|
|
"\x2A\x32\x31\x2A" + // achVendID
|
|
|
|
string16(properties.italicAngle ? 1 : 0) + // fsSelection
|
|
|
|
string16(firstCharIndex || properties.firstChar) + // usFirstCharIndex
|
|
|
|
string16(lastCharIndex || properties.lastChar) + // usLastCharIndex
|
|
|
|
string16(typoAscent) + // sTypoAscender
|
|
|
|
string16(typoDescent) + // sTypoDescender
|
|
|
|
"\x00\x64" + // sTypoLineGap (7%-10% of the unitsPerEM value)
|
|
|
|
string16(winAscent) + // usWinAscent
|
|
|
|
string16(winDescent) + // usWinDescent
|
|
|
|
"\x00\x00\x00\x00" + // ulCodePageRange1 (Bits 0-31)
|
|
|
|
"\x00\x00\x00\x00" + // ulCodePageRange2 (Bits 32-63)
|
|
|
|
string16(properties.xHeight) + // sxHeight
|
|
|
|
string16(properties.capHeight) + // sCapHeight
|
|
|
|
string16(0) + // usDefaultChar
|
|
|
|
string16(firstCharIndex || properties.firstChar) + // usBreakChar
|
|
|
|
"\x00\x03"
|
|
|
|
); // usMaxContext
|
|
|
|
}
|
|
|
|
|
|
|
|
function createPostTable(properties) {
|
|
|
|
const angle = Math.floor(properties.italicAngle * 2 ** 16);
|
|
|
|
return (
|
|
|
|
"\x00\x03\x00\x00" + // Version number
|
|
|
|
string32(angle) + // italicAngle
|
|
|
|
"\x00\x00" + // underlinePosition
|
|
|
|
"\x00\x00" + // underlineThickness
|
|
|
|
string32(properties.fixedPitch) + // isFixedPitch
|
|
|
|
"\x00\x00\x00\x00" + // minMemType42
|
|
|
|
"\x00\x00\x00\x00" + // maxMemType42
|
|
|
|
"\x00\x00\x00\x00" + // minMemType1
|
|
|
|
"\x00\x00\x00\x00"
|
|
|
|
); // maxMemType1
|
|
|
|
}
|
|
|
|
|
2021-05-27 00:07:59 +09:00
|
|
|
function createPostscriptName(name) {
|
|
|
|
// See https://docs.microsoft.com/en-us/typography/opentype/spec/recom#name.
|
|
|
|
return name.replace(/[^\x21-\x7E]|[[\](){}<>/%]/g, "").slice(0, 63);
|
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function createNameTable(name, proto) {
|
|
|
|
if (!proto) {
|
|
|
|
proto = [[], []]; // no strings and unicode strings
|
|
|
|
}
|
|
|
|
|
|
|
|
const strings = [
|
|
|
|
proto[0][0] || "Original licence", // 0.Copyright
|
|
|
|
proto[0][1] || name, // 1.Font family
|
|
|
|
proto[0][2] || "Unknown", // 2.Font subfamily (font weight)
|
|
|
|
proto[0][3] || "uniqueID", // 3.Unique ID
|
|
|
|
proto[0][4] || name, // 4.Full font name
|
|
|
|
proto[0][5] || "Version 0.11", // 5.Version
|
2021-05-27 00:07:59 +09:00
|
|
|
proto[0][6] || createPostscriptName(name), // 6.Postscript name
|
2021-05-03 00:42:48 +09:00
|
|
|
proto[0][7] || "Unknown", // 7.Trademark
|
|
|
|
proto[0][8] || "Unknown", // 8.Manufacturer
|
|
|
|
proto[0][9] || "Unknown", // 9.Designer
|
|
|
|
];
|
|
|
|
|
|
|
|
// Mac want 1-byte per character strings while Windows want
|
|
|
|
// 2-bytes per character, so duplicate the names table
|
|
|
|
const stringsUnicode = [];
|
|
|
|
let i, ii, j, jj, str;
|
|
|
|
for (i = 0, ii = strings.length; i < ii; i++) {
|
|
|
|
str = proto[1][i] || strings[i];
|
|
|
|
|
|
|
|
const strBufUnicode = [];
|
|
|
|
for (j = 0, jj = str.length; j < jj; j++) {
|
|
|
|
strBufUnicode.push(string16(str.charCodeAt(j)));
|
|
|
|
}
|
|
|
|
stringsUnicode.push(strBufUnicode.join(""));
|
|
|
|
}
|
|
|
|
|
|
|
|
const names = [strings, stringsUnicode];
|
|
|
|
const platforms = ["\x00\x01", "\x00\x03"];
|
|
|
|
const encodings = ["\x00\x00", "\x00\x01"];
|
|
|
|
const languages = ["\x00\x00", "\x04\x09"];
|
|
|
|
|
|
|
|
const namesRecordCount = strings.length * platforms.length;
|
|
|
|
let nameTable =
|
|
|
|
"\x00\x00" + // format
|
|
|
|
string16(namesRecordCount) + // Number of names Record
|
|
|
|
string16(namesRecordCount * 12 + 6); // Storage
|
|
|
|
|
|
|
|
// Build the name records field
|
|
|
|
let strOffset = 0;
|
|
|
|
for (i = 0, ii = platforms.length; i < ii; i++) {
|
|
|
|
const strs = names[i];
|
|
|
|
for (j = 0, jj = strs.length; j < jj; j++) {
|
|
|
|
str = strs[j];
|
|
|
|
const nameRecord =
|
|
|
|
platforms[i] + // platform ID
|
|
|
|
encodings[i] + // encoding ID
|
|
|
|
languages[i] + // language ID
|
|
|
|
string16(j) + // name ID
|
|
|
|
string16(str.length) +
|
|
|
|
string16(strOffset);
|
|
|
|
nameTable += nameRecord;
|
|
|
|
strOffset += str.length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nameTable += strings.join("") + stringsUnicode.join("");
|
|
|
|
return nameTable;
|
|
|
|
}
|
2014-03-13 21:56:12 +09:00
|
|
|
|
2011-06-10 08:20:00 +09:00
|
|
|
/**
|
2011-06-13 08:38:05 +09:00
|
|
|
* 'Font' is the class the outside world should use, it encapsulate all the font
|
|
|
|
* decoding logics whatever type it is (assuming the font type is supported).
|
2011-06-04 00:54:40 +09:00
|
|
|
*/
|
2021-05-03 00:42:48 +09:00
|
|
|
class Font {
|
|
|
|
constructor(name, file, properties) {
|
2011-06-21 13:49:59 +09:00
|
|
|
this.name = name;
|
2021-05-03 00:42:48 +09:00
|
|
|
this.mimetype = null;
|
|
|
|
this.disableFontFace = false;
|
|
|
|
|
2012-09-14 00:09:46 +09:00
|
|
|
this.loadedName = properties.loadedName;
|
2014-05-20 06:27:54 +09:00
|
|
|
this.isType3Font = properties.isType3Font;
|
2016-02-25 03:48:02 +09:00
|
|
|
this.missingFile = false;
|
2021-03-26 17:28:18 +09:00
|
|
|
this.cssFontInfo = properties.cssFontInfo;
|
2011-06-20 07:46:58 +09:00
|
|
|
|
2021-05-26 20:07:00 +09:00
|
|
|
this._charsCache = Object.create(null);
|
|
|
|
this._glyphCache = Object.create(null);
|
2014-03-13 21:56:12 +09:00
|
|
|
|
2012-01-28 09:53:05 +09:00
|
|
|
this.isSerifFont = !!(properties.flags & FontFlags.Serif);
|
|
|
|
this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
|
2012-09-15 02:58:33 +09:00
|
|
|
this.isMonospace = !!(properties.flags & FontFlags.FixedPitch);
|
2011-09-07 07:29:08 +09:00
|
|
|
|
2021-05-02 23:58:28 +09:00
|
|
|
let type = properties.type;
|
|
|
|
let subtype = properties.subtype;
|
2011-10-29 10:38:31 +09:00
|
|
|
this.type = type;
|
2017-07-21 09:30:22 +09:00
|
|
|
this.subtype = subtype;
|
2011-10-29 10:38:31 +09:00
|
|
|
|
2020-01-13 03:47:13 +09:00
|
|
|
let fallbackName = "sans-serif";
|
|
|
|
if (this.isMonospace) {
|
|
|
|
fallbackName = "monospace";
|
|
|
|
} else if (this.isSerifFont) {
|
|
|
|
fallbackName = "serif";
|
|
|
|
}
|
|
|
|
this.fallbackName = fallbackName;
|
2011-10-29 10:38:31 +09:00
|
|
|
|
|
|
|
this.differences = properties.differences;
|
|
|
|
this.widths = properties.widths;
|
|
|
|
this.defaultWidth = properties.defaultWidth;
|
|
|
|
this.composite = properties.composite;
|
2014-02-12 03:27:09 +09:00
|
|
|
this.cMap = properties.cMap;
|
2020-08-27 23:04:17 +09:00
|
|
|
this.capHeight = properties.capHeight / PDF_GLYPH_SPACE_UNITS;
|
2014-01-09 04:50:52 +09:00
|
|
|
this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS;
|
|
|
|
this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS;
|
2011-10-04 08:36:01 +09:00
|
|
|
this.fontMatrix = properties.fontMatrix;
|
2015-04-03 22:49:06 +09:00
|
|
|
this.bbox = properties.bbox;
|
2017-07-21 09:30:22 +09:00
|
|
|
this.defaultEncoding = properties.defaultEncoding;
|
2014-02-12 03:27:09 +09:00
|
|
|
|
2016-02-29 01:20:29 +09:00
|
|
|
this.toUnicode = properties.toUnicode;
|
Build a fallback `ToUnicode` map for simple fonts (issue 8229)
In some fonts, the included `ToUnicode` data is incomplete causing text-selection to not work properly. For simple fonts that contain encoding data, we can manually build a `ToUnicode` map to attempt to improve things.
Please note that since we're currently using the `ToUnicode` data during glyph mapping, in an attempt to avoid rendering regressions, I purposely didn't want to amend to original `ToUnicode` data for this text-selection edge-case.
Instead, I opted for the current solution, which will (hopefully) give slightly better text-extraction results in PDF file with incomplete `ToUnicode` data.
According to the PDF specification, see [section 9.10.2](http://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PDF32000_2008.pdf#G8.1873172):
> A conforming reader can use these methods, in the priority given, to map a character code to a Unicode value.
> ...
Reading that paragraph literally, it doesn't seem too unreasonable to use *different* methods for different charcodes.
Fixes 8229.
2017-11-26 21:29:43 +09:00
|
|
|
this.fallbackToUnicode = properties.fallbackToUnicode || new ToUnicodeMap();
|
2014-02-12 03:27:09 +09:00
|
|
|
|
|
|
|
this.toFontChar = [];
|
|
|
|
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
if (properties.type === "Type3") {
|
2021-05-03 00:42:48 +09:00
|
|
|
for (let charCode = 0; charCode < 256; charCode++) {
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
this.toFontChar[charCode] =
|
|
|
|
this.differences[charCode] || properties.defaultEncoding[charCode];
|
2014-02-12 03:27:09 +09:00
|
|
|
}
|
2014-06-16 23:52:04 +09:00
|
|
|
this.fontType = FontType.TYPE3;
|
2011-10-04 08:36:01 +09:00
|
|
|
return;
|
2011-12-05 04:51:12 +09:00
|
|
|
}
|
2011-07-25 23:42:46 +09:00
|
|
|
|
2013-01-15 23:20:58 +09:00
|
|
|
this.cidEncoding = properties.cidEncoding;
|
2020-04-01 20:40:35 +09:00
|
|
|
this.vertical = !!properties.vertical;
|
2013-02-08 21:29:22 +09:00
|
|
|
if (this.vertical) {
|
|
|
|
this.vmetrics = properties.vmetrics;
|
|
|
|
this.defaultVMetrics = properties.defaultVMetrics;
|
|
|
|
}
|
2017-07-21 09:30:22 +09:00
|
|
|
|
2014-05-18 07:57:06 +09:00
|
|
|
if (!file || file.isEmpty) {
|
|
|
|
if (file) {
|
|
|
|
// Some bad PDF generators will include empty font files,
|
|
|
|
// attempting to recover by assuming that no file exists.
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
warn('Font file is empty in "' + name + '" (' + this.loadedName + ")");
|
2014-05-18 07:57:06 +09:00
|
|
|
}
|
2020-09-28 21:39:48 +09:00
|
|
|
this.fallbackToSystemFont(properties);
|
2011-06-21 09:35:14 +09:00
|
|
|
return;
|
|
|
|
}
|
2011-06-13 08:38:05 +09:00
|
|
|
|
2018-08-05 17:34:14 +09:00
|
|
|
// Parse the font file to determine the correct type/subtype, rather than
|
|
|
|
// relying on the (often incorrect) data in the font dictionary; (see e.g.
|
|
|
|
// issue6782.pdf, issue7598.pdf, and issue9949.pdf).
|
|
|
|
[type, subtype] = getFontFileType(file, properties);
|
|
|
|
|
|
|
|
if (type !== this.type || subtype !== this.subtype) {
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
info(
|
|
|
|
"Inconsistent font file Type/SubType, expected: " +
|
|
|
|
`${this.type}/${this.subtype} but found: ${type}/${subtype}.`
|
|
|
|
);
|
2017-06-10 01:26:57 +09:00
|
|
|
}
|
2012-08-30 06:11:56 +09:00
|
|
|
|
2021-05-03 00:40:08 +09:00
|
|
|
let data;
|
2017-07-21 09:30:22 +09:00
|
|
|
try {
|
|
|
|
switch (type) {
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
case "MMType1":
|
|
|
|
info("MMType1 font (" + name + "), falling back to Type1.");
|
|
|
|
/* falls through */
|
|
|
|
case "Type1":
|
|
|
|
case "CIDFontType0":
|
|
|
|
this.mimetype = "font/opentype";
|
|
|
|
|
2021-05-03 00:40:08 +09:00
|
|
|
const cff =
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
subtype === "Type1C" || subtype === "CIDFontType0C"
|
|
|
|
? new CFFFont(file, properties)
|
|
|
|
: new Type1Font(name, file, properties);
|
2011-06-14 11:35:46 +09:00
|
|
|
|
2017-07-21 09:30:22 +09:00
|
|
|
adjustWidths(properties);
|
2013-01-04 09:39:06 +09:00
|
|
|
|
2017-07-21 09:30:22 +09:00
|
|
|
// Wrap the CFF data inside an OTF font file
|
|
|
|
data = this.convert(name, cff, properties);
|
|
|
|
break;
|
2011-06-13 08:38:05 +09:00
|
|
|
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
case "OpenType":
|
|
|
|
case "TrueType":
|
|
|
|
case "CIDFontType2":
|
|
|
|
this.mimetype = "font/opentype";
|
2011-07-12 01:41:47 +09:00
|
|
|
|
2017-07-21 09:30:22 +09:00
|
|
|
// Repair the TrueType file. It is can be damaged in the point of
|
|
|
|
// view of the sanitizer
|
|
|
|
data = this.checkAndRepair(name, file, properties);
|
|
|
|
if (this.isOpenType) {
|
|
|
|
adjustWidths(properties);
|
2015-12-08 06:30:09 +09:00
|
|
|
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
type = "OpenType";
|
2017-07-21 09:30:22 +09:00
|
|
|
}
|
|
|
|
break;
|
2011-07-12 01:41:47 +09:00
|
|
|
|
2017-07-21 09:30:22 +09:00
|
|
|
default:
|
|
|
|
throw new FormatError(`Font ${type} is not supported`);
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
warn(e);
|
2020-09-28 21:39:48 +09:00
|
|
|
this.fallbackToSystemFont(properties);
|
2017-07-21 09:30:22 +09:00
|
|
|
return;
|
2011-06-16 08:30:47 +09:00
|
|
|
}
|
2011-07-16 00:58:09 +09:00
|
|
|
|
2011-06-27 03:55:27 +09:00
|
|
|
this.data = data;
|
2020-12-11 10:32:18 +09:00
|
|
|
this.fontType = getFontType(type, subtype, properties.isStandardFont);
|
2013-01-04 09:39:06 +09:00
|
|
|
|
|
|
|
// Transfer some properties again that could change during font conversion
|
2011-10-04 08:36:01 +09:00
|
|
|
this.fontMatrix = properties.fontMatrix;
|
2013-01-04 09:39:06 +09:00
|
|
|
this.widths = properties.widths;
|
|
|
|
this.defaultWidth = properties.defaultWidth;
|
For embedded Type1 fonts without included `ToUnicode`/`Encoding` data, attempt to improve text selection by using the `builtInEncoding` to amend the `toUnicode` map (issue 6901, issue 7182, issue 7217, bug 917796, bug 1242142)
Note that in order to prevent any possible issues, this patch does *not* try to amend the `toUnicode` data for Type1 fonts that contain either `ToUnicode` or `Encoding` entries in the font dictionary.
Fixes, or at least improves, issues/bugs such as e.g. 6658, 6901, 7182, 7217, bug 917796, bug 1242142.
2016-08-18 01:33:06 +09:00
|
|
|
this.toUnicode = properties.toUnicode;
|
2013-02-27 03:00:20 +09:00
|
|
|
this.seacMap = properties.seacMap;
|
2013-02-01 08:31:41 +09:00
|
|
|
}
|
2011-06-14 11:35:46 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
get renderer() {
|
|
|
|
const renderer = FontRendererFactory.create(this, SEAC_ANALYSIS_ENABLED);
|
|
|
|
return shadow(this, "renderer", renderer);
|
2013-02-01 08:31:41 +09:00
|
|
|
}
|
2011-07-02 14:54:28 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
exportData(extraProperties = false) {
|
|
|
|
const exportDataProperties = extraProperties
|
|
|
|
? [...EXPORT_DATA_PROPERTIES, ...EXPORT_DATA_EXTRA_PROPERTIES]
|
|
|
|
: EXPORT_DATA_PROPERTIES;
|
2011-06-21 13:49:59 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const data = Object.create(null);
|
|
|
|
let property, value;
|
|
|
|
for (property of exportDataProperties) {
|
|
|
|
value = this[property];
|
|
|
|
// Ignore properties that haven't been explicitly set.
|
|
|
|
if (value !== undefined) {
|
|
|
|
data[property] = value;
|
|
|
|
}
|
2020-01-13 03:47:13 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
return data;
|
2013-02-01 08:31:41 +09:00
|
|
|
}
|
2011-06-21 13:49:59 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
fallbackToSystemFont(properties) {
|
|
|
|
this.missingFile = true;
|
|
|
|
// The file data is not specified. Trying to fix the font name
|
|
|
|
// to be used with the canvas.font.
|
|
|
|
const name = this.name;
|
|
|
|
const type = this.type;
|
|
|
|
const subtype = this.subtype;
|
2020-12-11 10:32:18 +09:00
|
|
|
let fontName = normalizeFontName(name);
|
2021-05-03 00:42:48 +09:00
|
|
|
const stdFontMap = getStdFontMap(),
|
|
|
|
nonStdFontMap = getNonStdFontMap();
|
|
|
|
const isStandardFont = !!stdFontMap[fontName];
|
|
|
|
const isMappedToStandardFont = !!(
|
|
|
|
nonStdFontMap[fontName] && stdFontMap[nonStdFontMap[fontName]]
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
);
|
2014-04-25 01:48:18 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName;
|
|
|
|
this.bold = fontName.search(/bold/gi) !== -1;
|
|
|
|
this.italic =
|
|
|
|
fontName.search(/oblique/gi) !== -1 || fontName.search(/italic/gi) !== -1;
|
2017-12-15 08:23:56 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// Use 'name' instead of 'fontName' here because the original
|
|
|
|
// name ArialBlack for example will be replaced by Helvetica.
|
|
|
|
this.black = name.search(/Black/g) !== -1;
|
2016-01-06 10:07:21 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// Use 'name' instead of 'fontName' here because the original
|
|
|
|
// name ArialNarrow for example will be replaced by Helvetica.
|
|
|
|
const isNarrow = name.search(/Narrow/g) !== -1;
|
2015-02-24 00:01:08 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// if at least one width is present, remeasure all chars when exists
|
|
|
|
this.remeasure =
|
|
|
|
(!isStandardFont || isNarrow) && Object.keys(this.widths).length > 0;
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
if (
|
2021-05-03 00:42:48 +09:00
|
|
|
(isStandardFont || isMappedToStandardFont) &&
|
|
|
|
type === "CIDFontType2" &&
|
|
|
|
this.cidEncoding.startsWith("Identity-")
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
) {
|
2021-05-03 00:42:48 +09:00
|
|
|
const GlyphMapForStandardFonts = getGlyphMapForStandardFonts(),
|
|
|
|
cidToGidMap = properties.cidToGidMap;
|
|
|
|
// Standard fonts might be embedded as CID font without glyph mapping.
|
|
|
|
// Building one based on GlyphMapForStandardFonts.
|
|
|
|
const map = [];
|
|
|
|
for (const charCode in GlyphMapForStandardFonts) {
|
|
|
|
map[+charCode] = GlyphMapForStandardFonts[charCode];
|
|
|
|
}
|
|
|
|
if (/Arial-?Black/i.test(name)) {
|
2021-05-16 17:58:34 +09:00
|
|
|
const SupplementalGlyphMapForArialBlack =
|
|
|
|
getSupplementalGlyphMapForArialBlack();
|
2021-05-03 00:42:48 +09:00
|
|
|
for (const charCode in SupplementalGlyphMapForArialBlack) {
|
|
|
|
map[+charCode] = SupplementalGlyphMapForArialBlack[charCode];
|
|
|
|
}
|
|
|
|
} else if (/Calibri/i.test(name)) {
|
2021-05-16 17:58:34 +09:00
|
|
|
const SupplementalGlyphMapForCalibri =
|
|
|
|
getSupplementalGlyphMapForCalibri();
|
2021-05-03 00:42:48 +09:00
|
|
|
for (const charCode in SupplementalGlyphMapForCalibri) {
|
|
|
|
map[+charCode] = SupplementalGlyphMapForCalibri[charCode];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Always update the glyph mapping with the `cidToGidMap` when it exists
|
|
|
|
// (fixes issue12418_reduced.pdf).
|
|
|
|
if (cidToGidMap) {
|
|
|
|
for (const charCode in map) {
|
|
|
|
const cid = map[charCode];
|
|
|
|
if (cidToGidMap[cid] !== undefined) {
|
|
|
|
map[+charCode] = cidToGidMap[cid];
|
|
|
|
}
|
|
|
|
}
|
2018-08-05 17:34:14 +09:00
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const isIdentityUnicode = this.toUnicode instanceof IdentityToUnicodeMap;
|
|
|
|
if (!isIdentityUnicode) {
|
|
|
|
this.toUnicode.forEach(function (charCode, unicodeCharCode) {
|
|
|
|
map[+charCode] = unicodeCharCode;
|
|
|
|
});
|
2016-03-11 02:08:17 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
this.toFontChar = map;
|
|
|
|
this.toUnicode = new ToUnicodeMap(map);
|
|
|
|
} else if (/Symbol/i.test(fontName)) {
|
|
|
|
this.toFontChar = buildToFontChar(
|
|
|
|
SymbolSetEncoding,
|
|
|
|
getGlyphsUnicode(),
|
|
|
|
this.differences
|
|
|
|
);
|
|
|
|
} else if (/Dingbats/i.test(fontName)) {
|
|
|
|
if (/Wingdings/i.test(name)) {
|
|
|
|
warn("Non-embedded Wingdings font, falling back to ZapfDingbats.");
|
|
|
|
}
|
|
|
|
this.toFontChar = buildToFontChar(
|
|
|
|
ZapfDingbatsEncoding,
|
|
|
|
getDingbatsGlyphsUnicode(),
|
|
|
|
this.differences
|
|
|
|
);
|
|
|
|
} else if (isStandardFont) {
|
|
|
|
this.toFontChar = buildToFontChar(
|
|
|
|
this.defaultEncoding,
|
|
|
|
getGlyphsUnicode(),
|
|
|
|
this.differences
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
const glyphsUnicodeMap = getGlyphsUnicode();
|
|
|
|
const map = [];
|
|
|
|
this.toUnicode.forEach((charCode, unicodeCharCode) => {
|
|
|
|
if (!this.composite) {
|
|
|
|
const glyphName =
|
|
|
|
this.differences[charCode] || this.defaultEncoding[charCode];
|
|
|
|
const unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap);
|
|
|
|
if (unicode !== -1) {
|
|
|
|
unicodeCharCode = unicode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
map[+charCode] = unicodeCharCode;
|
|
|
|
});
|
|
|
|
|
|
|
|
// Attempt to improve the glyph mapping for (some) composite fonts that
|
|
|
|
// appear to lack meaningful ToUnicode data.
|
|
|
|
if (this.composite && this.toUnicode instanceof IdentityToUnicodeMap) {
|
|
|
|
if (/Verdana/i.test(name)) {
|
|
|
|
// Fixes issue11242_reduced.pdf
|
|
|
|
const GlyphMapForStandardFonts = getGlyphMapForStandardFonts();
|
|
|
|
for (const charCode in GlyphMapForStandardFonts) {
|
|
|
|
map[+charCode] = GlyphMapForStandardFonts[charCode];
|
|
|
|
}
|
|
|
|
}
|
2016-03-11 02:08:17 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
this.toFontChar = map;
|
2016-03-11 02:08:17 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
this.loadedName = fontName.split("-")[0];
|
2020-12-11 10:32:18 +09:00
|
|
|
this.fontType = getFontType(type, subtype, properties.isStandardFont);
|
2016-03-11 02:08:17 +09:00
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
checkAndRepair(name, font, properties) {
|
|
|
|
const VALID_TABLES = [
|
|
|
|
"OS/2",
|
|
|
|
"cmap",
|
|
|
|
"head",
|
|
|
|
"hhea",
|
|
|
|
"hmtx",
|
|
|
|
"maxp",
|
|
|
|
"name",
|
|
|
|
"post",
|
|
|
|
"loca",
|
|
|
|
"glyf",
|
|
|
|
"fpgm",
|
|
|
|
"prep",
|
|
|
|
"cvt ",
|
|
|
|
"CFF ",
|
|
|
|
];
|
2014-02-12 03:27:09 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function readTables(file, numTables) {
|
|
|
|
const tables = Object.create(null);
|
|
|
|
tables["OS/2"] = null;
|
|
|
|
tables.cmap = null;
|
|
|
|
tables.head = null;
|
|
|
|
tables.hhea = null;
|
|
|
|
tables.hmtx = null;
|
|
|
|
tables.maxp = null;
|
|
|
|
tables.name = null;
|
|
|
|
tables.post = null;
|
2014-02-12 03:27:09 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
for (let i = 0; i < numTables; i++) {
|
|
|
|
const table = readTableEntry(file);
|
|
|
|
if (!VALID_TABLES.includes(table.tag)) {
|
|
|
|
continue; // skipping table if it's not a required or optional table
|
2014-03-18 01:34:30 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
if (table.length === 0) {
|
|
|
|
continue; // skipping empty tables
|
|
|
|
}
|
|
|
|
tables[table.tag] = table;
|
2011-06-21 13:49:59 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
return tables;
|
2011-06-21 13:49:59 +09:00
|
|
|
}
|
2011-08-09 05:13:32 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function readTableEntry(file) {
|
|
|
|
const tag = file.getString(4);
|
2011-06-21 13:49:59 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const checksum = file.getInt32() >>> 0;
|
|
|
|
const offset = file.getInt32() >>> 0;
|
|
|
|
const length = file.getInt32() >>> 0;
|
2011-07-02 09:44:57 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// Read the table associated data
|
|
|
|
const previousPosition = file.pos;
|
|
|
|
file.pos = file.start ? file.start : 0;
|
|
|
|
file.skip(offset);
|
|
|
|
const data = file.getBytes(length);
|
|
|
|
file.pos = previousPosition;
|
|
|
|
|
|
|
|
if (tag === "head") {
|
|
|
|
// clearing checksum adjustment
|
|
|
|
data[8] = data[9] = data[10] = data[11] = 0;
|
|
|
|
data[17] |= 0x20; // Set font optimized for cleartype flag.
|
2017-02-03 20:58:08 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
|
|
|
|
return {
|
|
|
|
tag,
|
|
|
|
checksum,
|
|
|
|
length,
|
|
|
|
offset,
|
|
|
|
data,
|
|
|
|
};
|
2013-02-05 06:20:33 +09:00
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function readOpenTypeHeader(ttf) {
|
|
|
|
return {
|
|
|
|
version: ttf.getString(4),
|
|
|
|
numTables: ttf.getUint16(),
|
|
|
|
searchRange: ttf.getUint16(),
|
|
|
|
entrySelector: ttf.getUint16(),
|
|
|
|
rangeShift: ttf.getUint16(),
|
|
|
|
};
|
2013-03-19 21:37:31 +09:00
|
|
|
}
|
2011-06-21 13:49:59 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function readTrueTypeCollectionHeader(ttc) {
|
|
|
|
const ttcTag = ttc.getString(4);
|
|
|
|
assert(ttcTag === "ttcf", "Must be a TrueType Collection font.");
|
2021-05-02 23:58:28 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const majorVersion = ttc.getUint16();
|
|
|
|
const minorVersion = ttc.getUint16();
|
|
|
|
const numFonts = ttc.getInt32() >>> 0;
|
|
|
|
const offsetTable = [];
|
|
|
|
for (let i = 0; i < numFonts; i++) {
|
|
|
|
offsetTable.push(ttc.getInt32() >>> 0);
|
2013-03-19 21:37:31 +09:00
|
|
|
}
|
2011-09-18 07:13:22 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const header = {
|
|
|
|
ttcTag,
|
|
|
|
majorVersion,
|
|
|
|
minorVersion,
|
|
|
|
numFonts,
|
|
|
|
offsetTable,
|
|
|
|
};
|
|
|
|
switch (majorVersion) {
|
|
|
|
case 1:
|
|
|
|
return header;
|
|
|
|
case 2:
|
|
|
|
header.dsigTag = ttc.getInt32() >>> 0;
|
|
|
|
header.dsigLength = ttc.getInt32() >>> 0;
|
|
|
|
header.dsigOffset = ttc.getInt32() >>> 0;
|
|
|
|
return header;
|
|
|
|
}
|
|
|
|
throw new FormatError(
|
|
|
|
`Invalid TrueType Collection majorVersion: ${majorVersion}.`
|
|
|
|
);
|
2013-02-05 06:20:33 +09:00
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function readTrueTypeCollectionData(ttc, fontName) {
|
|
|
|
const { numFonts, offsetTable } = readTrueTypeCollectionHeader(ttc);
|
|
|
|
const fontNameParts = fontName.split("+");
|
|
|
|
let fallbackData;
|
2011-06-30 09:52:47 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
for (let i = 0; i < numFonts; i++) {
|
|
|
|
ttc.pos = (ttc.start || 0) + offsetTable[i];
|
|
|
|
const potentialHeader = readOpenTypeHeader(ttc);
|
|
|
|
const potentialTables = readTables(ttc, potentialHeader.numTables);
|
2011-10-29 10:38:31 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
if (!potentialTables.name) {
|
2017-06-29 05:51:31 +09:00
|
|
|
throw new FormatError(
|
2021-05-03 00:42:48 +09:00
|
|
|
'TrueType Collection font must contain a "name" table.'
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
);
|
2011-10-29 10:38:31 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
const nameTable = readNameTable(potentialTables.name);
|
|
|
|
|
|
|
|
for (let j = 0, jj = nameTable.length; j < jj; j++) {
|
|
|
|
for (let k = 0, kk = nameTable[j].length; k < kk; k++) {
|
|
|
|
const nameEntry =
|
|
|
|
nameTable[j][k] && nameTable[j][k].replace(/\s/g, "");
|
|
|
|
if (!nameEntry) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (nameEntry === fontName) {
|
|
|
|
return {
|
|
|
|
header: potentialHeader,
|
|
|
|
tables: potentialTables,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (fontNameParts.length < 2) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (const part of fontNameParts) {
|
|
|
|
if (nameEntry === part) {
|
|
|
|
fallbackData = {
|
|
|
|
name: part,
|
|
|
|
header: potentialHeader,
|
|
|
|
tables: potentialTables,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-06-30 09:52:47 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
if (fallbackData) {
|
|
|
|
warn(
|
|
|
|
`TrueType Collection does not contain "${fontName}" font, ` +
|
|
|
|
`falling back to "${fallbackData.name}" font instead.`
|
|
|
|
);
|
|
|
|
return {
|
|
|
|
header: fallbackData.header,
|
|
|
|
tables: fallbackData.tables,
|
|
|
|
};
|
2018-01-05 07:43:07 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
throw new FormatError(
|
|
|
|
`TrueType Collection does not contain "${fontName}" font.`
|
|
|
|
);
|
2011-06-30 09:52:47 +09:00
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
/**
|
|
|
|
* Read the appropriate subtable from the cmap according to 9.6.6.4 from
|
|
|
|
* PDF spec
|
|
|
|
*/
|
|
|
|
function readCmapTable(cmap, file, isSymbolicFont, hasEncoding) {
|
|
|
|
if (!cmap) {
|
|
|
|
warn("No cmap table available.");
|
|
|
|
return {
|
|
|
|
platformId: -1,
|
|
|
|
encodingId: -1,
|
|
|
|
mappings: [],
|
|
|
|
hasShortCmap: false,
|
|
|
|
};
|
2012-09-13 09:31:04 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
let segment;
|
|
|
|
let start = (file.start ? file.start : 0) + cmap.offset;
|
|
|
|
file.pos = start;
|
|
|
|
|
|
|
|
file.skip(2); // version
|
|
|
|
const numTables = file.getUint16();
|
|
|
|
|
|
|
|
let potentialTable;
|
|
|
|
let canBreak = false;
|
|
|
|
// There's an order of preference in terms of which cmap subtable to
|
|
|
|
// use:
|
|
|
|
// - non-symbolic fonts the preference is a 3,1 table then a 1,0 table
|
|
|
|
// - symbolic fonts the preference is a 3,0 table then a 1,0 table
|
|
|
|
// The following takes advantage of the fact that the tables are sorted
|
|
|
|
// to work.
|
|
|
|
for (let i = 0; i < numTables; i++) {
|
|
|
|
const platformId = file.getUint16();
|
|
|
|
const encodingId = file.getUint16();
|
|
|
|
const offset = file.getInt32() >>> 0;
|
|
|
|
let useTable = false;
|
2021-01-14 23:34:44 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// Sometimes there are multiple of the same type of table. Default
|
|
|
|
// to choosing the first table and skip the rest.
|
|
|
|
if (
|
|
|
|
potentialTable &&
|
|
|
|
potentialTable.platformId === platformId &&
|
|
|
|
potentialTable.encodingId === encodingId
|
|
|
|
) {
|
|
|
|
continue;
|
2020-09-28 21:39:48 +09:00
|
|
|
}
|
Add basic support for non-embedded Calibri fonts (issue 9195)
There's a number of issues with the fonts in the referenced PDF file. First of all, they contain broken `ToUnicode` data (`NUL` bytes all over the place). However even if you skip those, the `ToUnicode` data appears to contain nothing but a `IdentityH` CMap which won't help provide a proper glyph mapping.
The real issue actually turns out to be that the PDF file uses the "Calibri" font[1], but doesn't include any font files. Since that one isn't a standard font, and uses a fairly different CID to GID map compared to the standard fonts, we're not able to render the file even remotely correct.
To work around this, I'm thus proposing that we include a (incomplete) glyph map for Calibri, and fallback to the standard Helvetica font. Obviously this isn't going to look perfect, but it's really the best that we can hope to achieve given that the PDF file is missing the necessary font data.
Finally, please note that none of the PDF readers I've tried (Adobe Reader, PDFium in Chrome) were able to extract the text (which isn't very surprising, given the broken `ToUnicode` data).
Fixes 9195.
---
[1] According to Wikipedia, see https://en.wikipedia.org/wiki/Calibri, Calibri is (primarily) a Windows font.
2017-12-03 22:02:22 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
if (
|
|
|
|
platformId === 0 &&
|
|
|
|
(encodingId === /* Unicode Default */ 0 ||
|
|
|
|
encodingId === /* Unicode 1.1 */ 1 ||
|
|
|
|
encodingId === /* Unicode BMP */ 3)
|
|
|
|
) {
|
|
|
|
useTable = true;
|
|
|
|
// Continue the loop since there still may be a higher priority
|
|
|
|
// table.
|
|
|
|
} else if (platformId === 1 && encodingId === 0) {
|
|
|
|
useTable = true;
|
|
|
|
// Continue the loop since there still may be a higher priority
|
|
|
|
// table.
|
|
|
|
} else if (
|
|
|
|
platformId === 3 &&
|
|
|
|
encodingId === 1 &&
|
|
|
|
(hasEncoding || !potentialTable)
|
|
|
|
) {
|
|
|
|
useTable = true;
|
|
|
|
if (!isSymbolicFont) {
|
|
|
|
canBreak = true;
|
2017-07-21 09:30:22 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
} else if (isSymbolicFont && platformId === 3 && encodingId === 0) {
|
|
|
|
useTable = true;
|
|
|
|
canBreak = true;
|
|
|
|
}
|
2019-10-15 21:06:54 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
if (useTable) {
|
|
|
|
potentialTable = {
|
|
|
|
platformId,
|
|
|
|
encodingId,
|
|
|
|
offset,
|
|
|
|
};
|
2019-10-15 21:06:54 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
if (canBreak) {
|
|
|
|
break;
|
2017-12-15 08:23:56 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
if (potentialTable) {
|
|
|
|
file.pos = start + potentialTable.offset;
|
2013-02-01 08:31:41 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
if (!potentialTable || file.peekByte() === -1) {
|
|
|
|
warn("Could not find a preferred cmap table.");
|
2011-06-21 13:49:59 +09:00
|
|
|
return {
|
2021-05-03 00:42:48 +09:00
|
|
|
platformId: -1,
|
|
|
|
encodingId: -1,
|
|
|
|
mappings: [],
|
|
|
|
hasShortCmap: false,
|
2011-07-06 15:06:45 +09:00
|
|
|
};
|
2013-02-01 08:31:41 +09:00
|
|
|
}
|
2011-06-10 09:07:41 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const format = file.getUint16();
|
|
|
|
file.skip(2 + 2); // length + language
|
2017-12-15 08:23:56 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
let hasShortCmap = false;
|
|
|
|
const mappings = [];
|
|
|
|
let j, glyphId;
|
2017-12-15 08:23:56 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// TODO(mack): refactor this cmap subtable reading logic out
|
|
|
|
if (format === 0) {
|
|
|
|
for (j = 0; j < 256; j++) {
|
|
|
|
const index = file.getByte();
|
|
|
|
if (!index) {
|
|
|
|
continue;
|
2017-12-15 08:23:56 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
mappings.push({
|
|
|
|
charCode: j,
|
|
|
|
glyphId: index,
|
|
|
|
});
|
2017-12-15 08:23:56 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
hasShortCmap = true;
|
|
|
|
} else if (format === 4) {
|
|
|
|
// re-creating the table in format 4 since the encoding
|
|
|
|
// might be changed
|
|
|
|
const segCount = file.getUint16() >> 1;
|
|
|
|
file.skip(6); // skipping range fields
|
|
|
|
const segments = [];
|
|
|
|
let segIndex;
|
|
|
|
for (segIndex = 0; segIndex < segCount; segIndex++) {
|
|
|
|
segments.push({ end: file.getUint16() });
|
|
|
|
}
|
|
|
|
file.skip(2);
|
|
|
|
for (segIndex = 0; segIndex < segCount; segIndex++) {
|
|
|
|
segments[segIndex].start = file.getUint16();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (segIndex = 0; segIndex < segCount; segIndex++) {
|
|
|
|
segments[segIndex].delta = file.getUint16();
|
|
|
|
}
|
|
|
|
|
|
|
|
let offsetsCount = 0,
|
|
|
|
offsetIndex;
|
|
|
|
for (segIndex = 0; segIndex < segCount; segIndex++) {
|
|
|
|
segment = segments[segIndex];
|
|
|
|
const rangeOffset = file.getUint16();
|
|
|
|
if (!rangeOffset) {
|
|
|
|
segment.offsetIndex = -1;
|
2017-08-04 14:19:36 +09:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
offsetIndex = (rangeOffset >> 1) - (segCount - segIndex);
|
|
|
|
segment.offsetIndex = offsetIndex;
|
|
|
|
offsetsCount = Math.max(
|
|
|
|
offsetsCount,
|
|
|
|
offsetIndex + segment.end - segment.start + 1
|
|
|
|
);
|
2011-08-09 05:13:32 +09:00
|
|
|
}
|
2011-06-22 11:41:31 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const offsets = [];
|
|
|
|
for (j = 0; j < offsetsCount; j++) {
|
|
|
|
offsets.push(file.getUint16());
|
2011-09-06 22:12:33 +09:00
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
for (segIndex = 0; segIndex < segCount; segIndex++) {
|
|
|
|
segment = segments[segIndex];
|
|
|
|
start = segment.start;
|
|
|
|
const end = segment.end;
|
|
|
|
const delta = segment.delta;
|
|
|
|
offsetIndex = segment.offsetIndex;
|
2011-10-29 10:38:31 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
for (j = start; j <= end; j++) {
|
|
|
|
if (j === 0xffff) {
|
2013-01-31 03:01:32 +09:00
|
|
|
continue;
|
|
|
|
}
|
2011-10-29 10:38:31 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
glyphId = offsetIndex < 0 ? j : offsets[offsetIndex + j - start];
|
|
|
|
glyphId = (glyphId + delta) & 0xffff;
|
2013-01-31 03:01:32 +09:00
|
|
|
mappings.push({
|
2021-05-03 00:42:48 +09:00
|
|
|
charCode: j,
|
2017-04-27 19:58:44 +09:00
|
|
|
glyphId,
|
2013-01-31 03:01:32 +09:00
|
|
|
});
|
|
|
|
}
|
2011-06-22 11:41:31 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
} else if (format === 6) {
|
|
|
|
// Format 6 is a 2-bytes dense mapping, which means the font data
|
|
|
|
// lives glue together even if they are pretty far in the unicode
|
|
|
|
// table. (This looks weird, so I can have missed something), this
|
|
|
|
// works on Linux but seems to fails on Mac so let's rewrite the
|
|
|
|
// cmap table to a 3-1-4 style
|
|
|
|
const firstCode = file.getUint16();
|
|
|
|
const entryCount = file.getUint16();
|
|
|
|
|
|
|
|
for (j = 0; j < entryCount; j++) {
|
|
|
|
glyphId = file.getUint16();
|
|
|
|
const charCode = firstCode + j;
|
|
|
|
|
|
|
|
mappings.push({
|
|
|
|
charCode,
|
|
|
|
glyphId,
|
|
|
|
});
|
2013-11-02 06:30:28 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
} else {
|
|
|
|
warn("cmap table has unsupported format: " + format);
|
2013-01-31 03:01:32 +09:00
|
|
|
return {
|
2021-05-03 00:42:48 +09:00
|
|
|
platformId: -1,
|
|
|
|
encodingId: -1,
|
|
|
|
mappings: [],
|
|
|
|
hasShortCmap: false,
|
2013-01-31 03:01:32 +09:00
|
|
|
};
|
2013-02-01 08:31:41 +09:00
|
|
|
}
|
2011-06-22 11:41:31 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// removing duplicate entries
|
|
|
|
mappings.sort(function (a, b) {
|
|
|
|
return a.charCode - b.charCode;
|
|
|
|
});
|
|
|
|
for (let i = 1; i < mappings.length; i++) {
|
|
|
|
if (mappings[i - 1].charCode === mappings[i].charCode) {
|
|
|
|
mappings.splice(i, 1);
|
|
|
|
i--;
|
2011-09-09 23:37:56 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
2011-09-09 23:37:56 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
return {
|
|
|
|
platformId: potentialTable.platformId,
|
|
|
|
encodingId: potentialTable.encodingId,
|
|
|
|
mappings,
|
|
|
|
hasShortCmap,
|
|
|
|
};
|
|
|
|
}
|
2013-01-23 03:46:54 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function sanitizeMetrics(file, header, metrics, numGlyphs, dupFirstEntry) {
|
|
|
|
if (!header) {
|
|
|
|
if (metrics) {
|
|
|
|
metrics.data = null;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
file.pos = (file.start ? file.start : 0) + header.offset;
|
|
|
|
file.pos += 4; // version
|
|
|
|
file.pos += 2; // ascent
|
|
|
|
file.pos += 2; // descent
|
|
|
|
file.pos += 2; // linegap
|
|
|
|
file.pos += 2; // adv_width_max
|
|
|
|
file.pos += 2; // min_sb1
|
|
|
|
file.pos += 2; // min_sb2
|
|
|
|
file.pos += 2; // max_extent
|
|
|
|
file.pos += 2; // caret_slope_rise
|
|
|
|
file.pos += 2; // caret_slope_run
|
|
|
|
file.pos += 2; // caret_offset
|
|
|
|
file.pos += 8; // reserved
|
|
|
|
file.pos += 2; // format
|
|
|
|
let numOfMetrics = file.getUint16();
|
|
|
|
|
|
|
|
if (numOfMetrics > numGlyphs) {
|
|
|
|
info(
|
|
|
|
"The numOfMetrics (" +
|
|
|
|
numOfMetrics +
|
|
|
|
") should not be " +
|
|
|
|
"greater than the numGlyphs (" +
|
|
|
|
numGlyphs +
|
|
|
|
")"
|
|
|
|
);
|
|
|
|
// Reduce numOfMetrics if it is greater than numGlyphs
|
|
|
|
numOfMetrics = numGlyphs;
|
|
|
|
header.data[34] = (numOfMetrics & 0xff00) >> 8;
|
|
|
|
header.data[35] = numOfMetrics & 0x00ff;
|
2013-02-01 08:31:41 +09:00
|
|
|
}
|
2011-09-09 23:37:56 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const numOfSidebearings = numGlyphs - numOfMetrics;
|
|
|
|
const numMissing =
|
|
|
|
numOfSidebearings - ((metrics.length - numOfMetrics * 4) >> 1);
|
2012-02-13 12:11:44 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
if (numMissing > 0) {
|
|
|
|
// For each missing glyph, we set both the width and lsb to 0 (zero).
|
|
|
|
// Since we need to add two properties for each glyph, this explains
|
|
|
|
// the use of |numMissing * 2| when initializing the typed array.
|
|
|
|
const entries = new Uint8Array(metrics.length + numMissing * 2);
|
|
|
|
entries.set(metrics.data);
|
|
|
|
if (dupFirstEntry) {
|
|
|
|
// Set the sidebearing value of the duplicated glyph.
|
|
|
|
entries[metrics.length] = metrics.data[2];
|
|
|
|
entries[metrics.length + 1] = metrics.data[3];
|
2013-11-03 07:07:13 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
metrics.data = entries;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function sanitizeGlyph(
|
|
|
|
source,
|
|
|
|
sourceStart,
|
|
|
|
sourceEnd,
|
|
|
|
dest,
|
|
|
|
destStart,
|
|
|
|
hintsValid
|
|
|
|
) {
|
|
|
|
const glyphProfile = {
|
|
|
|
length: 0,
|
|
|
|
sizeOfInstructions: 0,
|
|
|
|
};
|
|
|
|
if (sourceEnd - sourceStart <= 12) {
|
|
|
|
// glyph with data less than 12 is invalid one
|
|
|
|
return glyphProfile;
|
|
|
|
}
|
|
|
|
const glyf = source.subarray(sourceStart, sourceEnd);
|
|
|
|
let contoursCount = signedInt16(glyf[0], glyf[1]);
|
|
|
|
if (contoursCount < 0) {
|
|
|
|
// OTS doesn't like contour count to be less than -1.
|
|
|
|
contoursCount = -1;
|
|
|
|
writeSignedInt16(glyf, 0, contoursCount);
|
|
|
|
// complex glyph, writing as is
|
2012-02-13 12:11:44 +09:00
|
|
|
dest.set(glyf, destStart);
|
2017-09-26 09:24:21 +09:00
|
|
|
glyphProfile.length = glyf.length;
|
|
|
|
return glyphProfile;
|
2012-02-13 12:11:44 +09:00
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
let i,
|
|
|
|
j = 10,
|
|
|
|
flagsCount = 0;
|
|
|
|
for (i = 0; i < contoursCount; i++) {
|
|
|
|
const endPoint = (glyf[j] << 8) | glyf[j + 1];
|
|
|
|
flagsCount = endPoint + 1;
|
|
|
|
j += 2;
|
|
|
|
}
|
|
|
|
// skipping instructions
|
|
|
|
const instructionsStart = j;
|
|
|
|
const instructionsLength = (glyf[j] << 8) | glyf[j + 1];
|
|
|
|
glyphProfile.sizeOfInstructions = instructionsLength;
|
|
|
|
j += 2 + instructionsLength;
|
|
|
|
const instructionsEnd = j;
|
|
|
|
// validating flags
|
|
|
|
let coordinatesLength = 0;
|
|
|
|
for (i = 0; i < flagsCount; i++) {
|
|
|
|
const flag = glyf[j++];
|
|
|
|
if (flag & 0xc0) {
|
|
|
|
// reserved flags must be zero, cleaning up
|
|
|
|
glyf[j - 1] = flag & 0x3f;
|
|
|
|
}
|
|
|
|
let xLength = 2;
|
|
|
|
if (flag & 2) {
|
|
|
|
xLength = 1;
|
|
|
|
} else if (flag & 16) {
|
|
|
|
xLength = 0;
|
|
|
|
}
|
|
|
|
let yLength = 2;
|
|
|
|
if (flag & 4) {
|
|
|
|
yLength = 1;
|
|
|
|
} else if (flag & 32) {
|
|
|
|
yLength = 0;
|
|
|
|
}
|
|
|
|
const xyLength = xLength + yLength;
|
|
|
|
coordinatesLength += xyLength;
|
|
|
|
if (flag & 8) {
|
|
|
|
const repeat = glyf[j++];
|
|
|
|
i += repeat;
|
|
|
|
coordinatesLength += repeat * xyLength;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// glyph without coordinates will be rejected
|
|
|
|
if (coordinatesLength === 0) {
|
|
|
|
return glyphProfile;
|
|
|
|
}
|
|
|
|
let glyphDataLength = j + coordinatesLength;
|
|
|
|
if (glyphDataLength > glyf.length) {
|
|
|
|
// not enough data for coordinates
|
|
|
|
return glyphProfile;
|
|
|
|
}
|
|
|
|
if (!hintsValid && instructionsLength > 0) {
|
|
|
|
dest.set(glyf.subarray(0, instructionsStart), destStart);
|
|
|
|
dest.set([0, 0], destStart + instructionsStart);
|
|
|
|
dest.set(
|
|
|
|
glyf.subarray(instructionsEnd, glyphDataLength),
|
|
|
|
destStart + instructionsStart + 2
|
|
|
|
);
|
|
|
|
glyphDataLength -= instructionsLength;
|
|
|
|
if (glyf.length - glyphDataLength > 3) {
|
|
|
|
glyphDataLength = (glyphDataLength + 3) & ~3;
|
2013-01-23 03:46:54 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
glyphProfile.length = glyphDataLength;
|
|
|
|
return glyphProfile;
|
2013-01-19 08:06:12 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
if (glyf.length - glyphDataLength > 3) {
|
|
|
|
// truncating and aligning to 4 bytes the long glyph data
|
|
|
|
glyphDataLength = (glyphDataLength + 3) & ~3;
|
|
|
|
dest.set(glyf.subarray(0, glyphDataLength), destStart);
|
|
|
|
glyphProfile.length = glyphDataLength;
|
|
|
|
return glyphProfile;
|
|
|
|
}
|
|
|
|
// glyph data is fine
|
|
|
|
dest.set(glyf, destStart);
|
|
|
|
glyphProfile.length = glyf.length;
|
|
|
|
return glyphProfile;
|
|
|
|
}
|
2013-01-19 08:06:12 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function sanitizeHead(head, numGlyphs, locaLength) {
|
|
|
|
const data = head.data;
|
|
|
|
|
|
|
|
// Validate version:
|
|
|
|
// Should always be 0x00010000
|
|
|
|
const version = int32(data[0], data[1], data[2], data[3]);
|
|
|
|
if (version >> 16 !== 1) {
|
|
|
|
info("Attempting to fix invalid version in head table: " + version);
|
|
|
|
data[0] = 0;
|
|
|
|
data[1] = 1;
|
|
|
|
data[2] = 0;
|
|
|
|
data[3] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const indexToLocFormat = int16(data[50], data[51]);
|
|
|
|
if (indexToLocFormat < 0 || indexToLocFormat > 1) {
|
|
|
|
info(
|
|
|
|
"Attempting to fix invalid indexToLocFormat in head table: " +
|
|
|
|
indexToLocFormat
|
|
|
|
);
|
2014-10-04 02:35:49 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// The value of indexToLocFormat should be 0 if the loca table
|
|
|
|
// consists of short offsets, and should be 1 if the loca table
|
|
|
|
// consists of long offsets.
|
|
|
|
//
|
|
|
|
// The number of entries in the loca table should be numGlyphs + 1.
|
|
|
|
//
|
|
|
|
// Using this information, we can work backwards to deduce if the
|
|
|
|
// size of each offset in the loca table, and thus figure out the
|
|
|
|
// appropriate value for indexToLocFormat.
|
|
|
|
|
|
|
|
const numGlyphsPlusOne = numGlyphs + 1;
|
|
|
|
if (locaLength === numGlyphsPlusOne << 1) {
|
|
|
|
// 0x0000 indicates the loca table consists of short offsets
|
|
|
|
data[50] = 0;
|
|
|
|
data[51] = 0;
|
|
|
|
} else if (locaLength === numGlyphsPlusOne << 2) {
|
|
|
|
// 0x0001 indicates the loca table consists of long offsets
|
|
|
|
data[50] = 0;
|
|
|
|
data[51] = 1;
|
|
|
|
} else {
|
|
|
|
throw new FormatError(
|
|
|
|
"Could not fix indexToLocFormat: " + indexToLocFormat
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
);
|
2011-09-19 11:25:05 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
|
|
|
}
|
2012-02-19 06:01:53 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function sanitizeGlyphLocations(
|
|
|
|
loca,
|
|
|
|
glyf,
|
|
|
|
numGlyphs,
|
|
|
|
isGlyphLocationsLong,
|
|
|
|
hintsValid,
|
|
|
|
dupFirstEntry,
|
|
|
|
maxSizeOfInstructions
|
|
|
|
) {
|
|
|
|
let itemSize, itemDecode, itemEncode;
|
|
|
|
if (isGlyphLocationsLong) {
|
|
|
|
itemSize = 4;
|
|
|
|
itemDecode = function fontItemDecodeLong(data, offset) {
|
|
|
|
return (
|
|
|
|
(data[offset] << 24) |
|
|
|
|
(data[offset + 1] << 16) |
|
|
|
|
(data[offset + 2] << 8) |
|
|
|
|
data[offset + 3]
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
);
|
2017-09-26 09:24:21 +09:00
|
|
|
};
|
2021-05-03 00:42:48 +09:00
|
|
|
itemEncode = function fontItemEncodeLong(data, offset, value) {
|
|
|
|
data[offset] = (value >>> 24) & 0xff;
|
|
|
|
data[offset + 1] = (value >> 16) & 0xff;
|
|
|
|
data[offset + 2] = (value >> 8) & 0xff;
|
|
|
|
data[offset + 3] = value & 0xff;
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
itemSize = 2;
|
|
|
|
itemDecode = function fontItemDecode(data, offset) {
|
|
|
|
return (data[offset] << 9) | (data[offset + 1] << 1);
|
|
|
|
};
|
|
|
|
itemEncode = function fontItemEncode(data, offset, value) {
|
|
|
|
data[offset] = (value >> 9) & 0xff;
|
|
|
|
data[offset + 1] = (value >> 1) & 0xff;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
// The first glyph is duplicated.
|
|
|
|
const numGlyphsOut = dupFirstEntry ? numGlyphs + 1 : numGlyphs;
|
|
|
|
const locaDataSize = itemSize * (1 + numGlyphsOut);
|
|
|
|
// Resize loca table to account for duplicated glyph.
|
|
|
|
const locaData = new Uint8Array(locaDataSize);
|
|
|
|
locaData.set(loca.data.subarray(0, locaDataSize));
|
|
|
|
loca.data = locaData;
|
|
|
|
// removing the invalid glyphs
|
|
|
|
const oldGlyfData = glyf.data;
|
|
|
|
const oldGlyfDataLength = oldGlyfData.length;
|
|
|
|
const newGlyfData = new Uint8Array(oldGlyfDataLength);
|
|
|
|
|
|
|
|
// The spec says the offsets should be in ascending order, however
|
|
|
|
// this is not true for some fonts or they use the offset of 0 to mark a
|
|
|
|
// glyph as missing. OTS requires the offsets to be in order and not to
|
|
|
|
// be zero, so we must sort and rebuild the loca table and potentially
|
|
|
|
// re-arrange the glyf data.
|
|
|
|
let i, j;
|
|
|
|
const locaEntries = [];
|
|
|
|
// There are numGlyphs + 1 loca table entries.
|
|
|
|
for (i = 0, j = 0; i < numGlyphs + 1; i++, j += itemSize) {
|
|
|
|
let offset = itemDecode(locaData, j);
|
|
|
|
if (offset > oldGlyfDataLength) {
|
|
|
|
offset = oldGlyfDataLength;
|
|
|
|
}
|
|
|
|
locaEntries.push({
|
|
|
|
index: i,
|
|
|
|
offset,
|
|
|
|
endOffset: 0,
|
|
|
|
});
|
2011-09-19 11:25:05 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
locaEntries.sort((a, b) => {
|
|
|
|
return a.offset - b.offset;
|
|
|
|
});
|
|
|
|
// Now the offsets are sorted, calculate the end offset of each glyph.
|
|
|
|
// The last loca entry's endOffset is not calculated since it's the end
|
|
|
|
// of the data and will be stored on the previous entry's endOffset.
|
|
|
|
for (i = 0; i < numGlyphs; i++) {
|
|
|
|
locaEntries[i].endOffset = locaEntries[i + 1].offset;
|
|
|
|
}
|
|
|
|
// Re-sort so glyphs aren't out of order.
|
|
|
|
locaEntries.sort((a, b) => {
|
|
|
|
return a.index - b.index;
|
|
|
|
});
|
|
|
|
|
|
|
|
const missingGlyphs = Object.create(null);
|
|
|
|
let writeOffset = 0;
|
|
|
|
itemEncode(locaData, 0, writeOffset);
|
|
|
|
for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
|
|
|
|
const glyphProfile = sanitizeGlyph(
|
|
|
|
oldGlyfData,
|
|
|
|
locaEntries[i].offset,
|
|
|
|
locaEntries[i].endOffset,
|
|
|
|
newGlyfData,
|
|
|
|
writeOffset,
|
|
|
|
hintsValid
|
|
|
|
);
|
|
|
|
const newLength = glyphProfile.length;
|
|
|
|
if (newLength === 0) {
|
|
|
|
missingGlyphs[i] = true;
|
|
|
|
}
|
|
|
|
if (glyphProfile.sizeOfInstructions > maxSizeOfInstructions) {
|
|
|
|
maxSizeOfInstructions = glyphProfile.sizeOfInstructions;
|
|
|
|
}
|
|
|
|
writeOffset += newLength;
|
|
|
|
itemEncode(locaData, j, writeOffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (writeOffset === 0) {
|
|
|
|
// glyf table cannot be empty -- redoing the glyf and loca tables
|
|
|
|
// to have single glyph with one point
|
|
|
|
const simpleGlyph = new Uint8Array([
|
2021-05-16 17:58:34 +09:00
|
|
|
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0,
|
2021-05-03 00:42:48 +09:00
|
|
|
]);
|
|
|
|
for (i = 0, j = itemSize; i < numGlyphsOut; i++, j += itemSize) {
|
|
|
|
itemEncode(locaData, j, simpleGlyph.length);
|
|
|
|
}
|
|
|
|
glyf.data = simpleGlyph;
|
|
|
|
} else if (dupFirstEntry) {
|
|
|
|
// Browsers will not display a glyph at position 0. Typically glyph 0
|
|
|
|
// is notdef, but a number of fonts put a valid glyph there so it must
|
|
|
|
// be duplicated and appended.
|
|
|
|
const firstEntryLength = itemDecode(locaData, itemSize);
|
|
|
|
if (newGlyfData.length > firstEntryLength + writeOffset) {
|
|
|
|
glyf.data = newGlyfData.subarray(0, firstEntryLength + writeOffset);
|
|
|
|
} else {
|
|
|
|
glyf.data = new Uint8Array(firstEntryLength + writeOffset);
|
|
|
|
glyf.data.set(newGlyfData.subarray(0, writeOffset));
|
|
|
|
}
|
|
|
|
glyf.data.set(newGlyfData.subarray(0, firstEntryLength), writeOffset);
|
|
|
|
itemEncode(
|
|
|
|
loca.data,
|
|
|
|
locaData.length - itemSize,
|
|
|
|
writeOffset + firstEntryLength
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
glyf.data = newGlyfData.subarray(0, writeOffset);
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
missingGlyphs,
|
|
|
|
maxSizeOfInstructions,
|
|
|
|
};
|
|
|
|
}
|
2011-09-19 11:25:05 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function readPostScriptTable(post, propertiesObj, maxpNumGlyphs) {
|
|
|
|
const start = (font.start ? font.start : 0) + post.offset;
|
|
|
|
font.pos = start;
|
2011-10-29 10:38:31 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const length = post.length,
|
|
|
|
end = start + length;
|
|
|
|
const version = font.getInt32();
|
|
|
|
// skip rest to the tables
|
|
|
|
font.skip(28);
|
2011-10-29 10:38:31 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
let glyphNames;
|
|
|
|
let valid = true;
|
|
|
|
let i;
|
2014-04-08 06:42:54 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
switch (version) {
|
|
|
|
case 0x00010000:
|
|
|
|
glyphNames = MacStandardGlyphOrdering;
|
|
|
|
break;
|
|
|
|
case 0x00020000:
|
|
|
|
const numGlyphs = font.getUint16();
|
|
|
|
if (numGlyphs !== maxpNumGlyphs) {
|
|
|
|
valid = false;
|
2011-10-29 10:38:31 +09:00
|
|
|
break;
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
|
|
|
const glyphNameIndexes = [];
|
|
|
|
for (i = 0; i < numGlyphs; ++i) {
|
|
|
|
const index = font.getUint16();
|
|
|
|
if (index >= 32768) {
|
2012-11-15 07:17:06 +09:00
|
|
|
valid = false;
|
|
|
|
break;
|
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
glyphNameIndexes.push(index);
|
|
|
|
}
|
|
|
|
if (!valid) {
|
2011-10-29 10:38:31 +09:00
|
|
|
break;
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
|
|
|
const customNames = [],
|
|
|
|
strBuf = [];
|
|
|
|
while (font.pos < end) {
|
|
|
|
const stringLength = font.getByte();
|
|
|
|
strBuf.length = stringLength;
|
|
|
|
for (i = 0; i < stringLength; ++i) {
|
|
|
|
strBuf[i] = String.fromCharCode(font.getByte());
|
2015-04-02 23:26:14 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
customNames.push(strBuf.join(""));
|
2012-11-08 09:24:13 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
glyphNames = [];
|
|
|
|
for (i = 0; i < numGlyphs; ++i) {
|
|
|
|
const j = glyphNameIndexes[i];
|
|
|
|
if (j < 258) {
|
|
|
|
glyphNames.push(MacStandardGlyphOrdering[j]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
glyphNames.push(customNames[j - 258]);
|
2016-03-02 05:39:33 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
break;
|
|
|
|
case 0x00030000:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
warn("Unknown/unsupported post table version " + version);
|
|
|
|
valid = false;
|
|
|
|
if (propertiesObj.defaultEncoding) {
|
|
|
|
glyphNames = propertiesObj.defaultEncoding;
|
2012-11-08 09:24:13 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
propertiesObj.glyphNames = glyphNames;
|
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
function readNameTable(nameTable) {
|
|
|
|
const start = (font.start ? font.start : 0) + nameTable.offset;
|
|
|
|
font.pos = start;
|
|
|
|
|
|
|
|
const names = [[], []];
|
|
|
|
const length = nameTable.length,
|
|
|
|
end = start + length;
|
|
|
|
const format = font.getUint16();
|
|
|
|
const FORMAT_0_HEADER_LENGTH = 6;
|
|
|
|
if (format !== 0 || length < FORMAT_0_HEADER_LENGTH) {
|
|
|
|
// unsupported name table format or table "too" small
|
|
|
|
return names;
|
|
|
|
}
|
|
|
|
const numRecords = font.getUint16();
|
|
|
|
const stringsStart = font.getUint16();
|
|
|
|
const records = [];
|
|
|
|
const NAME_RECORD_LENGTH = 12;
|
|
|
|
let i, ii;
|
|
|
|
|
|
|
|
for (i = 0; i < numRecords && font.pos + NAME_RECORD_LENGTH <= end; i++) {
|
|
|
|
const r = {
|
|
|
|
platform: font.getUint16(),
|
|
|
|
encoding: font.getUint16(),
|
|
|
|
language: font.getUint16(),
|
|
|
|
name: font.getUint16(),
|
|
|
|
length: font.getUint16(),
|
|
|
|
offset: font.getUint16(),
|
|
|
|
};
|
|
|
|
// using only Macintosh and Windows platform/encoding names
|
|
|
|
if (
|
|
|
|
(r.platform === 1 && r.encoding === 0 && r.language === 0) ||
|
|
|
|
(r.platform === 3 && r.encoding === 1 && r.language === 0x409)
|
|
|
|
) {
|
|
|
|
records.push(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0, ii = records.length; i < ii; i++) {
|
|
|
|
const record = records[i];
|
|
|
|
if (record.length <= 0) {
|
|
|
|
continue; // Nothing to process, ignoring.
|
|
|
|
}
|
|
|
|
const pos = start + stringsStart + record.offset;
|
|
|
|
if (pos + record.length > end) {
|
|
|
|
continue; // outside of name table, ignoring
|
|
|
|
}
|
|
|
|
font.pos = pos;
|
|
|
|
const nameIndex = record.name;
|
|
|
|
if (record.encoding) {
|
|
|
|
// unicode
|
|
|
|
let str = "";
|
|
|
|
for (let j = 0, jj = record.length; j < jj; j += 2) {
|
|
|
|
str += String.fromCharCode(font.getUint16());
|
2012-11-08 09:24:13 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
names[1][nameIndex] = str;
|
|
|
|
} else {
|
|
|
|
names[0][nameIndex] = font.getString(record.length);
|
2012-11-08 09:24:13 +09:00
|
|
|
}
|
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
return names;
|
|
|
|
}
|
2012-11-08 09:24:13 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// prettier-ignore
|
|
|
|
const TTOpsStackDeltas = [
|
2021-05-19 18:24:38 +09:00
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, -2, 0, 0, -2, -5,
|
|
|
|
-1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, -1, -1,
|
|
|
|
1, -1, -999, 0, 1, 0, -1, -2, 0, -1, -2, -1, -1, 0, -1, -1,
|
|
|
|
0, 0, -999, -999, -1, -1, -1, -1, -2, -999, -2, -2, -999, 0, -2, -2,
|
|
|
|
0, 0, -2, 0, -2, 0, 0, 0, -2, -1, -1, 1, 1, 0, 0, -1,
|
|
|
|
-1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, 0, -999, -1, -1,
|
|
|
|
-1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
-2, -999, -999, -999, -999, -999, -1, -1, -2, -2, 0, 0, 0, 0, -1, -1,
|
|
|
|
-999, -2, -2, 0, 0, -1, -2, -2, 0, 0, 0, -1, -1, -1, -2];
|
2021-05-03 00:42:48 +09:00
|
|
|
// 0xC0-DF == -1 and 0xE0-FF == -2
|
|
|
|
|
|
|
|
function sanitizeTTProgram(table, ttContext) {
|
|
|
|
let data = table.data;
|
|
|
|
let i = 0,
|
|
|
|
j,
|
|
|
|
n,
|
|
|
|
b,
|
|
|
|
funcId,
|
|
|
|
pc,
|
|
|
|
lastEndf = 0,
|
|
|
|
lastDeff = 0;
|
|
|
|
const stack = [];
|
|
|
|
const callstack = [];
|
|
|
|
const functionsCalled = [];
|
|
|
|
let tooComplexToFollowFunctions = ttContext.tooComplexToFollowFunctions;
|
|
|
|
let inFDEF = false,
|
|
|
|
ifLevel = 0,
|
|
|
|
inELSE = 0;
|
|
|
|
for (let ii = data.length; i < ii; ) {
|
|
|
|
const op = data[i++];
|
|
|
|
// The TrueType instruction set docs can be found at
|
|
|
|
// https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html
|
|
|
|
if (op === 0x40) {
|
|
|
|
// NPUSHB - pushes n bytes
|
|
|
|
n = data[i++];
|
|
|
|
if (inFDEF || inELSE) {
|
|
|
|
i += n;
|
|
|
|
} else {
|
|
|
|
for (j = 0; j < n; j++) {
|
|
|
|
stack.push(data[i++]);
|
2012-11-08 02:32:55 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
|
|
|
} else if (op === 0x41) {
|
|
|
|
// NPUSHW - pushes n words
|
|
|
|
n = data[i++];
|
|
|
|
if (inFDEF || inELSE) {
|
|
|
|
i += n * 2;
|
|
|
|
} else {
|
|
|
|
for (j = 0; j < n; j++) {
|
|
|
|
b = data[i++];
|
|
|
|
stack.push((b << 8) | data[i++]);
|
2012-11-08 02:32:55 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
|
|
|
} else if ((op & 0xf8) === 0xb0) {
|
|
|
|
// PUSHB - pushes bytes
|
|
|
|
n = op - 0xb0 + 1;
|
|
|
|
if (inFDEF || inELSE) {
|
|
|
|
i += n;
|
|
|
|
} else {
|
|
|
|
for (j = 0; j < n; j++) {
|
|
|
|
stack.push(data[i++]);
|
2012-11-08 02:32:55 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
|
|
|
} else if ((op & 0xf8) === 0xb8) {
|
|
|
|
// PUSHW - pushes words
|
|
|
|
n = op - 0xb8 + 1;
|
|
|
|
if (inFDEF || inELSE) {
|
|
|
|
i += n * 2;
|
|
|
|
} else {
|
|
|
|
for (j = 0; j < n; j++) {
|
|
|
|
b = data[i++];
|
|
|
|
stack.push((b << 8) | data[i++]);
|
2012-11-08 02:32:55 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
|
|
|
} else if (op === 0x2b && !tooComplexToFollowFunctions) {
|
|
|
|
// CALL
|
|
|
|
if (!inFDEF && !inELSE) {
|
|
|
|
// collecting information about which functions are used
|
|
|
|
funcId = stack[stack.length - 1];
|
|
|
|
if (isNaN(funcId)) {
|
|
|
|
info("TT: CALL empty stack (or invalid entry).");
|
2013-03-23 18:08:18 +09:00
|
|
|
} else {
|
2021-05-03 00:42:48 +09:00
|
|
|
ttContext.functionsUsed[funcId] = true;
|
|
|
|
if (funcId in ttContext.functionsStackDeltas) {
|
|
|
|
const newStackLength =
|
|
|
|
stack.length + ttContext.functionsStackDeltas[funcId];
|
|
|
|
if (newStackLength < 0) {
|
|
|
|
warn("TT: CALL invalid functions stack delta.");
|
|
|
|
ttContext.hintsValid = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
stack.length = newStackLength;
|
|
|
|
} else if (
|
|
|
|
funcId in ttContext.functionsDefined &&
|
|
|
|
!functionsCalled.includes(funcId)
|
|
|
|
) {
|
|
|
|
callstack.push({ data, i, stackTop: stack.length - 1 });
|
|
|
|
functionsCalled.push(funcId);
|
|
|
|
pc = ttContext.functionsDefined[funcId];
|
|
|
|
if (!pc) {
|
|
|
|
warn("TT: CALL non-existent function");
|
|
|
|
ttContext.hintsValid = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
data = pc.data;
|
|
|
|
i = pc.i;
|
2013-10-31 00:54:19 +09:00
|
|
|
}
|
2013-03-23 18:08:18 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
|
|
|
} else if (op === 0x2c && !tooComplexToFollowFunctions) {
|
|
|
|
// FDEF
|
|
|
|
if (inFDEF || inELSE) {
|
|
|
|
warn("TT: nested FDEFs not allowed");
|
|
|
|
tooComplexToFollowFunctions = true;
|
|
|
|
}
|
|
|
|
inFDEF = true;
|
|
|
|
// collecting information about which functions are defined
|
|
|
|
lastDeff = i;
|
|
|
|
funcId = stack.pop();
|
|
|
|
ttContext.functionsDefined[funcId] = { data, i };
|
|
|
|
} else if (op === 0x2d) {
|
|
|
|
// ENDF - end of function
|
|
|
|
if (inFDEF) {
|
|
|
|
inFDEF = false;
|
|
|
|
lastEndf = i;
|
|
|
|
} else {
|
|
|
|
pc = callstack.pop();
|
|
|
|
if (!pc) {
|
|
|
|
warn("TT: ENDF bad stack");
|
|
|
|
ttContext.hintsValid = false;
|
|
|
|
return;
|
2014-03-13 10:19:04 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
funcId = functionsCalled.pop();
|
|
|
|
data = pc.data;
|
|
|
|
i = pc.i;
|
|
|
|
ttContext.functionsStackDeltas[funcId] = stack.length - pc.stackTop;
|
2012-11-08 02:32:55 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
} else if (op === 0x89) {
|
|
|
|
// IDEF - instruction definition
|
|
|
|
if (inFDEF || inELSE) {
|
|
|
|
warn("TT: nested IDEFs not allowed");
|
|
|
|
tooComplexToFollowFunctions = true;
|
|
|
|
}
|
|
|
|
inFDEF = true;
|
|
|
|
// recording it as a function to track ENDF
|
|
|
|
lastDeff = i;
|
|
|
|
} else if (op === 0x58) {
|
|
|
|
// IF
|
|
|
|
++ifLevel;
|
|
|
|
} else if (op === 0x1b) {
|
|
|
|
// ELSE
|
|
|
|
inELSE = ifLevel;
|
|
|
|
} else if (op === 0x59) {
|
|
|
|
// EIF
|
|
|
|
if (inELSE === ifLevel) {
|
|
|
|
inELSE = 0;
|
|
|
|
}
|
|
|
|
--ifLevel;
|
|
|
|
} else if (op === 0x1c) {
|
|
|
|
// JMPR
|
2013-03-23 18:08:18 +09:00
|
|
|
if (!inFDEF && !inELSE) {
|
2021-05-03 00:42:48 +09:00
|
|
|
const offset = stack[stack.length - 1];
|
|
|
|
// only jumping forward to prevent infinite loop
|
|
|
|
if (offset > 0) {
|
|
|
|
i += offset - 1;
|
2013-03-23 18:08:18 +09:00
|
|
|
}
|
2012-11-08 02:32:55 +09:00
|
|
|
}
|
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
// Adjusting stack not extactly, but just enough to get function id
|
|
|
|
if (!inFDEF && !inELSE) {
|
|
|
|
let stackDelta = 0;
|
|
|
|
if (op <= 0x8e) {
|
|
|
|
stackDelta = TTOpsStackDeltas[op];
|
|
|
|
} else if (op >= 0xc0 && op <= 0xdf) {
|
|
|
|
stackDelta = -1;
|
|
|
|
} else if (op >= 0xe0) {
|
|
|
|
stackDelta = -2;
|
|
|
|
}
|
|
|
|
if (op >= 0x71 && op <= 0x75) {
|
|
|
|
n = stack.pop();
|
|
|
|
if (!isNaN(n)) {
|
|
|
|
stackDelta = -n * 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (stackDelta < 0 && stack.length > 0) {
|
|
|
|
stack.pop();
|
|
|
|
stackDelta++;
|
|
|
|
}
|
|
|
|
while (stackDelta > 0) {
|
|
|
|
stack.push(NaN); // pushing any number into stack
|
|
|
|
stackDelta--;
|
|
|
|
}
|
2012-11-08 02:32:55 +09:00
|
|
|
}
|
2013-03-23 18:08:18 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
ttContext.tooComplexToFollowFunctions = tooComplexToFollowFunctions;
|
|
|
|
const content = [data];
|
|
|
|
if (i > data.length) {
|
|
|
|
content.push(new Uint8Array(i - data.length));
|
|
|
|
}
|
|
|
|
if (lastDeff > lastEndf) {
|
|
|
|
warn("TT: complementing a missing function tail");
|
|
|
|
// new function definition started, but not finished
|
|
|
|
// complete function by [CLEAR, ENDF]
|
|
|
|
content.push(new Uint8Array([0x22, 0x2d]));
|
|
|
|
}
|
|
|
|
foldTTTable(table, content);
|
|
|
|
}
|
2013-03-23 18:08:18 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function checkInvalidFunctions(ttContext, maxFunctionDefs) {
|
|
|
|
if (ttContext.tooComplexToFollowFunctions) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (ttContext.functionsDefined.length > maxFunctionDefs) {
|
|
|
|
warn("TT: more functions defined than expected");
|
|
|
|
ttContext.hintsValid = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (let j = 0, jj = ttContext.functionsUsed.length; j < jj; j++) {
|
|
|
|
if (j > maxFunctionDefs) {
|
|
|
|
warn("TT: invalid function id: " + j);
|
|
|
|
ttContext.hintsValid = false;
|
2013-03-18 22:06:59 +09:00
|
|
|
return;
|
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
if (ttContext.functionsUsed[j] && !ttContext.functionsDefined[j]) {
|
|
|
|
warn("TT: undefined function: " + j);
|
2014-03-03 06:40:48 +09:00
|
|
|
ttContext.hintsValid = false;
|
|
|
|
return;
|
|
|
|
}
|
2013-03-23 18:08:18 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
2013-03-23 18:08:18 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function foldTTTable(table, content) {
|
|
|
|
if (content.length > 1) {
|
|
|
|
// concatenating the content items
|
|
|
|
let newLength = 0;
|
|
|
|
let j, jj;
|
|
|
|
for (j = 0, jj = content.length; j < jj; j++) {
|
|
|
|
newLength += content[j].length;
|
|
|
|
}
|
|
|
|
newLength = (newLength + 3) & ~3;
|
|
|
|
const result = new Uint8Array(newLength);
|
|
|
|
let pos = 0;
|
|
|
|
for (j = 0, jj = content.length; j < jj; j++) {
|
|
|
|
result.set(content[j], pos);
|
|
|
|
pos += content[j].length;
|
2012-11-08 02:32:55 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
table.data = result;
|
|
|
|
table.length = newLength;
|
2012-11-08 02:32:55 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
2012-11-08 02:32:55 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function sanitizeTTPrograms(fpgm, prep, cvt, maxFunctionDefs) {
|
|
|
|
const ttContext = {
|
|
|
|
functionsDefined: [],
|
|
|
|
functionsUsed: [],
|
|
|
|
functionsStackDeltas: [],
|
|
|
|
tooComplexToFollowFunctions: false,
|
|
|
|
hintsValid: true,
|
|
|
|
};
|
|
|
|
if (fpgm) {
|
|
|
|
sanitizeTTProgram(fpgm, ttContext);
|
|
|
|
}
|
|
|
|
if (prep) {
|
|
|
|
sanitizeTTProgram(prep, ttContext);
|
|
|
|
}
|
|
|
|
if (fpgm) {
|
|
|
|
checkInvalidFunctions(ttContext, maxFunctionDefs);
|
|
|
|
}
|
|
|
|
if (cvt && cvt.length & 1) {
|
|
|
|
const cvtData = new Uint8Array(cvt.length + 1);
|
|
|
|
cvtData.set(cvt.data);
|
|
|
|
cvt.data = cvtData;
|
2012-11-08 02:32:55 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
return ttContext.hintsValid;
|
|
|
|
}
|
2012-11-08 02:32:55 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// The following steps modify the original font data, making copy
|
|
|
|
font = new Stream(new Uint8Array(font.getBytes()));
|
2013-05-16 22:56:22 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
let header, tables;
|
|
|
|
if (isTrueTypeCollectionFile(font)) {
|
|
|
|
const ttcData = readTrueTypeCollectionData(font, this.name);
|
|
|
|
header = ttcData.header;
|
|
|
|
tables = ttcData.tables;
|
|
|
|
} else {
|
|
|
|
header = readOpenTypeHeader(font);
|
|
|
|
tables = readTables(font, header.numTables);
|
|
|
|
}
|
|
|
|
let cff, cffFile;
|
|
|
|
|
|
|
|
const isTrueType = !tables["CFF "];
|
|
|
|
if (!isTrueType) {
|
|
|
|
const isComposite =
|
|
|
|
properties.composite &&
|
|
|
|
((properties.cidToGidMap || []).length > 0 ||
|
|
|
|
!(properties.cMap instanceof IdentityCMap));
|
|
|
|
// OpenType font (skip composite fonts with non-default glyph mapping).
|
|
|
|
if (
|
|
|
|
(header.version === "OTTO" && !isComposite) ||
|
|
|
|
!tables.head ||
|
|
|
|
!tables.hhea ||
|
|
|
|
!tables.maxp ||
|
|
|
|
!tables.post
|
|
|
|
) {
|
|
|
|
// No major tables: throwing everything at `CFFFont`.
|
|
|
|
cffFile = new Stream(tables["CFF "].data);
|
|
|
|
cff = new CFFFont(cffFile, properties);
|
2013-06-25 08:45:31 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
adjustWidths(properties);
|
2015-12-08 06:30:09 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
return this.convert(name, cff, properties);
|
|
|
|
}
|
2013-06-25 08:45:31 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
delete tables.glyf;
|
|
|
|
delete tables.loca;
|
|
|
|
delete tables.fpgm;
|
|
|
|
delete tables.prep;
|
|
|
|
delete tables["cvt "];
|
|
|
|
this.isOpenType = true;
|
|
|
|
} else {
|
|
|
|
if (!tables.loca) {
|
|
|
|
throw new FormatError('Required "loca" table is not found');
|
|
|
|
}
|
|
|
|
if (!tables.glyf) {
|
|
|
|
warn('Required "glyf" table is not found -- trying to recover.');
|
|
|
|
// Note: We use `sanitizeGlyphLocations` to add dummy glyf data below.
|
|
|
|
tables.glyf = {
|
|
|
|
tag: "glyf",
|
|
|
|
data: new Uint8Array(0),
|
|
|
|
};
|
2013-06-25 05:33:50 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
this.isOpenType = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tables.maxp) {
|
|
|
|
throw new FormatError('Required "maxp" table is not found');
|
|
|
|
}
|
|
|
|
|
|
|
|
font.pos = (font.start || 0) + tables.maxp.offset;
|
|
|
|
const version = font.getInt32();
|
|
|
|
const numGlyphs = font.getUint16();
|
2021-05-10 18:54:38 +09:00
|
|
|
|
|
|
|
if (
|
|
|
|
properties.scaleFactors &&
|
|
|
|
properties.scaleFactors.length === numGlyphs &&
|
|
|
|
isTrueType
|
|
|
|
) {
|
|
|
|
const { scaleFactors } = properties;
|
|
|
|
const isGlyphLocationsLong = int16(
|
|
|
|
tables.head.data[50],
|
|
|
|
tables.head.data[51]
|
|
|
|
);
|
|
|
|
|
|
|
|
const glyphs = new GlyfTable({
|
|
|
|
glyfTable: tables.glyf.data,
|
|
|
|
isGlyphLocationsLong,
|
|
|
|
locaTable: tables.loca.data,
|
|
|
|
numGlyphs,
|
|
|
|
});
|
|
|
|
glyphs.scale(scaleFactors);
|
|
|
|
|
|
|
|
const { glyf, loca, isLocationLong } = glyphs.write();
|
|
|
|
tables.glyf.data = glyf;
|
|
|
|
tables.loca.data = loca;
|
|
|
|
|
|
|
|
if (isLocationLong !== !!isGlyphLocationsLong) {
|
|
|
|
tables.head.data[50] = 0;
|
|
|
|
tables.head.data[51] = isLocationLong ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const metrics = tables.hmtx.data;
|
|
|
|
|
|
|
|
for (let i = 0; i < numGlyphs; i++) {
|
|
|
|
const j = 4 * i;
|
|
|
|
const advanceWidth = Math.round(
|
|
|
|
scaleFactors[i] * int16(metrics[j], metrics[j + 1])
|
|
|
|
);
|
|
|
|
metrics[j] = (advanceWidth >> 8) & 0xff;
|
|
|
|
metrics[j + 1] = advanceWidth & 0xff;
|
|
|
|
const lsb = Math.round(
|
|
|
|
scaleFactors[i] * signedInt16(metrics[j + 2], metrics[j + 3])
|
|
|
|
);
|
|
|
|
writeSignedInt16(metrics, j + 2, lsb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// Glyph 0 is duplicated and appended.
|
|
|
|
let numGlyphsOut = numGlyphs + 1;
|
|
|
|
let dupFirstEntry = true;
|
|
|
|
if (numGlyphsOut > 0xffff) {
|
|
|
|
dupFirstEntry = false;
|
|
|
|
numGlyphsOut = numGlyphs;
|
|
|
|
warn("Not enough space in glyfs to duplicate first glyph.");
|
|
|
|
}
|
|
|
|
let maxFunctionDefs = 0;
|
|
|
|
let maxSizeOfInstructions = 0;
|
|
|
|
if (version >= 0x00010000 && tables.maxp.length >= 22) {
|
|
|
|
// maxZones can be invalid
|
|
|
|
font.pos += 8;
|
|
|
|
const maxZones = font.getUint16();
|
|
|
|
if (maxZones > 2) {
|
|
|
|
// reset to 2 if font has invalid maxZones
|
|
|
|
tables.maxp.data[14] = 0;
|
|
|
|
tables.maxp.data[15] = 2;
|
|
|
|
}
|
|
|
|
font.pos += 4;
|
|
|
|
maxFunctionDefs = font.getUint16();
|
|
|
|
font.pos += 4;
|
|
|
|
maxSizeOfInstructions = font.getUint16();
|
|
|
|
}
|
2013-06-25 05:33:50 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
tables.maxp.data[4] = numGlyphsOut >> 8;
|
|
|
|
tables.maxp.data[5] = numGlyphsOut & 255;
|
2013-06-25 08:45:31 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const hintsValid = sanitizeTTPrograms(
|
|
|
|
tables.fpgm,
|
|
|
|
tables.prep,
|
|
|
|
tables["cvt "],
|
|
|
|
maxFunctionDefs
|
|
|
|
);
|
|
|
|
if (!hintsValid) {
|
|
|
|
delete tables.fpgm;
|
|
|
|
delete tables.prep;
|
|
|
|
delete tables["cvt "];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the hmtx table contains the advance width and
|
|
|
|
// sidebearings information for numGlyphs in the maxp table
|
|
|
|
sanitizeMetrics(
|
|
|
|
font,
|
|
|
|
tables.hhea,
|
|
|
|
tables.hmtx,
|
|
|
|
numGlyphsOut,
|
|
|
|
dupFirstEntry
|
|
|
|
);
|
2013-03-18 22:06:59 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
if (!tables.head) {
|
|
|
|
throw new FormatError('Required "head" table is not found');
|
|
|
|
}
|
2013-07-30 05:24:32 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
sanitizeHead(tables.head, numGlyphs, isTrueType ? tables.loca.length : 0);
|
2013-03-18 22:06:59 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
let missingGlyphs = Object.create(null);
|
|
|
|
if (isTrueType) {
|
|
|
|
const isGlyphLocationsLong = int16(
|
|
|
|
tables.head.data[50],
|
|
|
|
tables.head.data[51]
|
|
|
|
);
|
|
|
|
const glyphsInfo = sanitizeGlyphLocations(
|
|
|
|
tables.loca,
|
|
|
|
tables.glyf,
|
|
|
|
numGlyphs,
|
|
|
|
isGlyphLocationsLong,
|
|
|
|
hintsValid,
|
|
|
|
dupFirstEntry,
|
|
|
|
maxSizeOfInstructions
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
);
|
2021-05-03 00:42:48 +09:00
|
|
|
missingGlyphs = glyphsInfo.missingGlyphs;
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// Some fonts have incorrect maxSizeOfInstructions values, so we use
|
|
|
|
// the computed value instead.
|
|
|
|
if (version >= 0x00010000 && tables.maxp.length >= 22) {
|
|
|
|
tables.maxp.data[26] = glyphsInfo.maxSizeOfInstructions >> 8;
|
|
|
|
tables.maxp.data[27] = glyphsInfo.maxSizeOfInstructions & 255;
|
2013-01-19 08:06:12 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
|
|
|
if (!tables.hhea) {
|
|
|
|
throw new FormatError('Required "hhea" table is not found');
|
|
|
|
}
|
2013-01-19 08:06:12 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// Sanitizer reduces the glyph advanceWidth to the maxAdvanceWidth
|
|
|
|
// Sometimes it's 0. That needs to be fixed
|
|
|
|
if (tables.hhea.data[10] === 0 && tables.hhea.data[11] === 0) {
|
|
|
|
tables.hhea.data[10] = 0xff;
|
|
|
|
tables.hhea.data[11] = 0xff;
|
|
|
|
}
|
2013-06-25 05:33:50 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// Extract some more font properties from the OpenType head and
|
|
|
|
// hhea tables; yMin and descent value are always negative.
|
|
|
|
const metricsOverride = {
|
|
|
|
unitsPerEm: int16(tables.head.data[18], tables.head.data[19]),
|
|
|
|
yMax: int16(tables.head.data[42], tables.head.data[43]),
|
|
|
|
yMin: signedInt16(tables.head.data[38], tables.head.data[39]),
|
|
|
|
ascent: int16(tables.hhea.data[4], tables.hhea.data[5]),
|
|
|
|
descent: signedInt16(tables.hhea.data[6], tables.hhea.data[7]),
|
|
|
|
};
|
2017-09-26 09:24:21 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// PDF FontDescriptor metrics lie -- using data from actual font.
|
|
|
|
this.ascent = metricsOverride.ascent / metricsOverride.unitsPerEm;
|
|
|
|
this.descent = metricsOverride.descent / metricsOverride.unitsPerEm;
|
2012-03-14 08:59:16 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// The 'post' table has glyphs names.
|
|
|
|
if (tables.post) {
|
|
|
|
readPostScriptTable(tables.post, properties, numGlyphs);
|
|
|
|
}
|
2011-06-13 09:30:16 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// The original 'post' table is not needed, replace it.
|
|
|
|
tables.post = {
|
|
|
|
tag: "post",
|
|
|
|
data: createPostTable(properties),
|
|
|
|
};
|
2015-11-07 05:47:10 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const charCodeToGlyphId = [];
|
2015-11-07 05:47:10 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// Helper function to try to skip mapping of empty glyphs.
|
|
|
|
function hasGlyph(glyphId) {
|
|
|
|
return !missingGlyphs[glyphId];
|
|
|
|
}
|
2011-10-29 10:38:31 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
if (properties.composite) {
|
|
|
|
const cidToGidMap = properties.cidToGidMap || [];
|
|
|
|
const isCidToGidMapEmpty = cidToGidMap.length === 0;
|
2018-01-05 07:43:07 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
properties.cMap.forEach(function (charCode, cid) {
|
|
|
|
if (cid > 0xffff) {
|
|
|
|
throw new FormatError("Max size of CID is 65,535");
|
|
|
|
}
|
|
|
|
let glyphId = -1;
|
|
|
|
if (isCidToGidMapEmpty) {
|
|
|
|
glyphId = cid;
|
|
|
|
} else if (cidToGidMap[cid] !== undefined) {
|
|
|
|
glyphId = cidToGidMap[cid];
|
|
|
|
}
|
2015-02-07 08:13:41 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
if (glyphId >= 0 && glyphId < numGlyphs && hasGlyph(glyphId)) {
|
|
|
|
charCodeToGlyphId[charCode] = glyphId;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// Most of the following logic in this code branch is based on the
|
|
|
|
// 9.6.6.4 of the PDF spec.
|
|
|
|
const cmapTable = readCmapTable(
|
|
|
|
tables.cmap,
|
|
|
|
font,
|
|
|
|
this.isSymbolicFont,
|
|
|
|
properties.hasEncoding
|
|
|
|
);
|
|
|
|
const cmapPlatformId = cmapTable.platformId;
|
|
|
|
const cmapEncodingId = cmapTable.encodingId;
|
|
|
|
const cmapMappings = cmapTable.mappings;
|
|
|
|
const cmapMappingsLength = cmapMappings.length;
|
|
|
|
let baseEncoding = [];
|
|
|
|
if (
|
|
|
|
properties.hasEncoding &&
|
|
|
|
(properties.baseEncodingName === "MacRomanEncoding" ||
|
|
|
|
properties.baseEncodingName === "WinAnsiEncoding")
|
|
|
|
) {
|
|
|
|
baseEncoding = getEncoding(properties.baseEncodingName);
|
2015-02-07 08:13:41 +09:00
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// If the font has an encoding and is not symbolic then follow the
|
|
|
|
// rules in section 9.6.6.4 of the spec on how to map 3,1 and 1,0
|
|
|
|
// cmaps.
|
|
|
|
if (
|
|
|
|
properties.hasEncoding &&
|
|
|
|
!this.isSymbolicFont &&
|
|
|
|
((cmapPlatformId === 3 && cmapEncodingId === 1) ||
|
|
|
|
(cmapPlatformId === 1 && cmapEncodingId === 0))
|
|
|
|
) {
|
|
|
|
const glyphsUnicodeMap = getGlyphsUnicode();
|
|
|
|
for (let charCode = 0; charCode < 256; charCode++) {
|
|
|
|
let glyphName;
|
|
|
|
if (this.differences && charCode in this.differences) {
|
|
|
|
glyphName = this.differences[charCode];
|
|
|
|
} else if (
|
|
|
|
charCode in baseEncoding &&
|
|
|
|
baseEncoding[charCode] !== ""
|
|
|
|
) {
|
|
|
|
glyphName = baseEncoding[charCode];
|
|
|
|
} else {
|
|
|
|
glyphName = StandardEncoding[charCode];
|
2017-07-20 21:04:54 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
if (!glyphName) {
|
|
|
|
continue;
|
2013-07-30 05:24:32 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
// Ensure that non-standard glyph names are resolved to valid ones.
|
|
|
|
const standardGlyphName = recoverGlyphName(
|
|
|
|
glyphName,
|
|
|
|
glyphsUnicodeMap
|
|
|
|
);
|
2014-10-04 02:35:49 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
let unicodeOrCharCode;
|
|
|
|
if (cmapPlatformId === 3 && cmapEncodingId === 1) {
|
|
|
|
unicodeOrCharCode = glyphsUnicodeMap[standardGlyphName];
|
|
|
|
} else if (cmapPlatformId === 1 && cmapEncodingId === 0) {
|
|
|
|
// TODO: the encoding needs to be updated with mac os table.
|
|
|
|
unicodeOrCharCode = MacRomanEncoding.indexOf(standardGlyphName);
|
2011-11-28 11:43:23 +09:00
|
|
|
}
|
2013-01-31 03:01:32 +09:00
|
|
|
|
2017-12-15 08:23:56 +09:00
|
|
|
for (let i = 0; i < cmapMappingsLength; ++i) {
|
2021-05-03 00:42:48 +09:00
|
|
|
if (cmapMappings[i].charCode !== unicodeOrCharCode) {
|
|
|
|
continue;
|
2017-06-30 09:14:58 +09:00
|
|
|
}
|
2014-02-12 03:27:09 +09:00
|
|
|
charCodeToGlyphId[charCode] = cmapMappings[i].glyphId;
|
2021-05-03 00:42:48 +09:00
|
|
|
break;
|
2012-02-21 08:19:12 +09:00
|
|
|
}
|
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
} else if (cmapPlatformId === 0) {
|
|
|
|
// Default Unicode semantics, use the charcodes as is.
|
|
|
|
for (let i = 0; i < cmapMappingsLength; ++i) {
|
|
|
|
charCodeToGlyphId[cmapMappings[i].charCode] = cmapMappings[i].glyphId;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// When there is only a (1, 0) cmap table, the char code is a single
|
|
|
|
// byte and it is used directly as the char code.
|
|
|
|
|
|
|
|
// When a (3, 0) cmap table is present, it is used instead but the
|
|
|
|
// spec has special rules for char codes in the range of 0xF000 to
|
|
|
|
// 0xF0FF and it says the (3, 0) table should map the values from
|
|
|
|
// the (1, 0) table by prepending 0xF0 to the char codes. To reverse
|
|
|
|
// this, the upper bits of the char code are cleared, but only for the
|
|
|
|
// special range since some PDFs have char codes outside of this range
|
|
|
|
// (e.g. 0x2013) which when masked would overwrite other values in the
|
|
|
|
// cmap.
|
|
|
|
for (let i = 0; i < cmapMappingsLength; ++i) {
|
|
|
|
let charCode = cmapMappings[i].charCode;
|
|
|
|
if (
|
|
|
|
cmapPlatformId === 3 &&
|
|
|
|
charCode >= 0xf000 &&
|
|
|
|
charCode <= 0xf0ff
|
|
|
|
) {
|
|
|
|
charCode &= 0xff;
|
2020-08-22 07:25:07 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
charCodeToGlyphId[charCode] = cmapMappings[i].glyphId;
|
2020-08-22 07:25:07 +09:00
|
|
|
}
|
2012-03-26 04:15:40 +09:00
|
|
|
}
|
2011-11-28 11:43:23 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// Last, try to map any missing charcodes using the post table.
|
|
|
|
if (
|
|
|
|
properties.glyphNames &&
|
|
|
|
(baseEncoding.length || this.differences.length)
|
|
|
|
) {
|
|
|
|
for (let i = 0; i < 256; ++i) {
|
|
|
|
if (charCodeToGlyphId[i] !== undefined) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const glyphName = this.differences[i] || baseEncoding[i];
|
|
|
|
if (!glyphName) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const glyphId = properties.glyphNames.indexOf(glyphName);
|
|
|
|
if (glyphId > 0 && hasGlyph(glyphId)) {
|
|
|
|
charCodeToGlyphId[i] = glyphId;
|
|
|
|
}
|
|
|
|
}
|
2012-10-16 00:01:18 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
2012-10-16 00:01:18 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
if (charCodeToGlyphId.length === 0) {
|
|
|
|
// defines at least one glyph
|
|
|
|
charCodeToGlyphId[0] = 0;
|
|
|
|
}
|
2018-01-05 07:43:07 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// Typically glyph 0 is duplicated and the mapping must be updated, but if
|
|
|
|
// there isn't enough room to duplicate, the glyph id is left the same. In
|
|
|
|
// this case, glyph 0 may not work correctly, but that is better than
|
|
|
|
// having the whole font fail.
|
|
|
|
let glyphZeroId = numGlyphsOut - 1;
|
|
|
|
if (!dupFirstEntry) {
|
|
|
|
glyphZeroId = 0;
|
|
|
|
}
|
2021-03-26 17:28:18 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// When `cssFontInfo` is set, the font is used to render text in the HTML
|
|
|
|
// view (e.g. with Xfa) so nothing must be moved in the private use area.
|
|
|
|
if (!properties.cssFontInfo) {
|
|
|
|
// Converting glyphs and ids into font's cmap table
|
|
|
|
const newMapping = adjustMapping(
|
|
|
|
charCodeToGlyphId,
|
|
|
|
hasGlyph,
|
|
|
|
glyphZeroId
|
|
|
|
);
|
|
|
|
this.toFontChar = newMapping.toFontChar;
|
|
|
|
tables.cmap = {
|
|
|
|
tag: "cmap",
|
|
|
|
data: createCmapTable(newMapping.charCodeToGlyphId, numGlyphsOut),
|
|
|
|
};
|
2012-04-07 07:52:57 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
if (!tables["OS/2"] || !validateOS2Table(tables["OS/2"], font)) {
|
|
|
|
tables["OS/2"] = {
|
|
|
|
tag: "OS/2",
|
|
|
|
data: createOS2Table(
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
properties,
|
2021-05-03 00:42:48 +09:00
|
|
|
newMapping.charCodeToGlyphId,
|
|
|
|
metricsOverride
|
|
|
|
),
|
2013-06-25 05:33:50 +09:00
|
|
|
};
|
2011-07-15 21:58:14 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
2011-06-13 10:35:56 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
if (!isTrueType) {
|
|
|
|
try {
|
|
|
|
// Trying to repair CFF file
|
|
|
|
cffFile = new Stream(tables["CFF "].data);
|
|
|
|
const parser = new CFFParser(
|
|
|
|
cffFile,
|
|
|
|
properties,
|
|
|
|
SEAC_ANALYSIS_ENABLED
|
|
|
|
);
|
|
|
|
cff = parser.parse();
|
|
|
|
cff.duplicateFirstGlyph();
|
|
|
|
const compiler = new CFFCompiler(cff);
|
|
|
|
tables["CFF "].data = compiler.compile();
|
|
|
|
} catch (e) {
|
|
|
|
warn("Failed to compile font " + properties.loadedName);
|
2011-07-15 21:58:14 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
2011-06-21 11:30:28 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// Re-creating 'name' table
|
|
|
|
if (!tables.name) {
|
|
|
|
tables.name = {
|
|
|
|
tag: "name",
|
|
|
|
data: createNameTable(this.name),
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
// ... using existing 'name' table as prototype
|
|
|
|
const namePrototype = readNameTable(tables.name);
|
|
|
|
tables.name.data = createNameTable(name, namePrototype);
|
|
|
|
}
|
2011-07-23 01:57:26 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const builder = new OpenTypeFileBuilder(header.version);
|
|
|
|
for (const tableTag in tables) {
|
|
|
|
builder.addTable(tableTag, tables[tableTag].data);
|
|
|
|
}
|
|
|
|
return builder.toArray();
|
|
|
|
}
|
For embedded Type1 fonts without included `ToUnicode`/`Encoding` data, attempt to improve text selection by using the `builtInEncoding` to amend the `toUnicode` map (issue 6901, issue 7182, issue 7217, bug 917796, bug 1242142)
Note that in order to prevent any possible issues, this patch does *not* try to amend the `toUnicode` data for Type1 fonts that contain either `ToUnicode` or `Encoding` entries in the font dictionary.
Fixes, or at least improves, issues/bugs such as e.g. 6658, 6901, 7182, 7217, bug 917796, bug 1242142.
2016-08-18 01:33:06 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
convert(fontName, font, properties) {
|
|
|
|
// TODO: Check the charstring widths to determine this.
|
|
|
|
properties.fixedPitch = false;
|
2014-06-24 03:55:51 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
if (properties.builtInEncoding) {
|
|
|
|
// For Type1 fonts that do not include either `ToUnicode` or `Encoding`
|
|
|
|
// data, attempt to use the `builtInEncoding` to improve text selection.
|
|
|
|
adjustToUnicode(properties, properties.builtInEncoding);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Type 1 fonts have a notdef inserted at the beginning, so glyph 0
|
|
|
|
// becomes glyph 1. In a CFF font glyph 0 is appended to the end of the
|
|
|
|
// char strings.
|
|
|
|
let glyphZeroId = 1;
|
|
|
|
if (font instanceof CFFFont) {
|
|
|
|
glyphZeroId = font.numGlyphs - 1;
|
|
|
|
}
|
|
|
|
const mapping = font.getGlyphMapping(properties);
|
2021-05-17 23:52:34 +09:00
|
|
|
let newMapping = null;
|
|
|
|
let newCharCodeToGlyphId = mapping;
|
|
|
|
|
|
|
|
// When `cssFontInfo` is set, the font is used to render text in the HTML
|
|
|
|
// view (e.g. with Xfa) so nothing must be moved in the private use area.
|
|
|
|
if (!properties.cssFontInfo) {
|
|
|
|
newMapping = adjustMapping(
|
|
|
|
mapping,
|
|
|
|
font.hasGlyphId.bind(font),
|
|
|
|
glyphZeroId
|
|
|
|
);
|
|
|
|
this.toFontChar = newMapping.toFontChar;
|
|
|
|
newCharCodeToGlyphId = newMapping.charCodeToGlyphId;
|
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
const numGlyphs = font.numGlyphs;
|
2014-04-12 01:55:39 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function getCharCodes(charCodeToGlyphId, glyphId) {
|
|
|
|
let charCodes = null;
|
|
|
|
for (const charCode in charCodeToGlyphId) {
|
|
|
|
if (glyphId === charCodeToGlyphId[charCode]) {
|
2014-06-24 03:55:51 +09:00
|
|
|
if (!charCodes) {
|
2021-05-03 00:42:48 +09:00
|
|
|
charCodes = [];
|
2014-06-24 03:55:51 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
charCodes.push(charCode | 0);
|
2013-02-27 03:00:20 +09:00
|
|
|
}
|
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
return charCodes;
|
|
|
|
}
|
2013-02-27 03:00:20 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
function createCharCode(charCodeToGlyphId, glyphId) {
|
|
|
|
for (const charCode in charCodeToGlyphId) {
|
|
|
|
if (glyphId === charCodeToGlyphId[charCode]) {
|
|
|
|
return charCode | 0;
|
|
|
|
}
|
|
|
|
}
|
2021-05-16 17:58:34 +09:00
|
|
|
newMapping.charCodeToGlyphId[newMapping.nextAvailableFontCharCode] =
|
|
|
|
glyphId;
|
2021-05-03 00:42:48 +09:00
|
|
|
return newMapping.nextAvailableFontCharCode++;
|
|
|
|
}
|
2011-10-29 10:38:31 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const seacs = font.seacs;
|
2021-05-17 23:52:34 +09:00
|
|
|
if (newMapping && SEAC_ANALYSIS_ENABLED && seacs && seacs.length) {
|
2021-05-03 00:42:48 +09:00
|
|
|
const matrix = properties.fontMatrix || FONT_IDENTITY_MATRIX;
|
|
|
|
const charset = font.getCharset();
|
|
|
|
const seacMap = Object.create(null);
|
|
|
|
for (let glyphId in seacs) {
|
|
|
|
glyphId |= 0;
|
|
|
|
const seac = seacs[glyphId];
|
|
|
|
const baseGlyphName = StandardEncoding[seac[2]];
|
|
|
|
const accentGlyphName = StandardEncoding[seac[3]];
|
|
|
|
const baseGlyphId = charset.indexOf(baseGlyphName);
|
|
|
|
const accentGlyphId = charset.indexOf(accentGlyphName);
|
|
|
|
if (baseGlyphId < 0 || accentGlyphId < 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const accentOffset = {
|
|
|
|
x: seac[0] * matrix[0] + seac[1] * matrix[2] + matrix[4],
|
|
|
|
y: seac[0] * matrix[1] + seac[1] * matrix[3] + matrix[5],
|
|
|
|
};
|
2011-07-03 08:17:28 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const charCodes = getCharCodes(mapping, glyphId);
|
|
|
|
if (!charCodes) {
|
|
|
|
// There's no point in mapping it if the char code was never mapped
|
|
|
|
// to begin with.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (let i = 0, ii = charCodes.length; i < ii; i++) {
|
|
|
|
const charCode = charCodes[i];
|
|
|
|
// Find a fontCharCode that maps to the base and accent glyphs.
|
|
|
|
// If one doesn't exists, create it.
|
|
|
|
const charCodeToGlyphId = newMapping.charCodeToGlyphId;
|
|
|
|
const baseFontCharCode = createCharCode(
|
|
|
|
charCodeToGlyphId,
|
|
|
|
baseGlyphId
|
|
|
|
);
|
|
|
|
const accentFontCharCode = createCharCode(
|
|
|
|
charCodeToGlyphId,
|
|
|
|
accentGlyphId
|
|
|
|
);
|
|
|
|
seacMap[charCode] = {
|
|
|
|
baseFontCharCode,
|
|
|
|
accentFontCharCode,
|
|
|
|
accentOffset,
|
|
|
|
};
|
2014-03-18 01:34:30 +09:00
|
|
|
}
|
2011-12-13 12:32:20 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
properties.seacMap = seacMap;
|
|
|
|
}
|
2011-12-13 12:32:20 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
const unitsPerEm = 1 / (properties.fontMatrix || FONT_IDENTITY_MATRIX)[0];
|
|
|
|
|
|
|
|
const builder = new OpenTypeFileBuilder("\x4F\x54\x54\x4F");
|
|
|
|
// PostScript Font Program
|
|
|
|
builder.addTable("CFF ", font.data);
|
|
|
|
// OS/2 and Windows Specific metrics
|
2021-05-17 23:52:34 +09:00
|
|
|
builder.addTable("OS/2", createOS2Table(properties, newCharCodeToGlyphId));
|
2021-05-03 00:42:48 +09:00
|
|
|
// Character to glyphs mapping
|
2021-05-17 23:52:34 +09:00
|
|
|
builder.addTable("cmap", createCmapTable(newCharCodeToGlyphId, numGlyphs));
|
2021-05-03 00:42:48 +09:00
|
|
|
// Font header
|
|
|
|
builder.addTable(
|
|
|
|
"head",
|
|
|
|
"\x00\x01\x00\x00" + // Version number
|
|
|
|
"\x00\x00\x10\x00" + // fontRevision
|
|
|
|
"\x00\x00\x00\x00" + // checksumAdjustement
|
|
|
|
"\x5F\x0F\x3C\xF5" + // magicNumber
|
|
|
|
"\x00\x00" + // Flags
|
|
|
|
safeString16(unitsPerEm) + // unitsPerEM
|
|
|
|
"\x00\x00\x00\x00\x9e\x0b\x7e\x27" + // creation date
|
|
|
|
"\x00\x00\x00\x00\x9e\x0b\x7e\x27" + // modifification date
|
|
|
|
"\x00\x00" + // xMin
|
|
|
|
safeString16(properties.descent) + // yMin
|
|
|
|
"\x0F\xFF" + // xMax
|
|
|
|
safeString16(properties.ascent) + // yMax
|
|
|
|
string16(properties.italicAngle ? 2 : 0) + // macStyle
|
|
|
|
"\x00\x11" + // lowestRecPPEM
|
|
|
|
"\x00\x00" + // fontDirectionHint
|
|
|
|
"\x00\x00" + // indexToLocFormat
|
|
|
|
"\x00\x00"
|
|
|
|
); // glyphDataFormat
|
|
|
|
|
|
|
|
// Horizontal header
|
|
|
|
builder.addTable(
|
|
|
|
"hhea",
|
|
|
|
"\x00\x01\x00\x00" + // Version number
|
|
|
|
safeString16(properties.ascent) + // Typographic Ascent
|
|
|
|
safeString16(properties.descent) + // Typographic Descent
|
|
|
|
"\x00\x00" + // Line Gap
|
|
|
|
"\xFF\xFF" + // advanceWidthMax
|
|
|
|
"\x00\x00" + // minLeftSidebearing
|
|
|
|
"\x00\x00" + // minRightSidebearing
|
|
|
|
"\x00\x00" + // xMaxExtent
|
|
|
|
safeString16(properties.capHeight) + // caretSlopeRise
|
|
|
|
safeString16(Math.tan(properties.italicAngle) * properties.xHeight) + // caretSlopeRun
|
|
|
|
"\x00\x00" + // caretOffset
|
|
|
|
"\x00\x00" + // -reserved-
|
|
|
|
"\x00\x00" + // -reserved-
|
|
|
|
"\x00\x00" + // -reserved-
|
|
|
|
"\x00\x00" + // -reserved-
|
|
|
|
"\x00\x00" + // metricDataFormat
|
|
|
|
string16(numGlyphs)
|
|
|
|
); // Number of HMetrics
|
|
|
|
|
|
|
|
// Horizontal metrics
|
|
|
|
builder.addTable(
|
|
|
|
"hmtx",
|
|
|
|
(function fontFieldsHmtx() {
|
|
|
|
const charstrings = font.charstrings;
|
|
|
|
const cffWidths = font.cff ? font.cff.widths : null;
|
|
|
|
let hmtx = "\x00\x00\x00\x00"; // Fake .notdef
|
|
|
|
for (let i = 1, ii = numGlyphs; i < ii; i++) {
|
|
|
|
let width = 0;
|
|
|
|
if (charstrings) {
|
|
|
|
const charstring = charstrings[i - 1];
|
|
|
|
width = "width" in charstring ? charstring.width : 0;
|
|
|
|
} else if (cffWidths) {
|
|
|
|
width = Math.ceil(cffWidths[i] || 0);
|
|
|
|
}
|
|
|
|
hmtx += string16(width) + string16(0);
|
2020-01-22 03:36:41 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
return hmtx;
|
|
|
|
})()
|
|
|
|
);
|
2014-02-12 03:27:09 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// Maximum profile
|
|
|
|
builder.addTable(
|
|
|
|
"maxp",
|
|
|
|
"\x00\x00\x50\x00" + string16(numGlyphs) // Version number
|
|
|
|
); // Num of glyphs
|
2011-11-30 13:06:00 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// Naming tables
|
|
|
|
builder.addTable("name", createNameTable(fontName));
|
2013-02-27 03:00:20 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// PostScript information
|
|
|
|
builder.addTable("post", createPostTable(properties));
|
|
|
|
|
|
|
|
return builder.toArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
get spaceWidth() {
|
|
|
|
// trying to estimate space character width
|
|
|
|
const possibleSpaceReplacements = ["space", "minus", "one", "i", "I"];
|
|
|
|
let width;
|
|
|
|
for (let i = 0, ii = possibleSpaceReplacements.length; i < ii; i++) {
|
|
|
|
const glyphName = possibleSpaceReplacements[i];
|
|
|
|
// if possible, getting width by glyph name
|
|
|
|
if (glyphName in this.widths) {
|
|
|
|
width = this.widths[glyphName];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
const glyphsUnicodeMap = getGlyphsUnicode();
|
|
|
|
const glyphUnicode = glyphsUnicodeMap[glyphName];
|
|
|
|
// finding the charcode via unicodeToCID map
|
|
|
|
let charcode = 0;
|
|
|
|
if (this.composite && this.cMap.contains(glyphUnicode)) {
|
|
|
|
charcode = this.cMap.lookup(glyphUnicode);
|
2020-04-01 06:10:15 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
// ... via toUnicode map
|
|
|
|
if (!charcode && this.toUnicode) {
|
|
|
|
charcode = this.toUnicode.charCodeOf(glyphUnicode);
|
|
|
|
}
|
|
|
|
// setting it to unicode if negative or undefined
|
|
|
|
if (charcode <= 0) {
|
|
|
|
charcode = glyphUnicode;
|
|
|
|
}
|
|
|
|
// trying to get width via charcode
|
|
|
|
width = this.widths[charcode];
|
|
|
|
if (width) {
|
|
|
|
break; // the non-zero width found
|
|
|
|
}
|
|
|
|
}
|
|
|
|
width = width || this.defaultWidth;
|
|
|
|
return shadow(this, "spaceWidth", width);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_charToGlyph(charcode, isSpace = false) {
|
|
|
|
let fontCharCode, width, operatorListId;
|
2014-03-13 21:56:12 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
let widthCode = charcode;
|
|
|
|
if (this.cMap && this.cMap.contains(charcode)) {
|
|
|
|
widthCode = this.cMap.lookup(charcode);
|
|
|
|
}
|
|
|
|
width = this.widths[widthCode];
|
|
|
|
width = isNum(width) ? width : this.defaultWidth;
|
|
|
|
const vmetric = this.vmetrics && this.vmetrics[widthCode];
|
|
|
|
|
|
|
|
let unicode =
|
|
|
|
this.toUnicode.get(charcode) ||
|
|
|
|
this.fallbackToUnicode.get(charcode) ||
|
|
|
|
charcode;
|
|
|
|
if (typeof unicode === "number") {
|
|
|
|
unicode = String.fromCharCode(unicode);
|
|
|
|
}
|
|
|
|
|
|
|
|
let isInFont = charcode in this.toFontChar;
|
|
|
|
// First try the toFontChar map, if it's not there then try falling
|
|
|
|
// back to the char code.
|
|
|
|
fontCharCode = this.toFontChar[charcode] || charcode;
|
|
|
|
if (this.missingFile) {
|
|
|
|
const glyphName =
|
|
|
|
this.differences[charcode] || this.defaultEncoding[charcode];
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
if (
|
2021-05-03 00:42:48 +09:00
|
|
|
(glyphName === ".notdef" || glyphName === "") &&
|
|
|
|
this.type === "Type1"
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
) {
|
2021-05-03 00:42:48 +09:00
|
|
|
// .notdef glyphs should be invisible in non-embedded Type1 fonts, so
|
|
|
|
// replace them with spaces.
|
|
|
|
fontCharCode = 0x20;
|
2014-03-13 21:56:12 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
fontCharCode = mapSpecialUnicodeValues(fontCharCode);
|
|
|
|
}
|
2011-10-29 10:38:31 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
if (this.isType3Font) {
|
|
|
|
// Font char code in this case is actually a glyph name.
|
|
|
|
operatorListId = fontCharCode;
|
|
|
|
}
|
2011-08-09 05:13:32 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
let accent = null;
|
|
|
|
if (this.seacMap && this.seacMap[charcode]) {
|
|
|
|
isInFont = true;
|
|
|
|
const seac = this.seacMap[charcode];
|
|
|
|
fontCharCode = seac.baseFontCharCode;
|
|
|
|
accent = {
|
|
|
|
fontChar: String.fromCodePoint(seac.accentFontCharCode),
|
|
|
|
offset: seac.accentOffset,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
let fontChar = "";
|
|
|
|
if (typeof fontCharCode === "number") {
|
|
|
|
if (fontCharCode <= 0x10ffff) {
|
|
|
|
fontChar = String.fromCodePoint(fontCharCode);
|
|
|
|
} else {
|
|
|
|
warn(`charToGlyph - invalid fontCharCode: ${fontCharCode}`);
|
2011-07-09 08:36:50 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
|
|
|
|
2021-05-26 20:07:00 +09:00
|
|
|
let glyph = this._glyphCache[charcode];
|
2021-05-03 00:42:48 +09:00
|
|
|
if (
|
|
|
|
!glyph ||
|
|
|
|
!glyph.matchesForCache(
|
2021-06-05 01:48:30 +09:00
|
|
|
charcode,
|
2021-05-03 00:42:48 +09:00
|
|
|
fontChar,
|
|
|
|
unicode,
|
|
|
|
accent,
|
|
|
|
width,
|
|
|
|
vmetric,
|
|
|
|
operatorListId,
|
|
|
|
isSpace,
|
|
|
|
isInFont
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
glyph = new Glyph(
|
2021-06-05 01:48:30 +09:00
|
|
|
charcode,
|
2021-05-03 00:42:48 +09:00
|
|
|
fontChar,
|
|
|
|
unicode,
|
|
|
|
accent,
|
|
|
|
width,
|
|
|
|
vmetric,
|
|
|
|
operatorListId,
|
|
|
|
isSpace,
|
|
|
|
isInFont
|
|
|
|
);
|
2021-05-26 20:07:00 +09:00
|
|
|
this._glyphCache[charcode] = glyph;
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
|
|
|
return glyph;
|
|
|
|
}
|
2011-08-09 05:13:32 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
charsToGlyphs(chars) {
|
2021-05-26 20:07:00 +09:00
|
|
|
// If we translated this string before, just grab it from the cache.
|
|
|
|
let glyphs = this._charsCache[chars];
|
|
|
|
if (glyphs) {
|
|
|
|
return glyphs;
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
|
|
|
glyphs = [];
|
|
|
|
|
|
|
|
if (this.cMap) {
|
2021-05-26 20:07:00 +09:00
|
|
|
// Composite fonts have multi-byte strings, convert the string from
|
|
|
|
// single-byte to multi-byte.
|
|
|
|
const c = Object.create(null),
|
|
|
|
ii = chars.length;
|
|
|
|
let i = 0;
|
|
|
|
while (i < ii) {
|
2021-05-03 00:42:48 +09:00
|
|
|
this.cMap.readCharCode(chars, i, c);
|
2021-05-26 20:07:00 +09:00
|
|
|
const { charcode, length } = c;
|
2021-05-03 00:42:48 +09:00
|
|
|
i += length;
|
|
|
|
// Space is char with code 0x20 and length 1 in multiple-byte codes.
|
2021-05-26 20:07:00 +09:00
|
|
|
const glyph = this._charToGlyph(
|
|
|
|
charcode,
|
|
|
|
length === 1 && chars.charCodeAt(i - 1) === 0x20
|
|
|
|
);
|
2021-05-03 00:42:48 +09:00
|
|
|
glyphs.push(glyph);
|
|
|
|
}
|
|
|
|
} else {
|
2021-05-26 20:07:00 +09:00
|
|
|
for (let i = 0, ii = chars.length; i < ii; ++i) {
|
|
|
|
const charcode = chars.charCodeAt(i);
|
|
|
|
const glyph = this._charToGlyph(charcode, charcode === 0x20);
|
2021-05-03 00:42:48 +09:00
|
|
|
glyphs.push(glyph);
|
2011-07-09 08:36:50 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
2011-07-09 08:36:50 +09:00
|
|
|
|
2021-05-26 20:07:00 +09:00
|
|
|
// Enter the translated string into the cache.
|
|
|
|
return (this._charsCache[chars] = glyphs);
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
Fallback to the built-in font renderer when font loading fails
After PR 9340 all glyphs are now re-mapped to a Private Use Area (PUA) which means that if a font fails to load, for whatever reason[1], all glyphs in the font will now render as Unicode glyph outlines.
This obviously doesn't look good, to say the least, and might be seen as a "regression" since previously many glyphs were left in their original positions which provided a slightly better fallback[2].
Hence this patch, which implements a *general* fallback to the PDF.js built-in font renderer for fonts that fail to load (i.e. are rejected by the sanitizer). One caveat here is that this only works for the Font Loading API, since it's easy to handle errors in that case[3].
The solution implemented in this patch does *not* in any way delay the loading of valid fonts, which was the problem with my previous attempt at a solution, and will only require a bit of extra work/waiting for those fonts that actually fail to load.
*Please note:* This patch doesn't fix any of the underlying PDF.js font conversion bugs that's responsible for creating corrupt font files, however it does *improve* rendering in a number of cases; refer to this possibly incomplete list:
[Bug 1524888](https://bugzilla.mozilla.org/show_bug.cgi?id=1524888)
Issue 10175
Issue 10232
---
[1] Usually because the PDF.js font conversion code wasn't able to parse the font file correctly.
[2] Glyphs fell back to some default font, which while not accurate was more useful than the current state.
[3] Furthermore I'm not sure how to implement this generally, assuming that's even possible, and don't really have time/interest to look into it either.
2019-02-11 08:47:56 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
/**
|
|
|
|
* 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]);
|
2020-08-27 23:04:17 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
2020-08-27 23:04:17 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
return positions;
|
|
|
|
}
|
2020-08-27 23:04:17 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
get glyphCacheValues() {
|
2021-05-26 20:07:00 +09:00
|
|
|
return Object.values(this._glyphCache);
|
2021-05-03 00:42:48 +09:00
|
|
|
}
|
2020-08-27 23:04:17 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
/**
|
|
|
|
* 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;
|
2020-08-27 23:04:17 +09:00
|
|
|
}
|
2021-05-03 00:42:48 +09:00
|
|
|
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;
|
2020-08-27 23:04:17 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
// unicode can't be encoded
|
|
|
|
if (!hasCurrentBufErrors()) {
|
|
|
|
buffers.push(currentBuf.join(""));
|
|
|
|
currentBuf.length = 0;
|
|
|
|
}
|
|
|
|
currentBuf.push(String.fromCodePoint(unicode));
|
|
|
|
}
|
2020-08-27 23:04:17 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
buffers.push(currentBuf.join(""));
|
2011-06-04 00:48:32 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
return buffers;
|
|
|
|
}
|
|
|
|
}
|
2011-06-04 00:48:32 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
class ErrorFont {
|
|
|
|
constructor(error) {
|
2012-08-30 06:11:56 +09:00
|
|
|
this.error = error;
|
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).
Prettier is being used for a couple of reasons:
- To be consistent with `mozilla-central`, where Prettier is already in use across the tree.
- To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.
Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.
*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.
(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-25 23:59:37 +09:00
|
|
|
this.loadedName = "g_font_error";
|
2018-08-17 03:34:37 +09:00
|
|
|
this.missingFile = true;
|
2012-08-30 06:11:56 +09:00
|
|
|
}
|
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
charsToGlyphs() {
|
|
|
|
return [];
|
|
|
|
}
|
2012-08-30 06:11:56 +09:00
|
|
|
|
2021-05-03 00:42:48 +09:00
|
|
|
encodeString(chars) {
|
|
|
|
return [chars];
|
|
|
|
}
|
|
|
|
|
|
|
|
exportData(extraProperties = false) {
|
|
|
|
return { error: this.error };
|
|
|
|
}
|
|
|
|
}
|
2012-08-30 06:11:56 +09:00
|
|
|
|
2021-05-02 23:11:01 +09:00
|
|
|
export { ErrorFont, Font };
|