painbrush

1.0.21.1.0
src-packer/pipeline.js
+src-packer/pipeline.jsNew file
+77
Index: package/src-packer/pipeline.js
===================================================================
--- package/src-packer/pipeline.js
+++ package/src-packer/pipeline.js
@@ -0,0 +1,77 @@
+import path from "path/posix";
+import { decode } from "fast-bmp";
+import { solidFillBrush } from "../src/color.js";
+import { toImage } from "../src/image.js";
+import { padLayer, makeTextLayer, overlayLayerOver, makeRectangleLayer, } from "../src/layer.js";
+import { useFont } from "../src/typography.js";
+export const generateCharacters = async ({ img, fontMeta, }) => {
+    const { metrics } = fontMeta;
+    const data = decode(img);
+    const rawCharacters = [[]];
+    const colspan = fontMeta.cols * metrics.width;
+    data.data.forEach((item, index) => {
+        const pixelX = index % colspan;
+        const pixelY = ~~(index / colspan);
+        const charX = ~~(pixelX / metrics.width);
+        const charY = ~~(pixelY / metrics.height);
+        const charXPixelOffset = pixelX - charX * metrics.width;
+        const charYPixelOffset = pixelY - charY * metrics.height;
+        const charPos = charX + charY * fontMeta.cols;
+        if (!rawCharacters[charPos]) {
+            rawCharacters[charPos] = [];
+        }
+        const charPixelPos = charXPixelOffset + charYPixelOffset * metrics.width;
+        rawCharacters[charPos][charPixelPos] = item;
+    });
+    const alphabet = fontMeta.alphabet.join("");
+    return rawCharacters.map((char, index) => {
+        const letter = alphabet[index];
+        const maybeTrim = fontMeta.trim[letter] ?? fontMeta.trim["__DEFAULT__"];
+        if (!maybeTrim) {
+            return [metrics.width, char];
+        }
+        let newChar = [];
+        for (let i = 0; i < char.length; i++) {
+            const pos = i % metrics.width;
+            if (pos < metrics.width - maybeTrim) {
+                newChar.push(char[i]);
+            }
+        }
+        return [metrics.width - maybeTrim, newChar];
+    });
+};
+export const generatePxFontFile = (characters, { fontName, fontMeta, cwd, outDir }) => {
+    const fontFileAt = path.join(cwd, outDir, fontName + ".pxfont");
+    const alphabet = fontMeta.alphabet.join("");
+    const metrics = fontMeta.metrics;
+    return [
+        fontFileAt,
+        JSON.stringify({
+            metrics,
+            alphabet,
+            characters,
+        }, null, 2),
+    ];
+};
+export const generateSpecimenImage = async ([_, pxFontFile], { fontName, fontMeta, cwd, outDir }) => {
+    const alphabet = fontMeta.alphabet.join("");
+    const specimenImgPd = padLayer(await makeTextLayer(fontName.toUpperCase() +
+        "\n" +
+        "\n" +
+        "? " +
+        alphabet
+            .split("")
+            .map((s) => s.trim())
+            .filter(Boolean)
+            .sort()
+            .join(""), await useFont(Promise.resolve(pxFontFile)), solidFillBrush(fontMeta.specimen?.color ?? [0, 0, 0]), {
+        maxLengthPx: fontMeta.metrics.width * 12,
+        breakLinesOn: "", // break on anything
+    }), { x: fontMeta.metrics.width, y: fontMeta.metrics.height });
+    const specimenImg = overlayLayerOver(makeRectangleLayer({
+        x: specimenImgPd.width,
+        y: specimenImgPd.height,
+    }, solidFillBrush(fontMeta.specimen?.background ?? [255, 255, 255])), specimenImgPd);
+    const specimenFileAt = path.join(cwd, outDir, fontName + "-specimen.bmp");
+    return [specimenFileAt, toImage(specimenImg)];
+};