extractWidths()

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;
  }