mirror of
https://github.com/foliojs/pdfkit.git
synced 2025-12-08 20:15:54 +00:00
227 lines
7.0 KiB
JavaScript
227 lines
7.0 KiB
JavaScript
import fs from 'fs';
|
|
|
|
const WIN_ANSI_MAP = {
|
|
402: 131,
|
|
8211: 150,
|
|
8212: 151,
|
|
8216: 145,
|
|
8217: 146,
|
|
8218: 130,
|
|
8220: 147,
|
|
8221: 148,
|
|
8222: 132,
|
|
8224: 134,
|
|
8225: 135,
|
|
8226: 149,
|
|
8230: 133,
|
|
8364: 128,
|
|
8240: 137,
|
|
8249: 139,
|
|
8250: 155,
|
|
710: 136,
|
|
8482: 153,
|
|
338: 140,
|
|
339: 156,
|
|
732: 152,
|
|
352: 138,
|
|
353: 154,
|
|
376: 159,
|
|
381: 142,
|
|
382: 158
|
|
};
|
|
|
|
const characters = `\
|
|
.notdef .notdef .notdef .notdef
|
|
.notdef .notdef .notdef .notdef
|
|
.notdef .notdef .notdef .notdef
|
|
.notdef .notdef .notdef .notdef
|
|
.notdef .notdef .notdef .notdef
|
|
.notdef .notdef .notdef .notdef
|
|
.notdef .notdef .notdef .notdef
|
|
.notdef .notdef .notdef .notdef
|
|
|
|
space exclam quotedbl numbersign
|
|
dollar percent ampersand quotesingle
|
|
parenleft parenright asterisk plus
|
|
comma hyphen period slash
|
|
zero one two three
|
|
four five six seven
|
|
eight nine colon semicolon
|
|
less equal greater question
|
|
|
|
at A B C
|
|
D E F G
|
|
H I J K
|
|
L M N O
|
|
P Q R S
|
|
T U V W
|
|
X Y Z bracketleft
|
|
backslash bracketright asciicircum underscore
|
|
|
|
grave a b c
|
|
d e f g
|
|
h i j k
|
|
l m n o
|
|
p q r s
|
|
t u v w
|
|
x y z braceleft
|
|
bar braceright asciitilde .notdef
|
|
|
|
Euro .notdef quotesinglbase florin
|
|
quotedblbase ellipsis dagger daggerdbl
|
|
circumflex perthousand Scaron guilsinglleft
|
|
OE .notdef Zcaron .notdef
|
|
.notdef quoteleft quoteright quotedblleft
|
|
quotedblright bullet endash emdash
|
|
tilde trademark scaron guilsinglright
|
|
oe .notdef zcaron ydieresis
|
|
|
|
space exclamdown cent sterling
|
|
currency yen brokenbar section
|
|
dieresis copyright ordfeminine guillemotleft
|
|
logicalnot hyphen registered macron
|
|
degree plusminus twosuperior threesuperior
|
|
acute mu paragraph periodcentered
|
|
cedilla onesuperior ordmasculine guillemotright
|
|
onequarter onehalf threequarters questiondown
|
|
|
|
Agrave Aacute Acircumflex Atilde
|
|
Adieresis Aring AE Ccedilla
|
|
Egrave Eacute Ecircumflex Edieresis
|
|
Igrave Iacute Icircumflex Idieresis
|
|
Eth Ntilde Ograve Oacute
|
|
Ocircumflex Otilde Odieresis multiply
|
|
Oslash Ugrave Uacute Ucircumflex
|
|
Udieresis Yacute Thorn germandbls
|
|
|
|
agrave aacute acircumflex atilde
|
|
adieresis aring ae ccedilla
|
|
egrave eacute ecircumflex edieresis
|
|
igrave iacute icircumflex idieresis
|
|
eth ntilde ograve oacute
|
|
ocircumflex otilde odieresis divide
|
|
oslash ugrave uacute ucircumflex
|
|
udieresis yacute thorn ydieresis\
|
|
`.split(/\s+/);
|
|
|
|
|
|
class AFMFont {
|
|
static open(filename) {
|
|
return new AFMFont(fs.readFileSync(filename, 'utf8'));
|
|
}
|
|
|
|
constructor(contents) {
|
|
this.contents = contents;
|
|
this.attributes = {};
|
|
this.glyphWidths = {};
|
|
this.boundingBoxes = {};
|
|
this.kernPairs = {};
|
|
|
|
this.parse();
|
|
// todo: remove charWidths since appears to not be used
|
|
this.charWidths = new Array(256);
|
|
for (let char = 0; char <= 255; char++) {
|
|
this.charWidths[char] = this.glyphWidths[characters[char]];
|
|
}
|
|
|
|
this.bbox = (this.attributes['FontBBox'].split(/\s+/).map((e) => +e));
|
|
this.ascender = +(this.attributes['Ascender'] || 0);
|
|
this.descender = +(this.attributes['Descender'] || 0);
|
|
this.xHeight = +(this.attributes['XHeight'] || 0);
|
|
this.capHeight = +(this.attributes['CapHeight'] || 0);
|
|
this.lineGap = (this.bbox[3] - this.bbox[1]) - (this.ascender - this.descender);
|
|
}
|
|
|
|
parse() {
|
|
let section = '';
|
|
for (let line of this.contents.split('\n')) {
|
|
var match;
|
|
var a;
|
|
if (match = line.match(/^Start(\w+)/)) {
|
|
section = match[1];
|
|
continue;
|
|
|
|
} else if (match = line.match(/^End(\w+)/)) {
|
|
section = '';
|
|
continue;
|
|
}
|
|
|
|
switch (section) {
|
|
case 'FontMetrics':
|
|
match = line.match(/(^\w+)\s+(.*)/);
|
|
var key = match[1];
|
|
var value = match[2];
|
|
|
|
if (a = this.attributes[key]) {
|
|
if (!Array.isArray(a)) { a = (this.attributes[key] = [a]); }
|
|
a.push(value);
|
|
} else {
|
|
this.attributes[key] = value;
|
|
}
|
|
break;
|
|
|
|
case 'CharMetrics':
|
|
if (!/^CH?\s/.test(line)) { continue; }
|
|
var name = line.match(/\bN\s+(\.?\w+)\s*;/)[1];
|
|
this.glyphWidths[name] = +line.match(/\bWX\s+(\d+)\s*;/)[1];
|
|
break;
|
|
|
|
case 'KernPairs':
|
|
match = line.match(/^KPX\s+(\.?\w+)\s+(\.?\w+)\s+(-?\d+)/);
|
|
if (match) {
|
|
this.kernPairs[match[1] + '\0' + match[2]] = parseInt(match[3]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
encodeText(text) {
|
|
const res = [];
|
|
for (let i = 0, end = text.length, asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) {
|
|
let char = text.charCodeAt(i);
|
|
char = WIN_ANSI_MAP[char] || char;
|
|
res.push(char.toString(16));
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
glyphsForString(string) {
|
|
const glyphs = [];
|
|
|
|
for (let i = 0, end = string.length, asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) {
|
|
const charCode = string.charCodeAt(i);
|
|
glyphs.push(this.characterToGlyph(charCode));
|
|
}
|
|
|
|
return glyphs;
|
|
}
|
|
|
|
characterToGlyph(character) {
|
|
return characters[WIN_ANSI_MAP[character] || character] || '.notdef';
|
|
}
|
|
|
|
widthOfGlyph(glyph) {
|
|
return this.glyphWidths[glyph] || 0;
|
|
}
|
|
|
|
getKernPair(left, right) {
|
|
return this.kernPairs[left + '\0' + right] || 0;
|
|
}
|
|
|
|
advancesForGlyphs(glyphs) {
|
|
const advances = [];
|
|
|
|
for (let index = 0; index < glyphs.length; index++) {
|
|
const left = glyphs[index];
|
|
const right = glyphs[index + 1];
|
|
advances.push(this.widthOfGlyph(left) + this.getKernPair(left, right));
|
|
}
|
|
|
|
return advances;
|
|
}
|
|
};
|
|
|
|
export default AFMFont; |