lsa-planer

LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.

/ src domain druckzeichen.ts

4,8 KB Rohdatei
src/domain/druckzeichen.ts — 110 Zeilen
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 }