volleyball-dev-frontend/node_modules/@react-pdf/font/lib/index.browser.js
2025-06-02 16:42:16 +00:00

477 lines
14 KiB
JavaScript

import 'is-url';
import * as fontkit from 'fontkit';
import { PDFFont } from '@react-pdf/pdfkit';
// @ts-expect-error ts being silly
const STANDARD_FONTS = [
'Courier',
'Courier-Bold',
'Courier-Oblique',
'Courier-BoldOblique',
'Helvetica',
'Helvetica-Bold',
'Helvetica-Oblique',
'Helvetica-BoldOblique',
'Times-Roman',
'Times-Bold',
'Times-Italic',
'Times-BoldItalic',
];
class StandardFont {
name;
src;
fullName;
familyName;
subfamilyName;
postscriptName;
copyright;
version;
underlinePosition;
underlineThickness;
italicAngle;
bbox;
'OS/2';
hhea;
numGlyphs;
characterSet;
availableFeatures;
type;
constructor(src) {
this.name = src;
this.fullName = src;
this.familyName = src;
this.subfamilyName = src;
this.type = 'STANDARD';
this.postscriptName = src;
this.availableFeatures = [];
this.copyright = '';
this.version = 1;
this.underlinePosition = -100;
this.underlineThickness = 50;
this.italicAngle = 0;
this.bbox = {};
this['OS/2'] = {};
this.hhea = {};
this.numGlyphs = 0;
this.characterSet = [];
this.src = PDFFont.open(null, src);
}
encode(str) {
return this.src.encode(str);
}
layout(str) {
const [encoded, positions] = this.encode(str);
const glyphs = encoded.map((g, i) => {
const glyph = this.getGlyph(parseInt(g, 16));
glyph.advanceWidth = positions[i].advanceWidth;
return glyph;
});
const advanceWidth = positions.reduce((acc, p) => acc + p.advanceWidth, 0);
return {
positions,
stringIndices: positions.map((_, i) => i),
glyphs,
script: 'latin',
language: 'dflt',
direction: 'ltr',
features: {},
advanceWidth,
advanceHeight: 0,
bbox: undefined,
};
}
glyphForCodePoint(codePoint) {
const glyph = this.getGlyph(codePoint);
glyph.advanceWidth = 400;
return glyph;
}
getGlyph(id) {
return {
id,
codePoints: [id],
isLigature: false,
name: this.src.font.characterToGlyph(id),
_font: this.src,
// @ts-expect-error assign proper value
advanceWidth: undefined,
};
}
hasGlyphForCodePoint(codePoint) {
return this.src.font.characterToGlyph(codePoint) !== '.notdef';
}
// Based on empirical observation
get ascent() {
return 900;
}
// Based on empirical observation
get capHeight() {
switch (this.name) {
case 'Times-Roman':
case 'Times-Bold':
case 'Times-Italic':
case 'Times-BoldItalic':
return 650;
case 'Courier':
case 'Courier-Bold':
case 'Courier-Oblique':
case 'Courier-BoldOblique':
return 550;
default:
return 690;
}
}
// Based on empirical observation
get xHeight() {
switch (this.name) {
case 'Times-Roman':
case 'Times-Bold':
case 'Times-Italic':
case 'Times-BoldItalic':
return 440;
case 'Courier':
case 'Courier-Bold':
case 'Courier-Oblique':
case 'Courier-BoldOblique':
return 390;
default:
return 490;
}
}
// Based on empirical observation
get descent() {
switch (this.name) {
case 'Times-Roman':
case 'Times-Bold':
case 'Times-Italic':
case 'Times-BoldItalic':
return -220;
case 'Courier':
case 'Courier-Bold':
case 'Courier-Oblique':
case 'Courier-BoldOblique':
return -230;
default:
return -200;
}
}
get lineGap() {
return 0;
}
get unitsPerEm() {
return 1000;
}
stringsForGlyph() {
throw new Error('Method not implemented.');
}
glyphsForString() {
throw new Error('Method not implemented.');
}
widthOfGlyph() {
throw new Error('Method not implemented.');
}
getAvailableFeatures() {
throw new Error('Method not implemented.');
}
createSubset() {
throw new Error('Method not implemented.');
}
getVariation() {
throw new Error('Method not implemented.');
}
getFont() {
throw new Error('Method not implemented.');
}
getName() {
throw new Error('Method not implemented.');
}
setDefaultLanguage() {
throw new Error('Method not implemented.');
}
}
const fetchFont = async (src, options) => {
const response = await fetch(src, options);
const data = await response.arrayBuffer();
return new Uint8Array(data);
};
const isDataUrl = (dataUrl) => {
const header = dataUrl.split(',')[0];
const hasDataPrefix = header.substring(0, 5) === 'data:';
const hasBase64Prefix = header.split(';')[1] === 'base64';
return hasDataPrefix && hasBase64Prefix;
};
class FontSource {
src;
fontFamily;
fontStyle;
fontWeight;
data;
options;
loadResultPromise;
constructor(src, fontFamily, fontStyle, fontWeight, options) {
this.src = src;
this.fontFamily = fontFamily;
this.fontStyle = fontStyle || 'normal';
this.fontWeight = fontWeight || 400;
this.data = null;
this.options = options || {};
this.loadResultPromise = null;
}
async _load() {
const { postscriptName } = this.options;
let data = null;
if (STANDARD_FONTS.includes(this.src)) {
data = new StandardFont(this.src);
}
else if (isDataUrl(this.src)) {
const raw = this.src.split(',')[1];
const uint8Array = new Uint8Array(atob(raw)
.split('')
.map((c) => c.charCodeAt(0)));
data = fontkit.create(uint8Array, postscriptName);
}
else {
const { headers, body, method = 'GET' } = this.options;
const buffer = await fetchFont(this.src, { method, body, headers });
data = fontkit.create(buffer, postscriptName);
}
if (data && 'fonts' in data) {
throw new Error('Font collection is not supported');
}
this.data = data;
}
async load() {
if (this.loadResultPromise === null) {
this.loadResultPromise = this._load();
}
return this.loadResultPromise;
}
}
const FONT_WEIGHTS = {
thin: 100,
hairline: 100,
ultralight: 200,
extralight: 200,
light: 300,
normal: 400,
medium: 500,
semibold: 600,
demibold: 600,
bold: 700,
ultrabold: 800,
extrabold: 800,
heavy: 900,
black: 900,
};
const resolveFontWeight = (value) => {
return typeof value === 'string' ? FONT_WEIGHTS[value] : value;
};
const sortByFontWeight = (a, b) => a.fontWeight - b.fontWeight;
class FontFamily {
family;
sources;
static create(family) {
return new FontFamily(family);
}
constructor(family) {
this.family = family;
this.sources = [];
}
register({ src, fontWeight, fontStyle, ...options }) {
const numericFontWeight = fontWeight
? resolveFontWeight(fontWeight)
: undefined;
this.sources.push(new FontSource(src, this.family, fontStyle, numericFontWeight, options));
}
resolve(descriptor) {
const { fontWeight = 400, fontStyle = 'normal' } = descriptor;
const styleSources = this.sources.filter((s) => s.fontStyle === fontStyle);
const exactFit = styleSources.find((s) => s.fontWeight === fontWeight);
if (exactFit)
return exactFit;
// Weight resolution. https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight#Fallback_weights
let font = null;
const numericFontWeight = resolveFontWeight(fontWeight);
if (numericFontWeight >= 400 && numericFontWeight <= 500) {
const leftOffset = styleSources.filter((s) => s.fontWeight <= numericFontWeight);
const rightOffset = styleSources.filter((s) => s.fontWeight > 500);
const fit = styleSources.filter((s) => s.fontWeight >= numericFontWeight && s.fontWeight < 500);
font = fit[0] || leftOffset[leftOffset.length - 1] || rightOffset[0];
}
const lt = styleSources
.filter((s) => s.fontWeight < numericFontWeight)
.sort(sortByFontWeight);
const gt = styleSources
.filter((s) => s.fontWeight > numericFontWeight)
.sort(sortByFontWeight);
if (numericFontWeight < 400) {
font = lt[lt.length - 1] || gt[0];
}
if (numericFontWeight > 500) {
font = gt[0] || lt[lt.length - 1];
}
if (!font) {
throw new Error(`Could not resolve font for ${this.family}, fontWeight ${fontWeight}, fontStyle ${fontStyle}`);
}
return font;
}
}
class FontStore {
fontFamilies = {};
emojiSource = null;
constructor() {
this.register({
family: 'Helvetica',
fonts: [
{ src: 'Helvetica', fontStyle: 'normal', fontWeight: 400 },
{ src: 'Helvetica-Bold', fontStyle: 'normal', fontWeight: 700 },
{ src: 'Helvetica-Oblique', fontStyle: 'italic', fontWeight: 400 },
{ src: 'Helvetica-BoldOblique', fontStyle: 'italic', fontWeight: 700 },
],
});
this.register({
family: 'Courier',
fonts: [
{ src: 'Courier', fontStyle: 'normal', fontWeight: 400 },
{ src: 'Courier-Bold', fontStyle: 'normal', fontWeight: 700 },
{ src: 'Courier-Oblique', fontStyle: 'italic', fontWeight: 400 },
{ src: 'Courier-BoldOblique', fontStyle: 'italic', fontWeight: 700 },
],
});
this.register({
family: 'Times-Roman',
fonts: [
{ src: 'Times-Roman', fontStyle: 'normal', fontWeight: 400 },
{ src: 'Times-Bold', fontStyle: 'normal', fontWeight: 700 },
{ src: 'Times-Italic', fontStyle: 'italic', fontWeight: 400 },
{ src: 'Times-BoldItalic', fontStyle: 'italic', fontWeight: 700 },
],
});
// For backwards compatibility
this.register({
family: 'Helvetica-Bold',
src: 'Helvetica-Bold',
});
this.register({
family: 'Helvetica-Oblique',
src: 'Helvetica-Oblique',
});
this.register({
family: 'Helvetica-BoldOblique',
src: 'Helvetica-BoldOblique',
});
this.register({
family: 'Courier-Bold',
src: 'Courier-Bold',
});
this.register({
family: 'Courier-Oblique',
src: 'Courier-Oblique',
});
this.register({
family: 'Courier-BoldOblique',
src: 'Courier-BoldOblique',
});
this.register({
family: 'Times-Bold',
src: 'Times-Bold',
});
this.register({
family: 'Times-Italic',
src: 'Times-Italic',
});
this.register({
family: 'Times-BoldItalic',
src: 'Times-BoldItalic',
});
// Load default fonts
this.load({
fontFamily: 'Helvetica',
fontStyle: 'normal',
fontWeight: 400,
});
this.load({
fontFamily: 'Helvetica',
fontStyle: 'normal',
fontWeight: 700,
});
this.load({
fontFamily: 'Helvetica',
fontStyle: 'italic',
fontWeight: 400,
});
this.load({
fontFamily: 'Helvetica',
fontStyle: 'italic',
fontWeight: 700,
});
}
hyphenationCallback = null;
register = (data) => {
const { family } = data;
if (!this.fontFamilies[family]) {
this.fontFamilies[family] = FontFamily.create(family);
}
// Bulk loading
if ('fonts' in data) {
for (let i = 0; i < data.fonts.length; i += 1) {
const { src, fontStyle, fontWeight, ...options } = data.fonts[i];
this.fontFamilies[family].register({
src,
fontStyle,
fontWeight,
...options,
});
}
}
else {
const { src, fontStyle, fontWeight, ...options } = data;
this.fontFamilies[family].register({
src,
fontStyle,
fontWeight,
...options,
});
}
};
registerEmojiSource = (emojiSource) => {
this.emojiSource = emojiSource;
};
registerHyphenationCallback = (callback) => {
this.hyphenationCallback = callback;
};
getFont = (descriptor) => {
const { fontFamily } = descriptor;
if (!this.fontFamilies[fontFamily]) {
throw new Error(`Font family not registered: ${fontFamily}. Please register it calling Font.register() method.`);
}
return this.fontFamilies[fontFamily].resolve(descriptor);
};
load = async (descriptor) => {
const font = this.getFont(descriptor);
if (font)
await font.load();
};
reset = () => {
const keys = Object.keys(this.fontFamilies);
for (let i = 0; i < keys.length; i += 1) {
const key = keys[i];
for (let j = 0; j < this.fontFamilies[key].sources.length; j++) {
const fontSource = this.fontFamilies[key].sources[j];
fontSource.data = null;
}
}
};
clear = () => {
this.fontFamilies = {};
};
getRegisteredFonts = () => this.fontFamilies;
getEmojiSource = () => this.emojiSource;
getHyphenationCallback = () => this.hyphenationCallback;
getRegisteredFontFamilies = () => Object.keys(this.fontFamilies);
}
export { FontStore as default };