Merge pull request #12726 from brendandahl/standard-fonts
[api-minor] Include and use the 14 standard font files.
This commit is contained in:
commit
e7dc822e74
@ -54,6 +54,10 @@ const pdfjsLib = require("pdfjs-dist/legacy/build/pdf.js");
|
|||||||
const CMAP_URL = "../../../node_modules/pdfjs-dist/cmaps/";
|
const CMAP_URL = "../../../node_modules/pdfjs-dist/cmaps/";
|
||||||
const CMAP_PACKED = true;
|
const CMAP_PACKED = true;
|
||||||
|
|
||||||
|
// Where the standard fonts are located.
|
||||||
|
const STANDARD_FONT_DATA_URL =
|
||||||
|
"../../../node_modules/pdfjs-dist/standard_fonts/";
|
||||||
|
|
||||||
// Loading file from file system into typed array.
|
// Loading file from file system into typed array.
|
||||||
const pdfPath =
|
const pdfPath =
|
||||||
process.argv[2] || "../../../web/compressed.tracemonkey-pldi-09.pdf";
|
process.argv[2] || "../../../web/compressed.tracemonkey-pldi-09.pdf";
|
||||||
@ -64,6 +68,7 @@ const loadingTask = pdfjsLib.getDocument({
|
|||||||
data,
|
data,
|
||||||
cMapUrl: CMAP_URL,
|
cMapUrl: CMAP_URL,
|
||||||
cMapPacked: CMAP_PACKED,
|
cMapPacked: CMAP_PACKED,
|
||||||
|
standardFontDataUrl: STANDARD_FONT_DATA_URL,
|
||||||
});
|
});
|
||||||
loadingTask.promise
|
loadingTask.promise
|
||||||
.then(function (pdfDocument) {
|
.then(function (pdfDocument) {
|
||||||
|
BIN
external/standard_fonts/FoxitDingbats.pfb
vendored
Normal file
BIN
external/standard_fonts/FoxitDingbats.pfb
vendored
Normal file
Binary file not shown.
BIN
external/standard_fonts/FoxitFixed.pfb
vendored
Normal file
BIN
external/standard_fonts/FoxitFixed.pfb
vendored
Normal file
Binary file not shown.
BIN
external/standard_fonts/FoxitFixedBold.pfb
vendored
Normal file
BIN
external/standard_fonts/FoxitFixedBold.pfb
vendored
Normal file
Binary file not shown.
BIN
external/standard_fonts/FoxitFixedBoldItalic.pfb
vendored
Normal file
BIN
external/standard_fonts/FoxitFixedBoldItalic.pfb
vendored
Normal file
Binary file not shown.
BIN
external/standard_fonts/FoxitFixedItalic.pfb
vendored
Normal file
BIN
external/standard_fonts/FoxitFixedItalic.pfb
vendored
Normal file
Binary file not shown.
BIN
external/standard_fonts/FoxitSans.pfb
vendored
Normal file
BIN
external/standard_fonts/FoxitSans.pfb
vendored
Normal file
Binary file not shown.
BIN
external/standard_fonts/FoxitSansBold.pfb
vendored
Normal file
BIN
external/standard_fonts/FoxitSansBold.pfb
vendored
Normal file
Binary file not shown.
BIN
external/standard_fonts/FoxitSansBoldItalic.pfb
vendored
Normal file
BIN
external/standard_fonts/FoxitSansBoldItalic.pfb
vendored
Normal file
Binary file not shown.
BIN
external/standard_fonts/FoxitSansItalic.pfb
vendored
Normal file
BIN
external/standard_fonts/FoxitSansItalic.pfb
vendored
Normal file
Binary file not shown.
BIN
external/standard_fonts/FoxitSerif.pfb
vendored
Normal file
BIN
external/standard_fonts/FoxitSerif.pfb
vendored
Normal file
Binary file not shown.
BIN
external/standard_fonts/FoxitSerifBold.pfb
vendored
Normal file
BIN
external/standard_fonts/FoxitSerifBold.pfb
vendored
Normal file
Binary file not shown.
BIN
external/standard_fonts/FoxitSerifBoldItalic.pfb
vendored
Normal file
BIN
external/standard_fonts/FoxitSerifBoldItalic.pfb
vendored
Normal file
Binary file not shown.
BIN
external/standard_fonts/FoxitSerifItalic.pfb
vendored
Normal file
BIN
external/standard_fonts/FoxitSerifItalic.pfb
vendored
Normal file
Binary file not shown.
BIN
external/standard_fonts/FoxitSymbol.pfb
vendored
Normal file
BIN
external/standard_fonts/FoxitSymbol.pfb
vendored
Normal file
Binary file not shown.
27
external/standard_fonts/LICENSE
vendored
Normal file
27
external/standard_fonts/LICENSE
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2014 PDFium Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
11
external/standard_fonts/README.md
vendored
Normal file
11
external/standard_fonts/README.md
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
The files in this directory were extracted from Pdfium
|
||||||
|
|
||||||
|
Original copyright notice:
|
||||||
|
|
||||||
|
```
|
||||||
|
Copyright 2014 PDFium Authors. All rights reserved.
|
||||||
|
Use of this source code is governed by a BSD-style license that can be
|
||||||
|
found in the LICENSE file.
|
||||||
|
|
||||||
|
Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
|
||||||
|
```
|
44
gulpfile.js
44
gulpfile.js
@ -812,6 +812,14 @@ function buildGeneric(defines, dir) {
|
|||||||
base: "external/bcmaps",
|
base: "external/bcmaps",
|
||||||
})
|
})
|
||||||
.pipe(gulp.dest(dir + "web/cmaps")),
|
.pipe(gulp.dest(dir + "web/cmaps")),
|
||||||
|
gulp
|
||||||
|
.src(
|
||||||
|
["external/standard_fonts/*.pfb", "external/standard_fonts/LICENSE"],
|
||||||
|
{
|
||||||
|
base: "external/standard_fonts",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.pipe(gulp.dest(dir + "web/standard_fonts")),
|
||||||
preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")),
|
preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")),
|
||||||
preprocessCSS("web/viewer.css", "generic", defines, true)
|
preprocessCSS("web/viewer.css", "generic", defines, true)
|
||||||
.pipe(postcss([calc(), autoprefixer(AUTOPREFIXER_CONFIG)]))
|
.pipe(postcss([calc(), autoprefixer(AUTOPREFIXER_CONFIG)]))
|
||||||
@ -980,6 +988,14 @@ function buildMinified(defines, dir) {
|
|||||||
base: "external/bcmaps",
|
base: "external/bcmaps",
|
||||||
})
|
})
|
||||||
.pipe(gulp.dest(dir + "web/cmaps")),
|
.pipe(gulp.dest(dir + "web/cmaps")),
|
||||||
|
gulp
|
||||||
|
.src(
|
||||||
|
["external/standard_fonts/*.pfb", "external/standard_fonts/LICENSE"],
|
||||||
|
{
|
||||||
|
base: "external/standard_fonts",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.pipe(gulp.dest(dir + "web/standard_fonts")),
|
||||||
|
|
||||||
preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")),
|
preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")),
|
||||||
preprocessCSS("web/viewer.css", "minified", defines, true)
|
preprocessCSS("web/viewer.css", "minified", defines, true)
|
||||||
@ -1214,7 +1230,17 @@ gulp.task(
|
|||||||
base: "external/bcmaps",
|
base: "external/bcmaps",
|
||||||
})
|
})
|
||||||
.pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/cmaps")),
|
.pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/cmaps")),
|
||||||
|
gulp
|
||||||
|
.src(
|
||||||
|
[
|
||||||
|
"external/standard_fonts/*.pfb",
|
||||||
|
"external/standard_fonts/LICENSE",
|
||||||
|
],
|
||||||
|
{
|
||||||
|
base: "external/standard_fonts",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/standard_fonts")),
|
||||||
preprocessHTML("web/viewer.html", defines).pipe(
|
preprocessHTML("web/viewer.html", defines).pipe(
|
||||||
gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")
|
gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")
|
||||||
),
|
),
|
||||||
@ -1305,6 +1331,17 @@ gulp.task(
|
|||||||
base: "external/bcmaps",
|
base: "external/bcmaps",
|
||||||
})
|
})
|
||||||
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/cmaps")),
|
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/cmaps")),
|
||||||
|
gulp
|
||||||
|
.src(
|
||||||
|
[
|
||||||
|
"external/standard_fonts/*.pfb",
|
||||||
|
"external/standard_fonts/LICENSE",
|
||||||
|
],
|
||||||
|
{
|
||||||
|
base: "external/standard_fonts",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/standard_fonts")),
|
||||||
|
|
||||||
preprocessHTML("web/viewer.html", defines).pipe(
|
preprocessHTML("web/viewer.html", defines).pipe(
|
||||||
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")
|
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")
|
||||||
@ -2051,6 +2088,11 @@ gulp.task(
|
|||||||
gulp
|
gulp
|
||||||
.src(GENERIC_DIR + "web/cmaps/**/*", { base: GENERIC_DIR + "web" })
|
.src(GENERIC_DIR + "web/cmaps/**/*", { base: GENERIC_DIR + "web" })
|
||||||
.pipe(gulp.dest(DIST_DIR)),
|
.pipe(gulp.dest(DIST_DIR)),
|
||||||
|
gulp
|
||||||
|
.src(GENERIC_DIR + "web/standard_fonts/**/*", {
|
||||||
|
base: GENERIC_DIR + "web",
|
||||||
|
})
|
||||||
|
.pipe(gulp.dest(DIST_DIR)),
|
||||||
gulp
|
gulp
|
||||||
.src([
|
.src([
|
||||||
GENERIC_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.js",
|
GENERIC_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.js",
|
||||||
|
@ -74,7 +74,10 @@ class CFFFont {
|
|||||||
return charCodeToGlyphId;
|
return charCodeToGlyphId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const encoding = cff.encoding ? cff.encoding.encoding : null;
|
let encoding = cff.encoding ? cff.encoding.encoding : null;
|
||||||
|
if (properties.isInternalFont) {
|
||||||
|
encoding = properties.defaultEncoding;
|
||||||
|
}
|
||||||
charCodeToGlyphId = type1FontGlyphMapping(properties, encoding, charsets);
|
charCodeToGlyphId = type1FontGlyphMapping(properties, encoding, charsets);
|
||||||
return charCodeToGlyphId;
|
return charCodeToGlyphId;
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,9 @@ import {
|
|||||||
} from "./unicode.js";
|
} from "./unicode.js";
|
||||||
import {
|
import {
|
||||||
getSerifFonts,
|
getSerifFonts,
|
||||||
|
getStandardFontName,
|
||||||
getStdFontMap,
|
getStdFontMap,
|
||||||
|
getStdFontNameToFileMap,
|
||||||
getSymbolsFonts,
|
getSymbolsFonts,
|
||||||
} from "./standard_fonts.js";
|
} from "./standard_fonts.js";
|
||||||
import { getTilingPatternIR, Pattern } from "./pattern.js";
|
import { getTilingPatternIR, Pattern } from "./pattern.js";
|
||||||
@ -77,6 +79,7 @@ import {
|
|||||||
LocalImageCache,
|
LocalImageCache,
|
||||||
LocalTilingPatternCache,
|
LocalTilingPatternCache,
|
||||||
} from "./image_utils.js";
|
} from "./image_utils.js";
|
||||||
|
import { NullStream, Stream } from "./stream.js";
|
||||||
import { bidi } from "./bidi.js";
|
import { bidi } from "./bidi.js";
|
||||||
import { ColorSpace } from "./colorspace.js";
|
import { ColorSpace } from "./colorspace.js";
|
||||||
import { DecodeStream } from "./decode_stream.js";
|
import { DecodeStream } from "./decode_stream.js";
|
||||||
@ -84,7 +87,6 @@ import { getGlyphsUnicode } from "./glyphlist.js";
|
|||||||
import { getLookupTableFactory } from "./core_utils.js";
|
import { getLookupTableFactory } from "./core_utils.js";
|
||||||
import { getMetrics } from "./metrics.js";
|
import { getMetrics } from "./metrics.js";
|
||||||
import { MurmurHash3_64 } from "./murmurhash3.js";
|
import { MurmurHash3_64 } from "./murmurhash3.js";
|
||||||
import { NullStream } from "./stream.js";
|
|
||||||
import { OperatorList } from "./operator_list.js";
|
import { OperatorList } from "./operator_list.js";
|
||||||
import { PDFImage } from "./image.js";
|
import { PDFImage } from "./image.js";
|
||||||
|
|
||||||
@ -94,6 +96,8 @@ const DefaultPartialEvaluatorOptions = Object.freeze({
|
|||||||
ignoreErrors: false,
|
ignoreErrors: false,
|
||||||
isEvalSupported: true,
|
isEvalSupported: true,
|
||||||
fontExtraProperties: false,
|
fontExtraProperties: false,
|
||||||
|
standardFontDataUrl: null,
|
||||||
|
useSystemFonts: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const PatternType = {
|
const PatternType = {
|
||||||
@ -381,6 +385,43 @@ class PartialEvaluator {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fetchStandardFontData(name) {
|
||||||
|
// The symbol fonts are not consistent across platforms, always load the
|
||||||
|
// font data for them.
|
||||||
|
if (
|
||||||
|
this.options.useSystemFonts &&
|
||||||
|
name !== "Symbol" &&
|
||||||
|
name !== "ZapfDingbats"
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const standardFontNameToFileName = getStdFontNameToFileMap();
|
||||||
|
const filename = standardFontNameToFileName[name];
|
||||||
|
if (this.options.standardFontDataUrl !== null) {
|
||||||
|
const url = `${this.options.standardFontDataUrl}${filename}.pfb`;
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) {
|
||||||
|
warn(
|
||||||
|
`fetchStandardFontData failed to fetch file "${url}" with "${response.statusText}".`
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Stream(await response.arrayBuffer());
|
||||||
|
}
|
||||||
|
// Get the data on the main thread instead.
|
||||||
|
try {
|
||||||
|
const data = await this.handler.sendWithPromise("FetchStandardFontData", {
|
||||||
|
filename,
|
||||||
|
});
|
||||||
|
return new Stream(data);
|
||||||
|
} catch (e) {
|
||||||
|
warn(
|
||||||
|
`fetchStandardFontData failed to fetch file "${filename}" with "${e}".`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
async buildFormXObject(
|
async buildFormXObject(
|
||||||
resources,
|
resources,
|
||||||
xobj,
|
xobj,
|
||||||
@ -3725,6 +3766,7 @@ class PartialEvaluator {
|
|||||||
properties = {
|
properties = {
|
||||||
type,
|
type,
|
||||||
name: baseFontName,
|
name: baseFontName,
|
||||||
|
loadedName: baseDict.loadedName,
|
||||||
widths: metrics.widths,
|
widths: metrics.widths,
|
||||||
defaultWidth: metrics.defaultWidth,
|
defaultWidth: metrics.defaultWidth,
|
||||||
flags,
|
flags,
|
||||||
@ -3734,6 +3776,13 @@ class PartialEvaluator {
|
|||||||
isType3Font,
|
isType3Font,
|
||||||
};
|
};
|
||||||
const widths = dict.get("Widths");
|
const widths = dict.get("Widths");
|
||||||
|
const standardFontName = getStandardFontName(baseFontName);
|
||||||
|
let file = null;
|
||||||
|
if (standardFontName) {
|
||||||
|
properties.isStandardFont = true;
|
||||||
|
file = await this.fetchStandardFontData(standardFontName);
|
||||||
|
properties.isInternalFont = !!file;
|
||||||
|
}
|
||||||
return this.extractDataStructures(dict, dict, properties).then(
|
return this.extractDataStructures(dict, dict, properties).then(
|
||||||
newProperties => {
|
newProperties => {
|
||||||
if (widths) {
|
if (widths) {
|
||||||
@ -3749,7 +3798,7 @@ class PartialEvaluator {
|
|||||||
newProperties
|
newProperties
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return new Font(baseFontName, null, newProperties);
|
return new Font(baseFontName, file, newProperties);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -3802,6 +3851,8 @@ class PartialEvaluator {
|
|||||||
warn(`translateFont - fetching "${fontName.name}" font file: "${ex}".`);
|
warn(`translateFont - fetching "${fontName.name}" font file: "${ex}".`);
|
||||||
fontFile = new NullStream();
|
fontFile = new NullStream();
|
||||||
}
|
}
|
||||||
|
let isStandardFont = false;
|
||||||
|
let isInternalFont = false;
|
||||||
if (fontFile) {
|
if (fontFile) {
|
||||||
if (fontFile.dict) {
|
if (fontFile.dict) {
|
||||||
const subtypeEntry = fontFile.dict.get("Subtype");
|
const subtypeEntry = fontFile.dict.get("Subtype");
|
||||||
@ -3812,6 +3863,13 @@ class PartialEvaluator {
|
|||||||
length2 = fontFile.dict.get("Length2");
|
length2 = fontFile.dict.get("Length2");
|
||||||
length3 = fontFile.dict.get("Length3");
|
length3 = fontFile.dict.get("Length3");
|
||||||
}
|
}
|
||||||
|
} else if (type === "Type1") {
|
||||||
|
const standardFontName = getStandardFontName(fontName.name);
|
||||||
|
if (standardFontName) {
|
||||||
|
isStandardFont = true;
|
||||||
|
fontFile = await this.fetchStandardFontData(standardFontName);
|
||||||
|
isInternalFont = !!fontFile;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
properties = {
|
properties = {
|
||||||
@ -3822,6 +3880,8 @@ class PartialEvaluator {
|
|||||||
length1,
|
length1,
|
||||||
length2,
|
length2,
|
||||||
length3,
|
length3,
|
||||||
|
isStandardFont,
|
||||||
|
isInternalFont,
|
||||||
loadedName: baseDict.loadedName,
|
loadedName: baseDict.loadedName,
|
||||||
composite,
|
composite,
|
||||||
fixedPitch: false,
|
fixedPitch: false,
|
||||||
|
@ -30,6 +30,7 @@ import {
|
|||||||
FontFlags,
|
FontFlags,
|
||||||
getFontType,
|
getFontType,
|
||||||
MacStandardGlyphOrdering,
|
MacStandardGlyphOrdering,
|
||||||
|
normalizeFontName,
|
||||||
recoverGlyphName,
|
recoverGlyphName,
|
||||||
SEAC_ANALYSIS_ENABLED,
|
SEAC_ANALYSIS_ENABLED,
|
||||||
} from "./fonts_utils.js";
|
} from "./fonts_utils.js";
|
||||||
@ -130,6 +131,9 @@ function adjustWidths(properties) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function adjustToUnicode(properties, builtInEncoding) {
|
function adjustToUnicode(properties, builtInEncoding) {
|
||||||
|
if (properties.isInternalFont) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (properties.hasIncludedToUnicodeMap) {
|
if (properties.hasIncludedToUnicodeMap) {
|
||||||
return; // The font dictionary has a `ToUnicode` entry.
|
return; // The font dictionary has a `ToUnicode` entry.
|
||||||
}
|
}
|
||||||
@ -932,7 +936,7 @@ class Font {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.fontType = getFontType(type, subtype);
|
this.fontType = getFontType(type, subtype, properties.isStandardFont);
|
||||||
|
|
||||||
// Transfer some properties again that could change during font conversion
|
// Transfer some properties again that could change during font conversion
|
||||||
this.fontMatrix = properties.fontMatrix;
|
this.fontMatrix = properties.fontMatrix;
|
||||||
@ -971,7 +975,7 @@ class Font {
|
|||||||
const name = this.name;
|
const name = this.name;
|
||||||
const type = this.type;
|
const type = this.type;
|
||||||
const subtype = this.subtype;
|
const subtype = this.subtype;
|
||||||
let fontName = name.replace(/[,_]/g, "-").replace(/\s/g, "");
|
let fontName = normalizeFontName(name);
|
||||||
const stdFontMap = getStdFontMap(),
|
const stdFontMap = getStdFontMap(),
|
||||||
nonStdFontMap = getNonStdFontMap();
|
nonStdFontMap = getNonStdFontMap();
|
||||||
const isStandardFont = !!stdFontMap[fontName];
|
const isStandardFont = !!stdFontMap[fontName];
|
||||||
@ -1090,7 +1094,7 @@ class Font {
|
|||||||
this.toFontChar = map;
|
this.toFontChar = map;
|
||||||
}
|
}
|
||||||
this.loadedName = fontName.split("-")[0];
|
this.loadedName = fontName.split("-")[0];
|
||||||
this.fontType = getFontType(type, subtype);
|
this.fontType = getFontType(type, subtype, properties.isStandardFont);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAndRepair(name, font, properties) {
|
checkAndRepair(name, font, properties) {
|
||||||
|
@ -78,9 +78,12 @@ const MacStandardGlyphOrdering = [
|
|||||||
"threequarters", "franc", "Gbreve", "gbreve", "Idotaccent", "Scedilla",
|
"threequarters", "franc", "Gbreve", "gbreve", "Idotaccent", "Scedilla",
|
||||||
"scedilla", "Cacute", "cacute", "Ccaron", "ccaron", "dcroat"];
|
"scedilla", "Cacute", "cacute", "Ccaron", "ccaron", "dcroat"];
|
||||||
|
|
||||||
function getFontType(type, subtype) {
|
function getFontType(type, subtype, isStandardFont = false) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "Type1":
|
case "Type1":
|
||||||
|
if (isStandardFont) {
|
||||||
|
return FontType.TYPE1STANDARD;
|
||||||
|
}
|
||||||
return subtype === "Type1C" ? FontType.TYPE1C : FontType.TYPE1;
|
return subtype === "Type1C" ? FontType.TYPE1C : FontType.TYPE1;
|
||||||
case "CIDFontType0":
|
case "CIDFontType0":
|
||||||
return subtype === "CIDFontType0C"
|
return subtype === "CIDFontType0C"
|
||||||
@ -135,7 +138,17 @@ function type1FontGlyphMapping(properties, builtInEncoding, glyphNames) {
|
|||||||
let glyphId, charCode, baseEncoding;
|
let glyphId, charCode, baseEncoding;
|
||||||
const isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
|
const isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
|
||||||
|
|
||||||
if (properties.baseEncodingName) {
|
if (properties.isInternalFont) {
|
||||||
|
baseEncoding = builtInEncoding;
|
||||||
|
for (charCode = 0; charCode < baseEncoding.length; charCode++) {
|
||||||
|
glyphId = glyphNames.indexOf(baseEncoding[charCode]);
|
||||||
|
if (glyphId >= 0) {
|
||||||
|
charCodeToGlyphId[charCode] = glyphId;
|
||||||
|
} else {
|
||||||
|
charCodeToGlyphId[charCode] = 0; // notdef
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (properties.baseEncodingName) {
|
||||||
// If a valid base encoding name was used, the mapping is initialized with
|
// If a valid base encoding name was used, the mapping is initialized with
|
||||||
// that.
|
// that.
|
||||||
baseEncoding = getEncoding(properties.baseEncodingName);
|
baseEncoding = getEncoding(properties.baseEncodingName);
|
||||||
@ -193,10 +206,15 @@ function type1FontGlyphMapping(properties, builtInEncoding, glyphNames) {
|
|||||||
return charCodeToGlyphId;
|
return charCodeToGlyphId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeFontName(name) {
|
||||||
|
return name.replace(/[,_]/g, "-").replace(/\s/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
FontFlags,
|
FontFlags,
|
||||||
getFontType,
|
getFontType,
|
||||||
MacStandardGlyphOrdering,
|
MacStandardGlyphOrdering,
|
||||||
|
normalizeFontName,
|
||||||
recoverGlyphName,
|
recoverGlyphName,
|
||||||
SEAC_ANALYSIS_ENABLED,
|
SEAC_ANALYSIS_ENABLED,
|
||||||
type1FontGlyphMapping,
|
type1FontGlyphMapping,
|
||||||
|
@ -14,12 +14,30 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { getLookupTableFactory } from "./core_utils.js";
|
import { getLookupTableFactory } from "./core_utils.js";
|
||||||
|
import { normalizeFontName } from "./fonts_utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hold a map of decoded fonts and of the standard fourteen Type1
|
* Hold a map of decoded fonts and of the standard fourteen Type1
|
||||||
* fonts and their acronyms.
|
* fonts and their acronyms.
|
||||||
*/
|
*/
|
||||||
const getStdFontMap = getLookupTableFactory(function (t) {
|
const getStdFontMap = getLookupTableFactory(function (t) {
|
||||||
|
// The standard 14 fonts:
|
||||||
|
t["Times-Roman"] = "Times-Roman";
|
||||||
|
t.Helvetica = "Helvetica";
|
||||||
|
t.Courier = "Courier";
|
||||||
|
t.Symbol = "Symbol";
|
||||||
|
t["Times-Bold"] = "Times-Bold";
|
||||||
|
t["Helvetica-Bold"] = "Helvetica-Bold";
|
||||||
|
t["Courier-Bold"] = "Courier-Bold";
|
||||||
|
t.ZapfDingbats = "ZapfDingbats";
|
||||||
|
t["Times-Italic"] = "Times-Italic";
|
||||||
|
t["Helvetica-Oblique"] = "Helvetica-Oblique";
|
||||||
|
t["Courier-Oblique"] = "Courier-Oblique";
|
||||||
|
t["Times-BoldItalic"] = "Times-BoldItalic";
|
||||||
|
t["Helvetica-BoldOblique"] = "Helvetica-BoldOblique";
|
||||||
|
t["Courier-BoldOblique"] = "Courier-BoldOblique";
|
||||||
|
|
||||||
|
// Extra mappings
|
||||||
t.ArialNarrow = "Helvetica";
|
t.ArialNarrow = "Helvetica";
|
||||||
t["ArialNarrow-Bold"] = "Helvetica-Bold";
|
t["ArialNarrow-Bold"] = "Helvetica-Bold";
|
||||||
t["ArialNarrow-BoldItalic"] = "Helvetica-BoldOblique";
|
t["ArialNarrow-BoldItalic"] = "Helvetica-BoldOblique";
|
||||||
@ -40,7 +58,6 @@ const getStdFontMap = getLookupTableFactory(function (t) {
|
|||||||
t["Arial-BoldMT"] = "Helvetica-Bold";
|
t["Arial-BoldMT"] = "Helvetica-Bold";
|
||||||
t["Arial-ItalicMT"] = "Helvetica-Oblique";
|
t["Arial-ItalicMT"] = "Helvetica-Oblique";
|
||||||
t.ArialMT = "Helvetica";
|
t.ArialMT = "Helvetica";
|
||||||
t["Courier-Bold"] = "Courier-Bold";
|
|
||||||
t["Courier-BoldItalic"] = "Courier-BoldOblique";
|
t["Courier-BoldItalic"] = "Courier-BoldOblique";
|
||||||
t["Courier-Italic"] = "Courier-Oblique";
|
t["Courier-Italic"] = "Courier-Oblique";
|
||||||
t.CourierNew = "Courier";
|
t.CourierNew = "Courier";
|
||||||
@ -51,12 +68,8 @@ const getStdFontMap = getLookupTableFactory(function (t) {
|
|||||||
t["CourierNewPS-BoldMT"] = "Courier-Bold";
|
t["CourierNewPS-BoldMT"] = "Courier-Bold";
|
||||||
t["CourierNewPS-ItalicMT"] = "Courier-Oblique";
|
t["CourierNewPS-ItalicMT"] = "Courier-Oblique";
|
||||||
t.CourierNewPSMT = "Courier";
|
t.CourierNewPSMT = "Courier";
|
||||||
t.Helvetica = "Helvetica";
|
|
||||||
t["Helvetica-Bold"] = "Helvetica-Bold";
|
|
||||||
t["Helvetica-BoldItalic"] = "Helvetica-BoldOblique";
|
t["Helvetica-BoldItalic"] = "Helvetica-BoldOblique";
|
||||||
t["Helvetica-BoldOblique"] = "Helvetica-BoldOblique";
|
|
||||||
t["Helvetica-Italic"] = "Helvetica-Oblique";
|
t["Helvetica-Italic"] = "Helvetica-Oblique";
|
||||||
t["Helvetica-Oblique"] = "Helvetica-Oblique";
|
|
||||||
t["Symbol-Bold"] = "Symbol";
|
t["Symbol-Bold"] = "Symbol";
|
||||||
t["Symbol-BoldItalic"] = "Symbol";
|
t["Symbol-BoldItalic"] = "Symbol";
|
||||||
t["Symbol-Italic"] = "Symbol";
|
t["Symbol-Italic"] = "Symbol";
|
||||||
@ -77,6 +90,23 @@ const getStdFontMap = getLookupTableFactory(function (t) {
|
|||||||
t["TimesNewRomanPSMT-Italic"] = "Times-Italic";
|
t["TimesNewRomanPSMT-Italic"] = "Times-Italic";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getStdFontNameToFileMap = getLookupTableFactory(function (t) {
|
||||||
|
t.Courier = "FoxitFixed";
|
||||||
|
t["Courier-Bold"] = "FoxitFixedBold";
|
||||||
|
t["Courier-BoldOblique"] = "FoxitFixedBoldItalic";
|
||||||
|
t["Courier-Oblique"] = "FoxitFixedItalic";
|
||||||
|
t.Helvetica = "FoxitSans";
|
||||||
|
t["Helvetica-Bold"] = "FoxitSansBold";
|
||||||
|
t["Helvetica-BoldOblique"] = "FoxitSansBoldItalic";
|
||||||
|
t["Helvetica-Oblique"] = "FoxitSansItalic";
|
||||||
|
t["Times-Roman"] = "FoxitSerif";
|
||||||
|
t["Times-Bold"] = "FoxitSerifBold";
|
||||||
|
t["Times-BoldItalic"] = "FoxitSerifBoldItalic";
|
||||||
|
t["Times-Italic"] = "FoxitSerifItalic";
|
||||||
|
t.Symbol = "FoxitSymbol";
|
||||||
|
t.ZapfDingbats = "FoxitDingbats";
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the map of the non-standard fonts that might be included as
|
* Holds the map of the non-standard fonts that might be included as
|
||||||
* a standard fonts without glyph data.
|
* a standard fonts without glyph data.
|
||||||
@ -763,11 +793,19 @@ const getSupplementalGlyphMapForCalibri = getLookupTableFactory(function (t) {
|
|||||||
t[1086] = 45;
|
t[1086] = 45;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getStandardFontName(name) {
|
||||||
|
const fontName = normalizeFontName(name);
|
||||||
|
const stdFontMap = getStdFontMap();
|
||||||
|
return stdFontMap[fontName];
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getGlyphMapForStandardFonts,
|
getGlyphMapForStandardFonts,
|
||||||
getNonStdFontMap,
|
getNonStdFontMap,
|
||||||
getSerifFonts,
|
getSerifFonts,
|
||||||
|
getStandardFontName,
|
||||||
getStdFontMap,
|
getStdFontMap,
|
||||||
|
getStdFontNameToFileMap,
|
||||||
getSupplementalGlyphMapForArialBlack,
|
getSupplementalGlyphMapForArialBlack,
|
||||||
getSupplementalGlyphMapForCalibri,
|
getSupplementalGlyphMapForCalibri,
|
||||||
getSymbolsFonts,
|
getSymbolsFonts,
|
||||||
|
@ -411,6 +411,8 @@ class WorkerMessageHandler {
|
|||||||
ignoreErrors: data.ignoreErrors,
|
ignoreErrors: data.ignoreErrors,
|
||||||
isEvalSupported: data.isEvalSupported,
|
isEvalSupported: data.isEvalSupported,
|
||||||
fontExtraProperties: data.fontExtraProperties,
|
fontExtraProperties: data.fontExtraProperties,
|
||||||
|
useSystemFonts: data.useSystemFonts,
|
||||||
|
standardFontDataUrl: data.standardFontDataUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
getPdfManager(data, evaluatorOptions, data.enableXfa)
|
getPdfManager(data, evaluatorOptions, data.enableXfa)
|
||||||
|
@ -40,6 +40,7 @@ import {
|
|||||||
deprecated,
|
deprecated,
|
||||||
DOMCanvasFactory,
|
DOMCanvasFactory,
|
||||||
DOMCMapReaderFactory,
|
DOMCMapReaderFactory,
|
||||||
|
DOMStandardFontDataFactory,
|
||||||
isDataScheme,
|
isDataScheme,
|
||||||
loadScript,
|
loadScript,
|
||||||
PageViewport,
|
PageViewport,
|
||||||
@ -47,7 +48,11 @@ import {
|
|||||||
StatTimer,
|
StatTimer,
|
||||||
} from "./display_utils.js";
|
} from "./display_utils.js";
|
||||||
import { FontFaceObject, FontLoader } from "./font_loader.js";
|
import { FontFaceObject, FontLoader } from "./font_loader.js";
|
||||||
import { NodeCanvasFactory, NodeCMapReaderFactory } from "./node_utils.js";
|
import {
|
||||||
|
NodeCanvasFactory,
|
||||||
|
NodeCMapReaderFactory,
|
||||||
|
NodeStandardFontDataFactory,
|
||||||
|
} from "./node_utils.js";
|
||||||
import { AnnotationStorage } from "./annotation_storage.js";
|
import { AnnotationStorage } from "./annotation_storage.js";
|
||||||
import { apiCompatibilityParams } from "./api_compatibility.js";
|
import { apiCompatibilityParams } from "./api_compatibility.js";
|
||||||
import { CanvasGraphics } from "./canvas.js";
|
import { CanvasGraphics } from "./canvas.js";
|
||||||
@ -69,6 +74,10 @@ const DefaultCMapReaderFactory =
|
|||||||
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) && isNodeJS
|
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) && isNodeJS
|
||||||
? NodeCMapReaderFactory
|
? NodeCMapReaderFactory
|
||||||
: DOMCMapReaderFactory;
|
: DOMCMapReaderFactory;
|
||||||
|
const DefaultStandardFontDataFactory =
|
||||||
|
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) && isNodeJS
|
||||||
|
? NodeStandardFontDataFactory
|
||||||
|
: DOMStandardFontDataFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {function} IPDFStreamFactory
|
* @typedef {function} IPDFStreamFactory
|
||||||
@ -143,6 +152,19 @@ function setPDFNetworkStreamFactory(pdfNetworkStreamFactory) {
|
|||||||
* reading built-in CMap files. Providing a custom factory is useful for
|
* reading built-in CMap files. Providing a custom factory is useful for
|
||||||
* environments without Fetch API or `XMLHttpRequest` support, such as
|
* environments without Fetch API or `XMLHttpRequest` support, such as
|
||||||
* Node.js. The default value is {DOMCMapReaderFactory}.
|
* Node.js. The default value is {DOMCMapReaderFactory}.
|
||||||
|
* @property {boolean} [useSystemFonts] - When true, fonts that aren't embedded
|
||||||
|
* in the PDF will fallback to a system font. Defaults to true for web
|
||||||
|
* environments and false for node.
|
||||||
|
* @property {string} [standardFontDataUrl] - The URL where the standard font
|
||||||
|
* files are located. Include the trailing slash.
|
||||||
|
* @property {boolean} [useWorkerFetch] - Enable using fetch in the worker for
|
||||||
|
* resources. This currently only used for fetching the font data from the
|
||||||
|
* worker thread. When `true`, StandardFontDataFactory will be ignored. The
|
||||||
|
* default value is `true` in web environment and `false` for Node.
|
||||||
|
* @property {Object} [StandardFontDataFactory] - The factory that will be used
|
||||||
|
* when reading the standard font files. Providing a custom factory is useful
|
||||||
|
* for environments without Fetch API or `XMLHttpRequest` support, such as
|
||||||
|
* Node.js. The default value is {DOMStandardFontDataFactory}.
|
||||||
* @property {boolean} [stopAtErrors] - Reject certain promises, e.g.
|
* @property {boolean} [stopAtErrors] - Reject certain promises, e.g.
|
||||||
* `getOperatorList`, `getTextContent`, and `RenderTask`, when the associated
|
* `getOperatorList`, `getTextContent`, and `RenderTask`, when the associated
|
||||||
* PDF data cannot be successfully parsed, instead of attempting to recover
|
* PDF data cannot be successfully parsed, instead of attempting to recover
|
||||||
@ -287,6 +309,8 @@ function getDocument(src) {
|
|||||||
params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE;
|
params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE;
|
||||||
params.CMapReaderFactory =
|
params.CMapReaderFactory =
|
||||||
params.CMapReaderFactory || DefaultCMapReaderFactory;
|
params.CMapReaderFactory || DefaultCMapReaderFactory;
|
||||||
|
params.StandardFontDataFactory =
|
||||||
|
params.StandardFontDataFactory || DefaultStandardFontDataFactory;
|
||||||
params.ignoreErrors = params.stopAtErrors !== true;
|
params.ignoreErrors = params.stopAtErrors !== true;
|
||||||
params.fontExtraProperties = params.fontExtraProperties === true;
|
params.fontExtraProperties = params.fontExtraProperties === true;
|
||||||
params.pdfBug = params.pdfBug === true;
|
params.pdfBug = params.pdfBug === true;
|
||||||
@ -304,6 +328,13 @@ function getDocument(src) {
|
|||||||
if (!Number.isInteger(params.maxImageSize)) {
|
if (!Number.isInteger(params.maxImageSize)) {
|
||||||
params.maxImageSize = -1;
|
params.maxImageSize = -1;
|
||||||
}
|
}
|
||||||
|
if (typeof params.useSystemFonts !== "boolean") {
|
||||||
|
params.useSystemFonts = !isNodeJS;
|
||||||
|
}
|
||||||
|
if (typeof params.useWorkerFetch !== "boolean") {
|
||||||
|
params.useWorkerFetch =
|
||||||
|
params.StandardFontDataFactory === DOMStandardFontDataFactory;
|
||||||
|
}
|
||||||
if (typeof params.isEvalSupported !== "boolean") {
|
if (typeof params.isEvalSupported !== "boolean") {
|
||||||
params.isEvalSupported = true;
|
params.isEvalSupported = true;
|
||||||
}
|
}
|
||||||
@ -455,6 +486,10 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
|
|||||||
isEvalSupported: source.isEvalSupported,
|
isEvalSupported: source.isEvalSupported,
|
||||||
fontExtraProperties: source.fontExtraProperties,
|
fontExtraProperties: source.fontExtraProperties,
|
||||||
enableXfa: source.enableXfa,
|
enableXfa: source.enableXfa,
|
||||||
|
useSystemFonts: source.useSystemFonts,
|
||||||
|
standardFontDataUrl: source.useWorkerFetch
|
||||||
|
? source.standardFontDataUrl
|
||||||
|
: null,
|
||||||
})
|
})
|
||||||
.then(function (workerId) {
|
.then(function (workerId) {
|
||||||
if (worker.destroyed) {
|
if (worker.destroyed) {
|
||||||
@ -2243,6 +2278,9 @@ class WorkerTransport {
|
|||||||
baseUrl: params.cMapUrl,
|
baseUrl: params.cMapUrl,
|
||||||
isCompressed: params.cMapPacked,
|
isCompressed: params.cMapPacked,
|
||||||
});
|
});
|
||||||
|
this.StandardFontDataFactory = new params.StandardFontDataFactory({
|
||||||
|
baseUrl: params.standardFontDataUrl,
|
||||||
|
});
|
||||||
|
|
||||||
this.destroyed = false;
|
this.destroyed = false;
|
||||||
this.destroyCapability = null;
|
this.destroyCapability = null;
|
||||||
@ -2641,6 +2679,13 @@ class WorkerTransport {
|
|||||||
this._onUnsupportedFeature.bind(this)
|
this._onUnsupportedFeature.bind(this)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
messageHandler.on("FetchStandardFontData", data => {
|
||||||
|
if (this.destroyed) {
|
||||||
|
return Promise.reject(new Error("Worker was destroyed"));
|
||||||
|
}
|
||||||
|
return this.StandardFontDataFactory.fetch(data);
|
||||||
|
});
|
||||||
|
|
||||||
messageHandler.on("FetchBuiltInCMap", (data, sink) => {
|
messageHandler.on("FetchBuiltInCMap", (data, sink) => {
|
||||||
if (this.destroyed) {
|
if (this.destroyed) {
|
||||||
sink.error(new Error("Worker was destroyed"));
|
sink.error(new Error("Worker was destroyed"));
|
||||||
@ -3183,6 +3228,7 @@ export {
|
|||||||
build,
|
build,
|
||||||
DefaultCanvasFactory,
|
DefaultCanvasFactory,
|
||||||
DefaultCMapReaderFactory,
|
DefaultCMapReaderFactory,
|
||||||
|
DefaultStandardFontDataFactory,
|
||||||
getDocument,
|
getDocument,
|
||||||
LoopbackPort,
|
LoopbackPort,
|
||||||
PDFDataRangeTransport,
|
PDFDataRangeTransport,
|
||||||
|
@ -84,6 +84,56 @@ class DOMCanvasFactory extends BaseCanvasFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fetchData(url, asTypedArray) {
|
||||||
|
if (
|
||||||
|
(typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) ||
|
||||||
|
(isFetchSupported() && isValidFetchUrl(url, document.baseURI))
|
||||||
|
) {
|
||||||
|
return fetch(url).then(async response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(response.statusText);
|
||||||
|
}
|
||||||
|
let data;
|
||||||
|
if (asTypedArray) {
|
||||||
|
data = new Uint8Array(await response.arrayBuffer());
|
||||||
|
} else {
|
||||||
|
data = stringToBytes(await response.text());
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Fetch API is not supported.
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const request = new XMLHttpRequest();
|
||||||
|
request.open("GET", url, /* asTypedArray = */ true);
|
||||||
|
|
||||||
|
if (asTypedArray) {
|
||||||
|
request.responseType = "arraybuffer";
|
||||||
|
}
|
||||||
|
request.onreadystatechange = () => {
|
||||||
|
if (request.readyState !== XMLHttpRequest.DONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (request.status === 200 || request.status === 0) {
|
||||||
|
let data;
|
||||||
|
if (asTypedArray && request.response) {
|
||||||
|
data = new Uint8Array(request.response);
|
||||||
|
} else if (!asTypedArray && request.responseText) {
|
||||||
|
data = stringToBytes(request.responseText);
|
||||||
|
}
|
||||||
|
if (data) {
|
||||||
|
resolve(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reject(new Error(request.statusText));
|
||||||
|
};
|
||||||
|
|
||||||
|
request.send(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class BaseCMapReaderFactory {
|
class BaseCMapReaderFactory {
|
||||||
constructor({ baseUrl = null, isCompressed = false }) {
|
constructor({ baseUrl = null, isCompressed = false }) {
|
||||||
if (this.constructor === BaseCMapReaderFactory) {
|
if (this.constructor === BaseCMapReaderFactory) {
|
||||||
@ -125,56 +175,44 @@ class BaseCMapReaderFactory {
|
|||||||
|
|
||||||
class DOMCMapReaderFactory extends BaseCMapReaderFactory {
|
class DOMCMapReaderFactory extends BaseCMapReaderFactory {
|
||||||
_fetchData(url, compressionType) {
|
_fetchData(url, compressionType) {
|
||||||
if (
|
return fetchData(url, /* asTypedArray = */ this.isCompressed).then(data => {
|
||||||
(typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) ||
|
return { cMapData: data, compressionType };
|
||||||
(isFetchSupported() && isValidFetchUrl(url, document.baseURI))
|
|
||||||
) {
|
|
||||||
return fetch(url).then(async response => {
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(response.statusText);
|
|
||||||
}
|
|
||||||
let cMapData;
|
|
||||||
if (this.isCompressed) {
|
|
||||||
cMapData = new Uint8Array(await response.arrayBuffer());
|
|
||||||
} else {
|
|
||||||
cMapData = stringToBytes(await response.text());
|
|
||||||
}
|
|
||||||
return { cMapData, compressionType };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Fetch API is not supported.
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const request = new XMLHttpRequest();
|
|
||||||
request.open("GET", url, true);
|
|
||||||
|
|
||||||
if (this.isCompressed) {
|
|
||||||
request.responseType = "arraybuffer";
|
|
||||||
}
|
|
||||||
request.onreadystatechange = () => {
|
|
||||||
if (request.readyState !== XMLHttpRequest.DONE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (request.status === 200 || request.status === 0) {
|
|
||||||
let cMapData;
|
|
||||||
if (this.isCompressed && request.response) {
|
|
||||||
cMapData = new Uint8Array(request.response);
|
|
||||||
} else if (!this.isCompressed && request.responseText) {
|
|
||||||
cMapData = stringToBytes(request.responseText);
|
|
||||||
}
|
|
||||||
if (cMapData) {
|
|
||||||
resolve({ cMapData, compressionType });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reject(new Error(request.statusText));
|
|
||||||
};
|
|
||||||
|
|
||||||
request.send(null);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class BaseStandardFontDataFactory {
|
||||||
|
constructor({ baseUrl = null }) {
|
||||||
|
if (this.constructor === BaseStandardFontDataFactory) {
|
||||||
|
unreachable("Cannot initialize BaseStandardFontDataFactory.");
|
||||||
|
}
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetch({ filename }) {
|
||||||
|
if (!this.baseUrl) {
|
||||||
|
throw new Error(
|
||||||
|
'The standard font "baseUrl" parameter must be specified, ensure that ' +
|
||||||
|
'the "standardFontDataUrl" API parameter is provided.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!filename) {
|
||||||
|
throw new Error("Font filename must be specified.");
|
||||||
|
}
|
||||||
|
const url = this.baseUrl + filename + ".pfb";
|
||||||
|
|
||||||
|
return this._fetchData(url).catch(reason => {
|
||||||
|
throw new Error(`Unable to load font data at: ${url}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DOMStandardFontDataFactory extends BaseStandardFontDataFactory {
|
||||||
|
_fetchData(url) {
|
||||||
|
return fetchData(url, /* asTypedArray = */ true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class DOMSVGFactory {
|
class DOMSVGFactory {
|
||||||
create(width, height) {
|
create(width, height) {
|
||||||
assert(width > 0 && height > 0, "Invalid SVG dimensions");
|
assert(width > 0 && height > 0, "Invalid SVG dimensions");
|
||||||
@ -704,10 +742,12 @@ export {
|
|||||||
addLinkAttributes,
|
addLinkAttributes,
|
||||||
BaseCanvasFactory,
|
BaseCanvasFactory,
|
||||||
BaseCMapReaderFactory,
|
BaseCMapReaderFactory,
|
||||||
|
BaseStandardFontDataFactory,
|
||||||
DEFAULT_LINK_REL,
|
DEFAULT_LINK_REL,
|
||||||
deprecated,
|
deprecated,
|
||||||
DOMCanvasFactory,
|
DOMCanvasFactory,
|
||||||
DOMCMapReaderFactory,
|
DOMCMapReaderFactory,
|
||||||
|
DOMStandardFontDataFactory,
|
||||||
DOMSVGFactory,
|
DOMSVGFactory,
|
||||||
getFilenameFromUrl,
|
getFilenameFromUrl,
|
||||||
getPdfFilenameFromUrl,
|
getPdfFilenameFromUrl,
|
||||||
|
@ -14,10 +14,27 @@
|
|||||||
*/
|
*/
|
||||||
/* globals __non_webpack_require__ */
|
/* globals __non_webpack_require__ */
|
||||||
|
|
||||||
import { BaseCanvasFactory, BaseCMapReaderFactory } from "./display_utils.js";
|
import {
|
||||||
|
BaseCanvasFactory,
|
||||||
|
BaseCMapReaderFactory,
|
||||||
|
BaseStandardFontDataFactory,
|
||||||
|
} from "./display_utils.js";
|
||||||
import { isNodeJS } from "../shared/is_node.js";
|
import { isNodeJS } from "../shared/is_node.js";
|
||||||
import { unreachable } from "../shared/util.js";
|
import { unreachable } from "../shared/util.js";
|
||||||
|
|
||||||
|
function fetchData(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const fs = __non_webpack_require__("fs");
|
||||||
|
fs.readFile(url, (error, data) => {
|
||||||
|
if (error || !data) {
|
||||||
|
reject(new Error(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(new Uint8Array(data));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let NodeCanvasFactory = class {
|
let NodeCanvasFactory = class {
|
||||||
constructor() {
|
constructor() {
|
||||||
unreachable("Not implemented: NodeCanvasFactory");
|
unreachable("Not implemented: NodeCanvasFactory");
|
||||||
@ -30,6 +47,12 @@ let NodeCMapReaderFactory = class {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let NodeStandardFontDataFactory = class {
|
||||||
|
constructor() {
|
||||||
|
unreachable("Not implemented: NodeStandardFontDataFactory");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if ((typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) && isNodeJS) {
|
if ((typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) && isNodeJS) {
|
||||||
NodeCanvasFactory = class extends BaseCanvasFactory {
|
NodeCanvasFactory = class extends BaseCanvasFactory {
|
||||||
create(width, height) {
|
create(width, height) {
|
||||||
@ -47,18 +70,21 @@ if ((typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) && isNodeJS) {
|
|||||||
|
|
||||||
NodeCMapReaderFactory = class extends BaseCMapReaderFactory {
|
NodeCMapReaderFactory = class extends BaseCMapReaderFactory {
|
||||||
_fetchData(url, compressionType) {
|
_fetchData(url, compressionType) {
|
||||||
return new Promise((resolve, reject) => {
|
return fetchData(url).then(data => {
|
||||||
const fs = __non_webpack_require__("fs");
|
return { cMapData: data, compressionType };
|
||||||
fs.readFile(url, (error, data) => {
|
|
||||||
if (error || !data) {
|
|
||||||
reject(new Error(error));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
resolve({ cMapData: new Uint8Array(data), compressionType });
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NodeStandardFontDataFactory = class extends BaseStandardFontDataFactory {
|
||||||
|
_fetchData(url) {
|
||||||
|
return fetchData(url);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export { NodeCanvasFactory, NodeCMapReaderFactory };
|
export {
|
||||||
|
NodeCanvasFactory,
|
||||||
|
NodeCMapReaderFactory,
|
||||||
|
NodeStandardFontDataFactory,
|
||||||
|
};
|
||||||
|
@ -190,6 +190,7 @@ const StreamType = {
|
|||||||
const FontType = {
|
const FontType = {
|
||||||
UNKNOWN: "UNKNOWN",
|
UNKNOWN: "UNKNOWN",
|
||||||
TYPE1: "TYPE1",
|
TYPE1: "TYPE1",
|
||||||
|
TYPE1STANDARD: "TYPE1STANDARD",
|
||||||
TYPE1C: "TYPE1C",
|
TYPE1C: "TYPE1C",
|
||||||
CIDFONTTYPE0: "CIDFONTTYPE0",
|
CIDFONTTYPE0: "CIDFONTTYPE0",
|
||||||
CIDFONTTYPE0C: "CIDFONTTYPE0C",
|
CIDFONTTYPE0C: "CIDFONTTYPE0C",
|
||||||
|
@ -21,6 +21,7 @@ const WAITING_TIME = 100; // ms
|
|||||||
const PDF_TO_CSS_UNITS = 96.0 / 72.0;
|
const PDF_TO_CSS_UNITS = 96.0 / 72.0;
|
||||||
const CMAP_URL = "../external/bcmaps/";
|
const CMAP_URL = "../external/bcmaps/";
|
||||||
const CMAP_PACKED = true;
|
const CMAP_PACKED = true;
|
||||||
|
const STANDARD_FONT_DATA_URL = "/build/generic/web/standard_fonts/";
|
||||||
const IMAGE_RESOURCES_PATH = "/web/images/";
|
const IMAGE_RESOURCES_PATH = "/web/images/";
|
||||||
const WORKER_SRC = "../build/generic/build/pdf.worker.js";
|
const WORKER_SRC = "../build/generic/build/pdf.worker.js";
|
||||||
const RENDER_TASK_ON_CONTINUE_DELAY = 5; // ms
|
const RENDER_TASK_ON_CONTINUE_DELAY = 5; // ms
|
||||||
@ -415,9 +416,12 @@ var Driver = (function DriverClosure() {
|
|||||||
password: task.password,
|
password: task.password,
|
||||||
cMapUrl: CMAP_URL,
|
cMapUrl: CMAP_URL,
|
||||||
cMapPacked: CMAP_PACKED,
|
cMapPacked: CMAP_PACKED,
|
||||||
|
standardFontDataUrl: STANDARD_FONT_DATA_URL,
|
||||||
disableRange: task.disableRange,
|
disableRange: task.disableRange,
|
||||||
disableAutoFetch: !task.enableAutoFetch,
|
disableAutoFetch: !task.enableAutoFetch,
|
||||||
pdfBug: true,
|
pdfBug: true,
|
||||||
|
useSystemFonts: task.useSystemFonts,
|
||||||
|
useWorkerFetch: task.useWorkerFetch,
|
||||||
});
|
});
|
||||||
loadingTask.promise.then(
|
loadingTask.promise.then(
|
||||||
doc => {
|
doc => {
|
||||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -216,6 +216,7 @@
|
|||||||
!bug1473809.pdf
|
!bug1473809.pdf
|
||||||
!issue12120_reduced.pdf
|
!issue12120_reduced.pdf
|
||||||
!pdfjsbad1586.pdf
|
!pdfjsbad1586.pdf
|
||||||
|
!standard_fonts.pdf
|
||||||
!freeculture.pdf
|
!freeculture.pdf
|
||||||
!issue6006.pdf
|
!issue6006.pdf
|
||||||
!pdfkit_compressed.pdf
|
!pdfkit_compressed.pdf
|
||||||
|
BIN
test/pdfs/standard_fonts.pdf
Normal file
BIN
test/pdfs/standard_fonts.pdf
Normal file
Binary file not shown.
@ -1974,6 +1974,28 @@
|
|||||||
"link": true,
|
"link": true,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{ "id": "standard_fonts_system_fonts",
|
||||||
|
"file": "pdfs/standard_fonts.pdf",
|
||||||
|
"md5": "bb3a9ab3322328be983e8b4e8089843a",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"useSystemFonts": true
|
||||||
|
},
|
||||||
|
{ "id": "standard_fonts_no_system_fonts",
|
||||||
|
"file": "pdfs/standard_fonts.pdf",
|
||||||
|
"md5": "bb3a9ab3322328be983e8b4e8089843a",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"useSystemFonts": false
|
||||||
|
},
|
||||||
|
{ "id": "standard_fonts_main_thread_fetch",
|
||||||
|
"file": "pdfs/standard_fonts.pdf",
|
||||||
|
"md5": "bb3a9ab3322328be983e8b4e8089843a",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"useSystemFonts": false,
|
||||||
|
"useWorkerFetch": false
|
||||||
|
},
|
||||||
{ "id": "issue4573",
|
{ "id": "issue4573",
|
||||||
"file": "pdfs/issue4573.pdf",
|
"file": "pdfs/issue4573.pdf",
|
||||||
"md5": "34b0c4fdee19e57033275b766c5f57a3",
|
"md5": "34b0c4fdee19e57033275b766c5f57a3",
|
||||||
|
@ -29,10 +29,18 @@ import {
|
|||||||
stringToBytes,
|
stringToBytes,
|
||||||
stringToUTF8String,
|
stringToUTF8String,
|
||||||
} from "../../src/shared/util.js";
|
} from "../../src/shared/util.js";
|
||||||
import { CMAP_PARAMS, createIdFactory, XRefMock } from "./test_utils.js";
|
import {
|
||||||
|
CMAP_PARAMS,
|
||||||
|
createIdFactory,
|
||||||
|
STANDARD_FONT_DATA_URL,
|
||||||
|
XRefMock,
|
||||||
|
} from "./test_utils.js";
|
||||||
|
import {
|
||||||
|
DefaultCMapReaderFactory,
|
||||||
|
DefaultStandardFontDataFactory,
|
||||||
|
} from "../../src/display/api.js";
|
||||||
import { Dict, Name, Ref, RefSetCache } from "../../src/core/primitives.js";
|
import { Dict, Name, Ref, RefSetCache } from "../../src/core/primitives.js";
|
||||||
import { Lexer, Parser } from "../../src/core/parser.js";
|
import { Lexer, Parser } from "../../src/core/parser.js";
|
||||||
import { DefaultCMapReaderFactory } from "../../src/display/api.js";
|
|
||||||
import { PartialEvaluator } from "../../src/core/evaluator.js";
|
import { PartialEvaluator } from "../../src/core/evaluator.js";
|
||||||
import { StringStream } from "../../src/core/stream.js";
|
import { StringStream } from "../../src/core/stream.js";
|
||||||
import { WorkerTask } from "../../src/core/worker.js";
|
import { WorkerTask } from "../../src/core/worker.js";
|
||||||
@ -68,6 +76,10 @@ describe("annotation", function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fontDataReader = new DefaultStandardFontDataFactory({
|
||||||
|
baseUrl: STANDARD_FONT_DATA_URL,
|
||||||
|
});
|
||||||
|
|
||||||
function HandlerMock() {
|
function HandlerMock() {
|
||||||
this.inputs = [];
|
this.inputs = [];
|
||||||
}
|
}
|
||||||
@ -75,6 +87,12 @@ describe("annotation", function () {
|
|||||||
send(name, data) {
|
send(name, data) {
|
||||||
this.inputs.push({ name, data });
|
this.inputs.push({ name, data });
|
||||||
},
|
},
|
||||||
|
sendWithPromise(name, data) {
|
||||||
|
if (name !== "FetchStandardFontData") {
|
||||||
|
return Promise.reject(new Error(`Unsupported mock ${name}.`));
|
||||||
|
}
|
||||||
|
return fontDataReader.fetch(data);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let pdfManagerMock, idFactoryMock, partialEvaluator;
|
let pdfManagerMock, idFactoryMock, partialEvaluator;
|
||||||
@ -2282,7 +2300,6 @@ describe("annotation", function () {
|
|||||||
]);
|
]);
|
||||||
const task = new WorkerTask("test print");
|
const task = new WorkerTask("test print");
|
||||||
const checkboxEvaluator = partialEvaluator.clone({ ignoreErrors: true });
|
const checkboxEvaluator = partialEvaluator.clone({ ignoreErrors: true });
|
||||||
|
|
||||||
const annotation = await AnnotationFactory.create(
|
const annotation = await AnnotationFactory.create(
|
||||||
xref,
|
xref,
|
||||||
buttonWidgetRef,
|
buttonWidgetRef,
|
||||||
@ -2306,7 +2323,7 @@ describe("annotation", function () {
|
|||||||
OPS.showText,
|
OPS.showText,
|
||||||
OPS.endAnnotation,
|
OPS.endAnnotation,
|
||||||
]);
|
]);
|
||||||
expect(operatorList.argsArray[3][0][0].fontChar).toEqual("✔");
|
expect(operatorList.argsArray[3][0][0].unicode).toEqual("4");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render checkboxes for printing", async function () {
|
it("should render checkboxes for printing", async function () {
|
||||||
|
@ -1518,18 +1518,20 @@ describe("api", function () {
|
|||||||
const pdfPage = await pdfDoc.getPage(1);
|
const pdfPage = await pdfDoc.getPage(1);
|
||||||
const { items, styles } = await pdfPage.getTextContent();
|
const { items, styles } = await pdfPage.getTextContent();
|
||||||
expect(items.length).toEqual(1);
|
expect(items.length).toEqual(1);
|
||||||
expect(Object.keys(styles)).toEqual(["Times"]);
|
// Font name will a random object id.
|
||||||
|
const fontName = items[0].fontName;
|
||||||
|
expect(Object.keys(styles)).toEqual([fontName]);
|
||||||
|
|
||||||
expect(items[0]).toEqual({
|
expect(items[0]).toEqual({
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
fontName: "Times",
|
fontName,
|
||||||
height: 18,
|
height: 18,
|
||||||
str: "Issue 8276",
|
str: "Issue 8276",
|
||||||
transform: [18, 0, 0, 18, 441.81, 708.4499999999999],
|
transform: [18, 0, 0, 18, 441.81, 708.4499999999999],
|
||||||
width: 77.49,
|
width: 77.49,
|
||||||
hasEOL: false,
|
hasEOL: false,
|
||||||
});
|
});
|
||||||
expect(styles.Times).toEqual({
|
expect(styles[fontName]).toEqual({
|
||||||
fontFamily: "serif",
|
fontFamily: "serif",
|
||||||
ascent: NaN,
|
ascent: NaN,
|
||||||
descent: NaN,
|
descent: NaN,
|
||||||
@ -1678,7 +1680,7 @@ describe("api", function () {
|
|||||||
const expectedStreamTypes = {};
|
const expectedStreamTypes = {};
|
||||||
expectedStreamTypes[StreamType.FLATE] = true;
|
expectedStreamTypes[StreamType.FLATE] = true;
|
||||||
const expectedFontTypes = {};
|
const expectedFontTypes = {};
|
||||||
expectedFontTypes[FontType.TYPE1] = true;
|
expectedFontTypes[FontType.TYPE1STANDARD] = true;
|
||||||
expectedFontTypes[FontType.CIDFONTTYPE2] = true;
|
expectedFontTypes[FontType.CIDFONTTYPE2] = true;
|
||||||
|
|
||||||
expect(stats).toEqual({
|
expect(stats).toEqual({
|
||||||
|
@ -26,6 +26,10 @@ const CMAP_PARAMS = {
|
|||||||
cMapPacked: true,
|
cMapPacked: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const STANDARD_FONT_DATA_URL = isNodeJS
|
||||||
|
? "./external/standard_fonts/"
|
||||||
|
: "../../external/standard_fonts/";
|
||||||
|
|
||||||
class DOMFileReaderFactory {
|
class DOMFileReaderFactory {
|
||||||
static async fetch(params) {
|
static async fetch(params) {
|
||||||
const response = await fetch(params.path);
|
const response = await fetch(params.path);
|
||||||
@ -61,6 +65,7 @@ function buildGetDocumentParams(filename, options) {
|
|||||||
params.url = isNodeJS
|
params.url = isNodeJS
|
||||||
? TEST_PDFS_PATH + filename
|
? TEST_PDFS_PATH + filename
|
||||||
: new URL(TEST_PDFS_PATH + filename, window.location).href;
|
: new URL(TEST_PDFS_PATH + filename, window.location).href;
|
||||||
|
params.standardFontDataUrl = STANDARD_FONT_DATA_URL;
|
||||||
|
|
||||||
for (const option in options) {
|
for (const option in options) {
|
||||||
params[option] = options[option];
|
params[option] = options[option];
|
||||||
@ -146,6 +151,7 @@ export {
|
|||||||
createIdFactory,
|
createIdFactory,
|
||||||
DefaultFileReaderFactory,
|
DefaultFileReaderFactory,
|
||||||
isEmptyObj,
|
isEmptyObj,
|
||||||
|
STANDARD_FONT_DATA_URL,
|
||||||
TEST_PDFS_PATH,
|
TEST_PDFS_PATH,
|
||||||
XRefMock,
|
XRefMock,
|
||||||
};
|
};
|
||||||
|
@ -217,6 +217,14 @@ const defaultOptions = {
|
|||||||
value: false,
|
value: false,
|
||||||
kind: OptionKind.API,
|
kind: OptionKind.API,
|
||||||
},
|
},
|
||||||
|
standardFontDataUrl: {
|
||||||
|
/** @type {string} */
|
||||||
|
value:
|
||||||
|
typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")
|
||||||
|
? "../external/standard_fonts/"
|
||||||
|
: "../web/standard_fonts/",
|
||||||
|
kind: OptionKind.API,
|
||||||
|
},
|
||||||
verbosity: {
|
verbosity: {
|
||||||
/** @type {number} */
|
/** @type {number} */
|
||||||
value: 1,
|
value: 1,
|
||||||
|
Loading…
Reference in New Issue
Block a user