lsa-planer
LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.
| 1 | /** |
| 2 | * Der Zeichenvorrat, den der Ausdruck wirklich setzen kann. |
| 3 | * |
| 4 | * WARUM DAS IN `domain/` STEHT UND NICHT IN `render/`. Die PDF-Standard- |
| 5 | * schriften beherrschen Latin-1; alles darueber hinaus muss ersetzt oder |
| 6 | * entfernt werden, sonst steht im Ausdruck ein Fragezeichen oder nichts. Das |
| 7 | * ist eine Eigenschaft der Ausgabe - aber die Frage, OB ein Projekt davon |
| 8 | * betroffen ist, muss der Pruefbericht beantworten, und der wird in `domain/` |
| 9 | * gebildet. Beide Seiten muessen denselben Vorrat kennen; zwei Listen liefen |
| 10 | * auseinander, und dann meldete der Bericht etwas anderes, als der Ausdruck |
| 11 | * tut. Deshalb steht der Vorrat hier einmal, und `render/pdfSurface.ts` holt |
| 12 | * ihn sich - dieselbe Bauform wie `rilsa/constants.ts` fuer die Kennwerte. |
| 13 | * |
| 14 | * BIS 5.25.0 GESCHAH DAS ENTFERNEN STILL. Ersetzt wird seit jeher lesbar - ein |
| 15 | * Pfeil wird zu "->", ein Haken zu "ja" -, und das steht unten in der Tafel. |
| 16 | * Was nach den Ersetzungen noch ausserhalb des Vorrats lag, verschwand |
| 17 | * jedoch ohne ein Wort: Ein Bearbeiter namens "Łukasz" stand in der |
| 18 | * Planunterlage als "ukasz", ein Ortsname mit einem tuerkischen oder |
| 19 | * tschechischen Buchstaben ebenso verstuemmelt. Ueberall sonst sagt dieses |
| 20 | * Programm, wenn es einen Wert ersetzt oder aufrundet; hier schwieg es. Die |
| 21 | * Regel `projektdaten.zeichen-nicht-druckbar` sagt es jetzt. |
| 22 | */ |
| 23 | import { isFiniteNumber } from './units'; |
| 24 | |
| 25 | /* Die letzte Zeile der Ersetzungen enthaelt absichtlich ein geschuetztes und |
| 26 | ein schmales Leerzeichen: Sie sind das, was gesucht und durch ein |
| 27 | gewoehnliches Leerzeichen ersetzt wird. Der Linter meldet sie deshalb an |
| 28 | einer Stelle, an die sie gehoeren. |
| 29 | Das Nullzeichen weiter unten ist etwas anderes: Es wird nicht ersetzt, |
| 30 | sondern ist die untere Grenze des beibehaltenen Bereichs. Es steht als |
| 31 | Escape \x00 und nicht als Steuerzeichen, weil Git eine Quelldatei mit einem |
| 32 | Nullbyte als Binaerdatei fuehrt - ihre Aenderungen sind im Verlauf dann |
| 33 | nicht mehr lesbar. Auch das Escape meldet der Linter; beide Meldungen sind |
| 34 | an dieser Stelle abgestellt. */ |
| 35 | /* eslint-disable no-irregular-whitespace, no-control-regex -- die Zeichen sind hier der Pruefgegenstand; siehe darueber */ |
| 36 | const REPLACEMENTS: readonly (readonly [RegExp, string])[] = [ |
| 37 | [/[→➡⇒]/g, '->'], |
| 38 | [/[←⇐]/g, '<-'], |
| 39 | [/[✓✔☑]/g, 'ja'], |
| 40 | [/[✗✘☒❌]/g, 'nein'], |
| 41 | [/[•●▪]/g, '-'], |
| 42 | [/[≤]/g, '<='], |
| 43 | [/[≥]/g, '>='], |
| 44 | [/[≠]/g, '!='], |
| 45 | [/[±]/g, '+/-'], |
| 46 | [/[…]/g, '...'], |
| 47 | [/[‘’‚]/g, "'"], |
| 48 | [/[“”„]/g, '"'], |
| 49 | [/[–—]/g, '-'], |
| 50 | [/[ ]/g, ' '], |
| 51 | ]; |
| 52 | |
| 53 | export function aufDruckzeichen(value: string): string { |
| 54 | let text = value; |
| 55 | for (const [pattern, replacement] of REPLACEMENTS) { |
| 56 | text = text.replace(pattern, replacement); |
| 57 | } |
| 58 | // Alles, was danach noch ausserhalb von Latin-1 liegt (z. B. Emoji oder |
| 59 | // kyrillische Zeichen in einem Projektnamen), wird entfernt statt als |
| 60 | // unleserliches Zeichen gedruckt zu werden. |
| 61 | // |
| 62 | // Die untere Grenze ist \x00 und nicht das Leerzeichen: Die Steuerzeichen |
| 63 | // unterhalb davon bleiben absichtlich stehen, weil jsPDF an \n umbricht. |
| 64 | // Daran haengen die mehrzeilige Bemerkung auf dem Deckblatt und die |
| 65 | // Zuordnungszeilen unter der Zwischenzeitenmatrix; mit dem Leerzeichen als |
| 66 | // Grenze liefen sie zu einem Fliesstext zusammen. |
| 67 | return text.replace(/[^\x00-ÿ€‚ƒ„…‰Š‹Œ]/gu, ''); |
| 68 | } |
| 69 | |
| 70 | /** Kann der Ausdruck dieses eine Zeichen setzen? */ |
| 71 | function druckbar(zeichen: string): boolean { |
| 72 | return !/[^\x00-ÿ€‚ƒ„…‰Š‹Œ]/u.test(zeichen); |
| 73 | } |
| 74 | |
| 75 | /** |
| 76 | * Die Zeichen einer Zeichenkette, die der Ausdruck weder setzen noch ersetzen |
| 77 | * kann - in der Reihenfolge ihres Auftretens, jedes hoechstens einmal. |
| 78 | * |
| 79 | * Gemessen wird NACH den Ersetzungen: Ein Pfeil ist kein Verlust, er wird zu |
| 80 | * "->". Gemeldet gehoert allein, was ohne Entsprechung verschwindet. |
| 81 | */ |
| 82 | export function nichtDruckbareZeichen(value: string): readonly string[] { |
| 83 | let text = value; |
| 84 | for (const [pattern, replacement] of REPLACEMENTS) { |
| 85 | text = text.replace(pattern, replacement); |
| 86 | } |
| 87 | const gefunden: string[] = []; |
| 88 | for (const zeichen of text) { |
| 89 | if (druckbar(zeichen)) continue; |
| 90 | if (gefunden.includes(zeichen)) continue; |
| 91 | gefunden.push(zeichen); |
| 92 | } |
| 93 | return gefunden; |
| 94 | } |
| 95 | |
| 96 | /** |
| 97 | * Die Kennung eines Zeichens fuer die Meldung: das Zeichen selbst und sein |
| 98 | * Kodepunkt. |
| 99 | * |
| 100 | * DER KODEPUNKT GEHOERT DAZU, und zwar aus einem handfesten Grund: Die Meldung |
| 101 | * steht im Pruefbericht und geht von dort in die gedruckte Planunterlage - und |
| 102 | * die kann das beanstandete Zeichen gerade NICHT setzen. Ohne den Kodepunkt |
| 103 | * stuende dort eine Luecke, wo das Zeichen stehen sollte, und die Meldung |
| 104 | * beklagte etwas Unsichtbares. |
| 105 | */ |
| 106 | export function zeichenKennung(zeichen: string): string { |
| 107 | const punkt = zeichen.codePointAt(0); |
| 108 | const nummer = isFiniteNumber(punkt) ? punkt.toString(16).toUpperCase().padStart(4, '0') : '????'; |
| 109 | return `U+${nummer}`; |
| 110 | } |