/** * Der Zeichenvorrat, den der Ausdruck wirklich setzen kann. * * WARUM DAS IN `domain/` STEHT UND NICHT IN `render/`. Die PDF-Standard- * schriften beherrschen Latin-1; alles darueber hinaus muss ersetzt oder * entfernt werden, sonst steht im Ausdruck ein Fragezeichen oder nichts. Das * ist eine Eigenschaft der Ausgabe - aber die Frage, OB ein Projekt davon * betroffen ist, muss der Pruefbericht beantworten, und der wird in `domain/` * gebildet. Beide Seiten muessen denselben Vorrat kennen; zwei Listen liefen * auseinander, und dann meldete der Bericht etwas anderes, als der Ausdruck * tut. Deshalb steht der Vorrat hier einmal, und `render/pdfSurface.ts` holt * ihn sich - dieselbe Bauform wie `rilsa/constants.ts` fuer die Kennwerte. * * BIS 5.25.0 GESCHAH DAS ENTFERNEN STILL. Ersetzt wird seit jeher lesbar - ein * Pfeil wird zu "->", ein Haken zu "ja" -, und das steht unten in der Tafel. * Was nach den Ersetzungen noch ausserhalb des Vorrats lag, verschwand * jedoch ohne ein Wort: Ein Bearbeiter namens "Łukasz" stand in der * Planunterlage als "ukasz", ein Ortsname mit einem tuerkischen oder * tschechischen Buchstaben ebenso verstuemmelt. Ueberall sonst sagt dieses * Programm, wenn es einen Wert ersetzt oder aufrundet; hier schwieg es. Die * Regel `projektdaten.zeichen-nicht-druckbar` sagt es jetzt. */ import { isFiniteNumber } from './units'; /* Die letzte Zeile der Ersetzungen enthaelt absichtlich ein geschuetztes und ein schmales Leerzeichen: Sie sind das, was gesucht und durch ein gewoehnliches Leerzeichen ersetzt wird. Der Linter meldet sie deshalb an einer Stelle, an die sie gehoeren. Das Nullzeichen weiter unten ist etwas anderes: Es wird nicht ersetzt, sondern ist die untere Grenze des beibehaltenen Bereichs. Es steht als Escape \x00 und nicht als Steuerzeichen, weil Git eine Quelldatei mit einem Nullbyte als Binaerdatei fuehrt - ihre Aenderungen sind im Verlauf dann nicht mehr lesbar. Auch das Escape meldet der Linter; beide Meldungen sind an dieser Stelle abgestellt. */ /* eslint-disable no-irregular-whitespace, no-control-regex -- die Zeichen sind hier der Pruefgegenstand; siehe darueber */ const REPLACEMENTS: readonly (readonly [RegExp, string])[] = [ [/[→➡⇒]/g, '->'], [/[←⇐]/g, '<-'], [/[✓✔☑]/g, 'ja'], [/[✗✘☒❌]/g, 'nein'], [/[•●▪]/g, '-'], [/[≤]/g, '<='], [/[≥]/g, '>='], [/[≠]/g, '!='], [/[±]/g, '+/-'], [/[…]/g, '...'], [/[‘’‚]/g, "'"], [/[“”„]/g, '"'], [/[–—]/g, '-'], [/[   ]/g, ' '], ]; export function aufDruckzeichen(value: string): string { let text = value; for (const [pattern, replacement] of REPLACEMENTS) { text = text.replace(pattern, replacement); } // Alles, was danach noch ausserhalb von Latin-1 liegt (z. B. Emoji oder // kyrillische Zeichen in einem Projektnamen), wird entfernt statt als // unleserliches Zeichen gedruckt zu werden. // // Die untere Grenze ist \x00 und nicht das Leerzeichen: Die Steuerzeichen // unterhalb davon bleiben absichtlich stehen, weil jsPDF an \n umbricht. // Daran haengen die mehrzeilige Bemerkung auf dem Deckblatt und die // Zuordnungszeilen unter der Zwischenzeitenmatrix; mit dem Leerzeichen als // Grenze liefen sie zu einem Fliesstext zusammen. return text.replace(/[^\x00-ÿ€‚ƒ„…‰Š‹Œ]/gu, ''); } /** Kann der Ausdruck dieses eine Zeichen setzen? */ function druckbar(zeichen: string): boolean { return !/[^\x00-ÿ€‚ƒ„…‰Š‹Œ]/u.test(zeichen); } /** * Die Zeichen einer Zeichenkette, die der Ausdruck weder setzen noch ersetzen * kann - in der Reihenfolge ihres Auftretens, jedes hoechstens einmal. * * Gemessen wird NACH den Ersetzungen: Ein Pfeil ist kein Verlust, er wird zu * "->". Gemeldet gehoert allein, was ohne Entsprechung verschwindet. */ export function nichtDruckbareZeichen(value: string): readonly string[] { let text = value; for (const [pattern, replacement] of REPLACEMENTS) { text = text.replace(pattern, replacement); } const gefunden: string[] = []; for (const zeichen of text) { if (druckbar(zeichen)) continue; if (gefunden.includes(zeichen)) continue; gefunden.push(zeichen); } return gefunden; } /** * Die Kennung eines Zeichens fuer die Meldung: das Zeichen selbst und sein * Kodepunkt. * * DER KODEPUNKT GEHOERT DAZU, und zwar aus einem handfesten Grund: Die Meldung * steht im Pruefbericht und geht von dort in die gedruckte Planunterlage - und * die kann das beanstandete Zeichen gerade NICHT setzen. Ohne den Kodepunkt * stuende dort eine Luecke, wo das Zeichen stehen sollte, und die Meldung * beklagte etwas Unsichtbares. */ export function zeichenKennung(zeichen: string): string { const punkt = zeichen.codePointAt(0); const nummer = isFiniteNumber(punkt) ? punkt.toString(16).toUpperCase().padStart(4, '0') : '????'; return `U+${nummer}`; }