import { test } from 'node:test' import assert from 'node:assert/strict' import { createHash } from 'node:crypto' import { readFileSync, readdirSync } from 'node:fs' import { useTestEnv } from './support/testEnv.js' const FONTS = new URL('../server/assets/fonts/', import.meta.url) const sha256 = (datei) => createHash('sha256').update(readFileSync(new URL(datei, FONTS))).digest('hex') const cp = (...codes) => String.fromCodePoint(...codes) const NBSP = cp(0xa0) test('Schriftdateien: Figtree Regular und SemiBold samt OFL-Lizenz und Quellenangabe liegen unverändert vor', () => { assert.deepEqual(readdirSync(FONTS).sort(), ['Figtree-OFL.txt', 'Figtree-Regular.ttf', 'Figtree-SemiBold.ttf', 'QUELLE.txt']) assert.equal(sha256('Figtree-Regular.ttf'), '4e2e9876c4a908c2cbe410ceb648e4724464a84492bd49cb2d6e7b0d3f769a66') assert.equal(sha256('Figtree-SemiBold.ttf'), '1b76aae525b51758fc0c9c5096f33d90831657d3ad2fe3c34fb7e666d76f8960') assert.equal(sha256('Figtree-OFL.txt'), '140d37233e7f3ce7313798befa9600893bcceaf41a55fa0fa5ad52f7f657a268') assert.match(readFileSync(new URL('Figtree-OFL.txt', FONTS), 'utf8'), /^Copyright 2022 The Figtree Project Authors/) assert.match(readFileSync(new URL('QUELLE.txt', FONTS), 'utf8'), /@expo-google-fonts\/figtree@0\.4\.1/) }) useTestEnv({ appwriteUrl: 'http://127.0.0.1:1', stripeUrl: 'http://127.0.0.1:1' }) const { ladeSchriften, hatGlyph, bereinigeText, bereinigeTexte } = await import('../server/services/auftraege/pdf.js') test('ladeSchriften: Dateien werden einmal je Prozess gelesen, jeder Aufruf liefert dasselbe Objekt', () => { const schriften = ladeSchriften() assert.equal(ladeSchriften(), schriften) assert.equal(schriften.regularFont.postscriptName, 'Figtree-Regular') assert.equal(schriften.semiboldFont.postscriptName, 'Figtree-SemiBold') assert.equal(schriften.regular.length, 40140) assert.equal(schriften.logo.subarray(1, 4).toString('latin1'), 'PNG') }) test('hatGlyph: deutsche Buchstaben, €, Minuszeichen, geschütztes Leerzeichen und typografische Zeichen sind in beiden Schnitten da', () => { const noetig = [ ...'äöüÄÖÜ߀§%/&()@:;,.!?+*#"\'0123456789 ', NBSP, cp(0x2212), cp(0x2013), cp(0x2014), cp(0x201e), cp(0x201c), cp(0x201d), cp(0x2018), cp(0x2019), cp(0x2026), cp(0x2022), cp(0xb7), cp(0xd7), 'ł', 'ğ', 'Ş', 'ő', 'ř', 'ı', ] for (const zeichen of noetig) assert.ok(hatGlyph(zeichen.codePointAt(0)), `Glyph fehlt: U+${zeichen.codePointAt(0).toString(16)}`) for (const code of [0x202f, 0x2009, 0x200b, 0x416, 0x1f600, 0x3042]) assert.equal(hatGlyph(code), false, `U+${code.toString(16)} sollte fehlen`) }) test('bereinigeText: vorhandene Zeichen bleiben unverändert, getrennte Akzente werden zusammengesetzt', () => { const text = `Müller & Söhne – „Größe“ 1.234,56${NBSP}€ ${cp(0x2212)} 20,00${NBSP}€ ÄÖÜ äöü ß … Łukasz Yıldız Çelik` assert.equal(bereinigeText(text), text) assert.equal(bereinigeText('a' + cp(0x301)), 'á'.normalize('NFC')) assert.equal(bereinigeText(null), '') assert.equal(bereinigeText(undefined), '') assert.equal(bereinigeText(42), '42') }) test('bereinigeText: Steuerzeichen, Zero-Width, weiche Trennung und BOM entfallen; Zeilenenden werden Zeilenumbrüche, Tab wird Leerzeichen', () => { const roh = `a${cp(0x200b)}b${cp(0xad)}c${cp(0xfeff)}d${cp(0)}e\tf\r\ng\rh${cp(0x2028)}i${cp(0x200d, 0xfe0f)}j` assert.equal(bereinigeText(roh), 'abcde f\ng\nh\nij') }) test('bereinigeText mit Figtree: schmale Leerzeichen werden Leerzeichen, kyrillische und japanische Zeichen sowie Emoji je ein Fragezeichen', () => { assert.equal(bereinigeText(`a${cp(0x202f)}b${cp(0x2009)}c`), 'a b c') assert.equal(bereinigeText(`${cp(0x416)}${cp(0x3042)}${cp(0x1f600)}`), '???') }) test('bereinigeText: Ersatzregeln bei fehlendem Glyph (Schrift ohne Zeichen über ASCII)', () => { const nurAscii = (code) => code < 0x80 const fall = (eingabe) => bereinigeText(eingabe, nurAscii) assert.equal(fall('9,00' + NBSP + '€'), '9,00 EUR') assert.equal(fall(`${cp(0x201e)}x${cp(0x201c)} ${cp(0x201a)}y${cp(0x2019)}`), `"x" 'y'`) assert.equal(fall(`1 ${cp(0x2013)} 2 ${cp(0x2212)} 3 ${cp(0x2014)} 4`), '1 - 2 - 3 - 4') assert.equal(fall(`wait${cp(0x2026)} 3${cp(0xd7)}4 a${cp(0x2192)}b`), 'wait... 3x4 a->b') assert.equal(fall('Müller Çelik Ångström'), 'Muller Celik Angstrom') assert.equal(fall('Łukasz Ğ'), '?ukasz G') assert.equal(fall('e' + cp(0x301) + cp(0x301)), 'e') assert.equal(fall(cp(0x301)), '') }) test('bereinigeTexte: bearbeitet jede Zeichenkette in verschachtelten Objekten und Listen, andere Werte bleiben', () => { const roh = { a: ['x' + cp(0x200b) + 'y', { b: 'ż' + cp(0x1f600) }], n: 5, f: null, t: true } assert.deepEqual(bereinigeTexte(roh), { a: ['xy', { b: 'ż?' }], n: 5, f: null, t: true }) })