All files / src/shared/lib/helpers fonts.ts

96.23% Statements 51/53
86.11% Branches 31/36
80% Functions 4/5
96% Lines 48/50

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 10810x                     10x       654x 211x 211x 211x     443x 443x 181x 32x       443x 28x 28x 28x     415x 415x 415x     10x       79x   79x 8x         79x 79x 643x 643x   643x 648x 30x   618x       643x     79x     10x           5x 5x   5x   5x 9x 3x 3x 2x   3x 3x     6x   6x       6x 6x 6x           5x    
import { lexText, Token } from "shared/lib/compiler/lexText";
 
export interface FontData {
  id: string;
  img: HTMLImageElement;
  isMono: boolean;
  widths: number[];
  mapping: Record<string, number | number[]>;
  table: number[];
}
 
export const resolveMapping = (
  input: string,
  mapping?: Record<string, number | number[]>,
): { codes: number[]; length: number } => {
  if (!mapping) {
    const code = input.codePointAt(0) ?? 0;
    const length = code > 0xffff ? 2 : 1;
    return { codes: [code], length };
  }
 
  let longestMatch = "";
  for (const key of Object.keys(mapping)) {
    if (input.startsWith(key) && key.length > longestMatch.length) {
      longestMatch = key;
    }
  }
 
  if (longestMatch) {
    const raw = mapping[longestMatch];
    const codes = Array.isArray(raw) ? raw : [raw];
    return { codes, length: longestMatch.length };
  }
 
  const code = input.codePointAt(0) ?? 0;
  const length = code > 0xffff ? 2 : 1;
  return { codes: [code], length };
};
 
export const encodeString = (
  inStr: string,
  mapping?: Record<string, number | number[]>,
): string => {
  let output = "";
 
  const decodedStr = inStr
    .replace(/\\([0-7]{3})/g, (_, oct) => String.fromCharCode(parseInt(oct, 8)))
    .replace(/\\x([0-9A-Fa-f]+)/g, (_, hex) =>
      String.fromCharCode(parseInt(hex, 16)),
    );
 
  let i = 0;
  while (i < decodedStr.length) {
    const slice = decodedStr.slice(i);
    const { codes, length } = resolveMapping(slice, mapping);
 
    for (const code of codes) {
      if (code < 32 || code > 127 || code === 34) {
        output += "\\" + (code & 0xff).toString(8).padStart(3, "0");
      } else {
        output += String.fromCharCode(code);
      }
    }
 
    i += length;
  }
 
  return output;
};
 
export const lexTextWithMapping = (
  text: string,
  fontsData: Record<string, FontData>,
  fontId: string,
  preferPreviewValue?: boolean,
): Token[] => {
  const rawTokens = lexText(text);
  const result: Token[] = [];
 
  let font = fontsData[fontId];
 
  for (const token of rawTokens) {
    if (token.type === "font") {
      const newFont = fontsData[token.fontId];
      if (newFont) {
        font = newFont;
      }
      result.push(token);
      continue;
    }
 
    if (token.type === "text") {
      const value =
        preferPreviewValue && token.previewValue !== undefined
          ? token.previewValue
          : token.value;
 
      const encoded = encodeString(value, font?.mapping);
      const encodedTokens = lexText(encoded);
      result.push(...encodedTokens);
    } else E{
      result.push(token);
    }
  }
 
  return result;
};