in src/core/evaluator.js [3958:4095]
extractWidths(dict, descriptor, properties) {
const xref = this.xref;
let glyphsWidths = [];
let defaultWidth = 0;
const glyphsVMetrics = [];
let defaultVMetrics;
if (properties.composite) {
const dw = dict.get("DW");
defaultWidth = typeof dw === "number" ? Math.ceil(dw) : 1000;
const widths = dict.get("W");
if (Array.isArray(widths)) {
for (let i = 0, ii = widths.length; i < ii; i++) {
let start = xref.fetchIfRef(widths[i++]);
if (!Number.isInteger(start)) {
break; // Invalid /W data.
}
const code = xref.fetchIfRef(widths[i]);
if (Array.isArray(code)) {
for (const c of code) {
const width = xref.fetchIfRef(c);
if (typeof width === "number") {
glyphsWidths[start] = width;
}
start++;
}
} else if (Number.isInteger(code)) {
const width = xref.fetchIfRef(widths[++i]);
if (typeof width !== "number") {
continue;
}
for (let j = start; j <= code; j++) {
glyphsWidths[j] = width;
}
} else {
break; // Invalid /W data.
}
}
}
if (properties.vertical) {
const dw2 = dict.getArray("DW2");
let vmetrics = isNumberArray(dw2, 2) ? dw2 : [880, -1000];
defaultVMetrics = [vmetrics[1], defaultWidth * 0.5, vmetrics[0]];
vmetrics = dict.get("W2");
if (Array.isArray(vmetrics)) {
for (let i = 0, ii = vmetrics.length; i < ii; i++) {
let start = xref.fetchIfRef(vmetrics[i++]);
if (!Number.isInteger(start)) {
break; // Invalid /W2 data.
}
const code = xref.fetchIfRef(vmetrics[i]);
if (Array.isArray(code)) {
for (let j = 0, jj = code.length; j < jj; j++) {
const vmetric = [
xref.fetchIfRef(code[j++]),
xref.fetchIfRef(code[j++]),
xref.fetchIfRef(code[j]),
];
if (isNumberArray(vmetric, null)) {
glyphsVMetrics[start] = vmetric;
}
start++;
}
} else if (Number.isInteger(code)) {
const vmetric = [
xref.fetchIfRef(vmetrics[++i]),
xref.fetchIfRef(vmetrics[++i]),
xref.fetchIfRef(vmetrics[++i]),
];
if (!isNumberArray(vmetric, null)) {
continue;
}
for (let j = start; j <= code; j++) {
glyphsVMetrics[j] = vmetric;
}
} else {
break; // Invalid /W2 data.
}
}
}
}
} else {
const widths = dict.get("Widths");
if (Array.isArray(widths)) {
let j = properties.firstChar;
for (const w of widths) {
const width = xref.fetchIfRef(w);
if (typeof width === "number") {
glyphsWidths[j] = width;
}
j++;
}
const missingWidth = descriptor.get("MissingWidth");
defaultWidth = typeof missingWidth === "number" ? missingWidth : 0;
} else {
// Trying get the BaseFont metrics (see comment above).
const baseFontName = dict.get("BaseFont");
if (baseFontName instanceof Name) {
const metrics = this.getBaseFontMetrics(baseFontName.name);
glyphsWidths = this.buildCharCodeToWidth(metrics.widths, properties);
defaultWidth = metrics.defaultWidth;
}
}
}
// Heuristic: detection of monospace font by checking all non-zero widths
let isMonospace = true;
let firstWidth = defaultWidth;
for (const glyph in glyphsWidths) {
const glyphWidth = glyphsWidths[glyph];
if (!glyphWidth) {
continue;
}
if (!firstWidth) {
firstWidth = glyphWidth;
continue;
}
if (firstWidth !== glyphWidth) {
isMonospace = false;
break;
}
}
if (isMonospace) {
properties.flags |= FontFlags.FixedPitch;
} else {
// Clear the flag.
properties.flags &= ~FontFlags.FixedPitch;
}
properties.defaultWidth = defaultWidth;
properties.widths = glyphsWidths;
properties.defaultVMetrics = defaultVMetrics;
properties.vmetrics = glyphsVMetrics;
}