Files
MorphDoc/packages/docx-engine/src/word-font-metadata.ts
T

113 lines
2.5 KiB
TypeScript

import type { SfntFontMetadata } from "./font-embedding.js";
export type WordFontFamily =
| "auto"
| "decorative"
| "modern"
| "roman"
| "script"
| "swiss";
export interface WordFontMetadata {
panose1: string;
charset: string;
family: WordFontFamily;
pitch: "fixed" | "variable";
signature: {
usb0: string;
usb1: string;
usb2: string;
usb3: string;
csb0: string;
csb1: string;
};
}
const codePageCharsets = [
{ bit: 18, charset: "86" },
{ bit: 20, charset: "88" },
{ bit: 17, charset: "80" },
{ bit: 19, charset: "81" },
{ bit: 21, charset: "82" },
{ bit: 16, charset: "DE" },
{ bit: 6, charset: "B2" },
{ bit: 5, charset: "B1" },
{ bit: 7, charset: "BA" },
{ bit: 2, charset: "CC" },
{ bit: 3, charset: "A1" },
{ bit: 4, charset: "A2" },
{ bit: 31, charset: "02" }
] as const;
function uint32Hex(value: number) {
return value.toString(16).toUpperCase().padStart(8, "0");
}
function byteHex(value: number) {
return value.toString(16).toUpperCase().padStart(2, "0");
}
function hasCodePageBit(
ranges: SfntFontMetadata["codePageRanges"],
bit: number
) {
const range = ranges[Math.floor(bit / 32)] ?? 0;
return ((range >>> (bit % 32)) & 1) === 1;
}
function resolveCharset(metadata: SfntFontMetadata) {
return (
codePageCharsets.find(({ bit }) =>
hasCodePageBit(metadata.codePageRanges, bit)
)?.charset ?? "00"
);
}
function resolveFamily(metadata: SfntFontMetadata): WordFontFamily {
if (metadata.fixedPitch) {
return "modern";
}
const [familyType, serifStyle] = metadata.panose;
if (familyType === 3) {
return "script";
}
if (familyType === 4) {
return "decorative";
}
if (familyType !== 2) {
return "auto";
}
if (serifStyle >= 11 && serifStyle <= 13) {
return "swiss";
}
if (
(serifStyle >= 2 && serifStyle <= 10) ||
serifStyle === 14 ||
serifStyle === 15
) {
return "roman";
}
return "auto";
}
export function createWordFontMetadata(
metadata: SfntFontMetadata
): WordFontMetadata {
const [usb0, usb1, usb2, usb3] = metadata.unicodeRanges;
const [csb0, csb1] = metadata.codePageRanges;
return {
panose1: metadata.panose.map(byteHex).join(""),
charset: resolveCharset(metadata),
family: resolveFamily(metadata),
pitch: metadata.fixedPitch ? "fixed" : "variable",
signature: {
usb0: uint32Hex(usb0),
usb1: uint32Hex(usb1),
usb2: uint32Hex(usb2),
usb3: uint32Hex(usb3),
csb0: uint32Hex(csb0),
csb1: uint32Hex(csb1)
}
};
}