Spaces:
Sleeping
Sleeping
| import { expect, test, describe } from "vitest"; | |
| import "@jimp/test-utils/image-snapshot"; | |
| import { HorizontalAlign, VerticalAlign, createJimp } from "@jimp/core"; | |
| import png from "@jimp/js-png"; | |
| import jpeg from "@jimp/js-jpeg"; | |
| import { methods, measureText, measureTextHeight } from "./index.js"; | |
| import { loadFont } from "./load-font.js"; | |
| import * as fonts from "./fonts.js"; | |
| const Jimp = createJimp({ plugins: [methods], formats: [png, jpeg] }); | |
| async function createTextImage( | |
| width: number, | |
| height: number, | |
| font: string, | |
| { | |
| x = 0, | |
| y = 0, | |
| text, | |
| maxWidth, | |
| maxHeight, | |
| }: { | |
| text: | |
| | string | |
| | { | |
| text: string; | |
| alignmentX?: HorizontalAlign; | |
| alignmentY?: VerticalAlign; | |
| }; | |
| maxWidth?: number; | |
| maxHeight?: number; | |
| x?: number; | |
| y?: number; | |
| }, | |
| ) { | |
| const loadedFont = await loadFont(font); | |
| const image = new Jimp({ width, height, color: 0xffffffff }); | |
| return image | |
| .print({ font: loadedFont, x, y, text, maxWidth, maxHeight }) | |
| .getBuffer("image/png"); | |
| } | |
| describe("Write text over image", function () { | |
| const fontDefs = { | |
| SANS_8_BLACK: { w: 28, h: 28, bg: 0xffffffff }, | |
| SANS_16_BLACK: { w: 54, h: 54, bg: 0xffffffff }, | |
| SANS_32_BLACK: { w: 114, h: 114, bg: 0xffffffff }, | |
| SANS_64_BLACK: { w: 220, h: 220, bg: 0xffffffff }, | |
| SANS_8_WHITE: { w: 28, h: 28, bg: 0x000000ff }, | |
| SANS_16_WHITE: { w: 54, h: 54, bg: 0x000000ff }, | |
| SANS_32_WHITE: { w: 114, h: 114, bg: 0x000000ff }, | |
| SANS_64_WHITE: { w: 220, h: 220, bg: 0x000000ff }, | |
| }; | |
| Object.entries(fontDefs).forEach(([fontName, conf]) => { | |
| test("Jimp preset " + fontName + " bitmap font", async () => { | |
| const font = await loadFont(fonts[fontName as keyof typeof fonts]); | |
| const image = new Jimp({ width: conf.w, height: conf.h, color: conf.bg }); | |
| const output = await image | |
| .print({ | |
| font, | |
| x: 0, | |
| y: 0, | |
| text: "This is only a test.", | |
| maxWidth: image.bitmap.width, | |
| }) | |
| .getBuffer("image/png"); | |
| expect(output).toMatchImageSnapshot(); | |
| }); | |
| }); | |
| test("Jimp preset SANS_16_BLACK bitmap font positioned", async () => { | |
| const font = await loadFont(fonts.SANS_16_BLACK); | |
| const image = new Jimp({ width: 300, height: 100, color: 0xff8800ff }); | |
| const output = await image | |
| .print({ | |
| font, | |
| x: 150, | |
| y: 50, | |
| text: "This is only a test.", | |
| maxWidth: 100, | |
| }) | |
| .getBuffer("image/png"); | |
| expect(output).toMatchImageSnapshot(); | |
| }); | |
| test("Jimp loads font from URL", async () => { | |
| const font = await loadFont( | |
| "https://raw.githubusercontent.com/jimp-dev/jimp/main/plugins/plugin-print/fonts/open-sans/open-sans-16-black/open-sans-16-black.fnt", | |
| ); | |
| const image = new Jimp({ width: 300, height: 100, color: 0xff8800ff }); | |
| const output = await image | |
| .print({ | |
| font, | |
| x: 150, | |
| y: 50, | |
| text: "This is only a test.", | |
| maxWidth: 100, | |
| }) | |
| .getBuffer("image/png"); | |
| expect(output).toMatchImageSnapshot(); | |
| }); | |
| test("Max width works without spaces", async () => { | |
| const font = await loadFont( | |
| "https://raw.githubusercontent.com/jimp-dev/jimp/main/plugins/plugin-print/fonts/open-sans/open-sans-16-black/open-sans-16-black.fnt", | |
| ); | |
| const image = new Jimp({ width: 300, height: 100, color: 0xff8800ff }); | |
| const output = await image | |
| .print({ | |
| font, | |
| x: 150, | |
| y: 50, | |
| text: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", | |
| maxWidth: 100, | |
| }) | |
| .getBuffer("image/png"); | |
| expect(output).toMatchImageSnapshot(); | |
| }); | |
| test("Jimp renders ? for unknown characters", async () => { | |
| const font = await loadFont(fonts.SANS_16_BLACK); | |
| const image = new Jimp({ width: 300, height: 100, color: 0xff8800ff }); | |
| const output = await image | |
| .print({ font, x: 0, y: 0, text: "ツ ツ ツ", maxWidth: 100 }) | |
| .getBuffer("image/png"); | |
| expect(output).toMatchImageSnapshot(); | |
| }); | |
| test("Jimp can print numbers too", async () => { | |
| const font = await loadFont(fonts.SANS_16_BLACK); | |
| const image = new Jimp({ width: 300, height: 100, color: 0xff8800ff }); | |
| const output = await image | |
| .print({ font, x: 0, y: 0, text: 12345678, maxWidth: 100 }) | |
| .getBuffer("image/png"); | |
| expect(output).toMatchImageSnapshot(); | |
| }); | |
| test("left-align text by default", async () => { | |
| const textImage = await createTextImage(320, 240, fonts.SANS_16_BLACK, { | |
| text: "This is only a test.", | |
| maxWidth: 100, | |
| }); | |
| expect(textImage).toMatchImageSnapshot(); | |
| }); | |
| test("left-align text by default when passing object", async () => { | |
| const textImage = await createTextImage(320, 240, fonts.SANS_16_BLACK, { | |
| text: { text: "This is only a test." }, | |
| maxWidth: 100, | |
| }); | |
| expect(textImage).toMatchImageSnapshot(); | |
| }); | |
| test("left-align text when passing object with alignmentX", async () => { | |
| const textImage = await createTextImage(320, 240, fonts.SANS_16_BLACK, { | |
| text: { | |
| text: "This is only a test.", | |
| alignmentX: HorizontalAlign.LEFT, | |
| }, | |
| maxWidth: 100, | |
| }); | |
| expect(textImage).toMatchImageSnapshot(); | |
| }); | |
| test("center-align text when passing object with alignmentX", async () => { | |
| const textImage = await createTextImage(320, 240, fonts.SANS_16_BLACK, { | |
| text: { | |
| text: "This is only a test.", | |
| alignmentX: HorizontalAlign.CENTER, | |
| }, | |
| maxWidth: 100, | |
| }); | |
| expect(textImage).toMatchImageSnapshot(); | |
| }); | |
| test("right-align text when passing object with alignmentX", async () => { | |
| const textImage = await createTextImage(320, 240, fonts.SANS_16_BLACK, { | |
| text: { | |
| text: "This is only a test.", | |
| alignmentX: HorizontalAlign.RIGHT, | |
| }, | |
| maxWidth: 100, | |
| }); | |
| expect(textImage).toMatchImageSnapshot(); | |
| }); | |
| test("middle-align text when passing object with alignmentY", async () => { | |
| const textImage = await createTextImage(320, 240, fonts.SANS_16_BLACK, { | |
| text: { | |
| text: "This is only a test.", | |
| alignmentY: VerticalAlign.MIDDLE, | |
| }, | |
| maxWidth: 100, | |
| maxHeight: 240, | |
| }); | |
| expect(textImage).toMatchImageSnapshot(); | |
| }); | |
| test("middle-align text when passing object with alignmentY can offset y", async () => { | |
| const textImage = await createTextImage(320, 240, fonts.SANS_16_BLACK, { | |
| y: 50, | |
| text: { | |
| text: "This is only a test.", | |
| alignmentY: VerticalAlign.MIDDLE, | |
| }, | |
| maxWidth: 100, | |
| maxHeight: 240, | |
| }); | |
| expect(textImage).toMatchImageSnapshot(); | |
| }); | |
| test("bottom-align text when passing object with alignmentY", async () => { | |
| const textImage = await createTextImage(320, 240, fonts.SANS_16_BLACK, { | |
| text: { | |
| text: "This is only a test.", | |
| alignmentY: VerticalAlign.BOTTOM, | |
| }, | |
| maxWidth: 100, | |
| maxHeight: 240, | |
| }); | |
| expect(textImage).toMatchImageSnapshot(); | |
| }); | |
| test("bottom-align text when passing object with alignmentY offset y", async () => { | |
| const textImage = await createTextImage(320, 240, fonts.SANS_16_BLACK, { | |
| y: 100, | |
| text: { | |
| text: "This is only a test.", | |
| alignmentY: VerticalAlign.BOTTOM, | |
| }, | |
| maxWidth: 100, | |
| maxHeight: 100, | |
| }); | |
| expect(textImage).toMatchImageSnapshot(); | |
| }); | |
| test("exposes print y position in cb", async () => { | |
| const loadedFont = await loadFont(fonts.SANS_16_BLACK); | |
| const image = new Jimp({ width: 500, height: 500, color: 0xffffffff }); | |
| image.print({ | |
| font: loadedFont, | |
| x: 0, | |
| y: 0, | |
| text: "One two three four fix six seven eight nine ten eleven twelve", | |
| maxWidth: 250, | |
| cb: ({ x, y }) => { | |
| image.print({ | |
| font: loadedFont, | |
| x, | |
| y: y + 50, | |
| text: "thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty", | |
| maxWidth: 250, | |
| }); | |
| }, | |
| }); | |
| const output = await image.getBuffer("image/png"); | |
| expect(output).toMatchImageSnapshot(); | |
| }); | |
| test("measureText is consistent with measureTextWidth", async () => { | |
| const font = await loadFont(fonts.SANS_16_BLACK); | |
| const text = "n n n"; | |
| const width = measureText(font, text); | |
| const height = measureTextHeight(font, text, width); | |
| const lineHeight = measureTextHeight(font, text, Infinity); | |
| expect(height).toEqual(lineHeight); | |
| }); | |
| test("text with newlines, default alignment", async () => { | |
| const textImage = await createTextImage(100, 240, fonts.SANS_16_BLACK, { | |
| text: "This \nis only \na \ntest.", | |
| maxWidth: 300, | |
| }); | |
| expect(textImage).toMatchImageSnapshot(); | |
| }); | |
| }); | |