waffensachkunde

Waffensachkunde – Lernsoftware für die Sachkundeprüfung nach § 7 WaffG. Barrierefrei, offline, EUPL-1.2.

/ app src shared druck umfang.ts

4,1 KB Rohdatei
app/src/shared/druck/umfang.ts — 116 Zeilen
1 /**
2 * Wie dick wird das Dokument? – die Schätzung, die vor dem Speichern dasteht.
3 *
4 * ## Warum das überhaupt nötig ist
5 *
6 * Der schlimmste gemessene Fall sind **982 Seiten**: 486 Fragen mit voller
7 * Begründung im Großdruck. Wer das ohne Vorwarnung an einen Drucker schickt,
8 * hat ein Problem. Die Zahl muss deshalb dastehen, bevor der Speicherdialog
9 * aufgeht.
10 *
11 * ## Warum die Stützstellen gemessen sind und nicht gerechnet
12 *
13 * Der erste Anlauf schätzte über eine Faustregel („rund 3000 Zeichen je
14 * A4-Seite“). Nachgemessen an wirklich gedruckten PDF lag sie um **Faktor
15 * 1,7 bis 2,8 zu niedrig** – die Fragenliste über den ganzen Katalog wurde
16 * mit 55 Seiten angekündigt und hatte 162. Eine Anzeige, die zugunsten des
17 * Nutzers irrt, ist genauso falsch wie eine, die gegen ihn irrt.
18 *
19 * Die Werte unten stammen deshalb aus Dokumenten, die mit demselben
20 * Stylesheet gebaut und mit echtem Chromium gedruckt wurden. Gezählt wurden
21 * die Seitenobjekte im PDF, nicht der erste Knoten des Seitenbaums – dieser
22 * Griff liefert bei jedem Dokument über acht Seiten die Zahl 8.
23 *
24 * ## Was die Zahl nicht ist
25 *
26 * Eine Zusage. Die Länge hängt daran, welche Fragen es trifft; eine Frage mit
27 * Abbildung und langer Begründung braucht ein Mehrfaches einer Ja/Nein-Frage.
28 * Die Oberfläche sagt deshalb „geschätzt etwa“ und nennt keine Nachkommastelle.
29 */
30
31 import type { Schriftgroesse } from './stil';
32
33 /**
34 * Gemessene Stützstellen, Seiten je Frage bei Schriftgröße „normal“.
35 *
36 * | Dokument | gemessen | Seiten |
37 * |---|---|---|
38 * | Fehlerprotokoll, Kurzerklärung | 150 Fragen | 70 |
39 * | Fehlerprotokoll, volle Begründung | 150 Fragen | 169 |
40 * | Fehlerprotokoll, volle Begründung | 486 Fragen | 539 |
41 * | Fragenliste | 575 Fragen | 162 |
42 * | Fragenliste mit Lösungsanhang | 575 Fragen | 185 |
43 */
44 const JE_FRAGE = Object.freeze({
45 fehlerKurz: 0.47,
46 fehlerVoll: 1.1,
47 fragenliste: 0.28,
48 /** Der Lösungsanhang kostet zusätzlich – 23 Seiten auf 575 Fragen. */
49 loesungsanhang: 0.04,
50 });
51
52 /** Vorspann, Quellenblock und Überschriften – unabhängig von der Fragenzahl. */
53 const GRUNDLAST = 2;
54
55 /**
56 * Großdruck gegen Normalgröße, gemessen.
57 *
58 * 162 gegen 279 Seiten bei derselben Fragenliste, also Faktor 1,72; beim
59 * Fehlerprotokoll mit voller Begründung über 486 Fragen 539 gegen 982, also
60 * 1,82. Genommen wird 1,9, damit die Schätzung eher zu hoch als zu niedrig
61 * ausfällt.
62 *
63 * Hier stand bis 0.27.2 „508 gegen 982“ — eine Zahl, die der Tabelle darüber
64 * widerspricht, in der für denselben Fall 539 steht. `tests/druck.test.ts`
65 * rechnet die Stützstellen jetzt nach.
66 */
67 const GROSSDRUCK = 1.9;
68
69 function runden(seiten: number): number {
70 return Math.max(1, Math.round(seiten));
71 }
72
73 /** Geschätzte Seitenzahl des Fehlerprotokolls. */
74 export function seitenFehlerprotokoll(
75 fragen: number,
76 tiefe: 'kurz' | 'voll',
77 schriftgroesse: Schriftgroesse,
78 ): number {
79 if (fragen === 0) {
80 return 1;
81 }
82 const roh = GRUNDLAST + fragen * (tiefe === 'voll' ? JE_FRAGE.fehlerVoll : JE_FRAGE.fehlerKurz);
83 return runden(schriftgroesse === 'gross' ? roh * GROSSDRUCK : roh);
84 }
85
86 /** Geschätzte Seitenzahl der Fragenliste. */
87 export function seitenFragenliste(
88 fragen: number,
89 mitLoesungen: boolean,
90 schriftgroesse: Schriftgroesse,
91 ): number {
92 if (fragen === 0) {
93 return 1;
94 }
95 const roh =
96 GRUNDLAST + fragen * (JE_FRAGE.fragenliste + (mitLoesungen ? JE_FRAGE.loesungsanhang : 0));
97 return runden(schriftgroesse === 'gross' ? roh * GROSSDRUCK : roh);
98 }
99
100 /**
101 * Der Satz, der vor dem Speichern dasteht.
102 *
103 * Nennt Blatt **und** Seiten: Wer druckt, denkt in Blättern, und beidseitig
104 * ist die Hälfte. 982 Seiten sind 491 Blatt – erst diese Zahl macht klar,
105 * worum es geht.
106 */
107 export function umfangssatz(seiten: number): string {
108 const blatt = Math.ceil(seiten / 2);
109 if (seiten === 1) {
110 return 'Geschätzt eine Seite.';
111 }
112 return (
113 `Geschätzt etwa ${String(seiten)} Seiten – beidseitig gedruckt rund ${String(blatt)} Blatt. ` +
114 'Die genaue Länge hängt davon ab, welche Fragen es trifft.'
115 );
116 }