1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952 | //! Sets a page whose every letter is one byte in the stream rather than two,
//! and states what that costs and what it buys.
//!
//! A font is reached by glyph index unless it is told otherwise: two bytes a
//! glyph, and every glyph the face holds within reach. Reached through a simple
//! encoding it is addressed by one-byte codes instead, at most 255 of them, and
//! the file states which character each code stands for. The page weighs the
//! same three lines both ways and prints the two figures it measured.
//!
//! The single byte also gives word spacing something to reach: the operator
//! moves the pen at the byte 32 and at no other code, so the last two lines of
//! the page are the same words drawn without it and with it.
//!
//! A font may also be reached through a code page named by its standard rather
//! than through codes fitted to the text: one line of the page is set through
//! Windows code page 1251, and the page prints the first bytes it cost.
//!
//! A second page reads the byte 0xC9 against each of the thirty encodings a
//! code can start from, and sets an encoding by hand: four codes settled over
//! an empty one, each with the name of the glyph it reaches, and a line drawn
//! through them. It says what the file names of that encoding, what a face is
//! handed where it lacks a character, and which glyph stands for the ones it
//! cannot draw.
//!
//! The terminal also says what a file holding only the samples weighs with the
//! face's program carried and with its description alone, which a face reached
//! by one-byte codes allows.
//!
//! Both sets of words are held in `Words`, once per language, and
//! `HQF_PDF_LANG` picks which set is drawn.
//!
//! Usage: `cargo run --example write_simple_encoding -- [out.pdf] [face.ttf]`
//! `HQF_PDF_LANG=fr cargo run --example write_simple_encoding`
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use hqf_pdf::content::Content;
use hqf_pdf::font::encoding::{BaseEncoding, Encoding, SimpleEncoding};
use hqf_pdf::font::{NOTDEF, character_named, glyph_name, stand_in};
use hqf_pdf::{Document, Font, FontHandle, Page, Rgb, TextFlow};
#[path = "shared/out.rs"]
mod out;
#[path = "shared/licence.rs"]
mod licence;
#[path = "shared/language.rs"]
mod language;
#[path = "shared/failure.rs"]
mod failure;
use language::Language;
/// The font the page is set in when none is given on the command line: the one
/// committed for the tests, so the example runs on any machine.
fn default_font() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("fonts")
.join("DejaVuSans.ttf")
}
/// The words the page is written in, one set per language.
#[derive(Debug)]
struct Words {
/// The page's title.
title: &'static str,
/// What the page is about.
lead: &'static str,
/// The label over the three lines set through one-byte codes.
shown: &'static str,
/// The three lines, drawn through the encoding.
samples: [&'static str; 3],
/// The label over the codes the encoding settled.
codes: &'static str,
/// The sentence the first of those codes are put into.
settled: &'static str,
/// The label over what the two streams weigh.
weight: &'static str,
/// The sentence the two weights are put into.
weighed: &'static str,
/// The label over the two lines that show word spacing.
spacing: &'static str,
/// The line drawn twice, once with word spacing and once without.
pushed: &'static str,
/// What those two lines show.
spread: &'static str,
/// The label over the line set through a named code page.
page: &'static str,
/// The sentence that line's first bytes are put into.
named: &'static str,
/// What an encoding of one byte cannot do.
caveat: &'static str,
/// The heading over the thirty encodings.
bases: &'static str,
/// What the thirty encodings are shown with.
bases_lead: &'static str,
/// What a code that stands for nothing is shown as.
nothing: &'static str,
/// The heading over the encoding settled by hand.
own: &'static str,
/// One code of it; `{code}`, `{point}` and `{name}` stand for the code, the
/// character's code point and its glyph's name.
slot: &'static str,
/// What the file names of it; `{base}` and `{count}` stand for the encoding
/// it starts from and how many codes differ from it.
named_of: &'static str,
/// What an encoding that starts from none is said to start from.
from_nothing: &'static str,
/// What a face lacking a character is handed; `{fi}` and `{ohm}` stand for
/// what stands in for the ligature and for the Ohm sign.
stand_ins: &'static str,
/// Which glyph a face draws for what it cannot draw; `{index}` stands for
/// it.
notdef: &'static str,
}
impl Words {
/// The words the page is written in, in `language`.
fn of(language: Language) -> &'static Self {
language::pick(&WORDS, language)
}
/// Every character the page draws through the encoding, the samples first:
/// the encoding hands out its codes in the order it meets its characters,
/// so the lines the page lists the codes of are the ones that take the
/// first codes.
fn drawn(&self) -> String {
let mut text = self.samples.concat();
text.push_str(self.pushed);
text
}
}
/// The page in English.
const ENGLISH: Words = Words {
title: "A page addressed by one-byte codes",
lead: "Every letter on the three lines below costs one byte of this file's \
page stream instead of two. A font is reached by glyph index unless \
it is told otherwise, two bytes a glyph, which puts every glyph the \
face holds within reach. Reached through an encoding it is addressed \
by one-byte codes instead: at most 255 of them, each standing for one \
character, and the file states which character each of them stands \
for.",
shown: "Set through one-byte codes",
samples: [
"HQF Development",
"An invoice, a payslip, a delivery note.",
"One byte a letter, and the file says which.",
],
codes: "The codes it settled",
settled: "The encoding is fitted to the words this page draws: the space \
keeps code 32, and every other character takes the lowest code \
free from 33 up, so nothing in the stream is a byte reading \
software has to escape. The first of them are %codes%.",
weight: "What the stream weighs",
weighed: "Drawn by glyph index, the three lines above cost %identity% bytes \
of page stream; drawn through one-byte codes, %simple% bytes, \
which is %saved% less. Both figures are measured by the program \
that drew this page, not written in by hand.",
spacing: "What the single byte 32 buys",
pushed: "Words reading software can push apart.",
spread: "The two lines above hold the same characters. The second is drawn \
with word spacing set, which moves the pen at the single byte 32 \
and at no other code. Under glyph indices no code is a single byte \
32, so the operator has nothing to reach and the words never move; \
the space of this encoding sits at 32, so they do.",
page: "A code page named by its standard",
named: "The line above is set through Windows code page 1251, named once \
rather than spelled out code by code: the file states the Windows \
encoding and names over it every code the page fills differently, \
so a reader finds each Cyrillic letter where the page puts it. Its \
first bytes are %bytes%.",
caveat: "One byte addresses 255 characters at a time, so a page of several \
alphabets is still reached by glyph index. No ligature and no small \
capital is drawn through an encoding either: those are glyphs no \
character stands for, and a code stands for a character. A file \
that claims archival conformance states the Windows encoding as the \
one its codes are read against, which this one does.",
bases: "Thirty encodings a code can be read against",
bases_lead: "What the byte 0xC9 stands for in each: the character's code point, and the \
character itself where this face can draw it.",
nothing: "nothing",
own: "An encoding settled by hand",
slot: "code {code} stands for {point}, whose glyph is named {name}",
named_of: "The line above is drawn through those four codes alone. The encoding \
starts from {base}, and the file names {count} codes that differ from it.",
from_nothing: "no encoding at all",
stand_ins: "A face that lacks the ligature U+FB01 is handed \u{201C}{fi}\u{201D} in its \
place, and one that lacks the Ohm sign U+2126, \u{201C}{ohm}\u{201D}.",
notdef: "What a face draws for a character it cannot draw is its glyph {index}, \
which every subset keeps.",
};
/// The page in French.
const FRENCH: Words = Words {
title: "Une page adressée par des codes d’un octet",
lead: "Chaque lettre des trois lignes ci-dessous coûte un octet du flux de cette \
page, et non deux. Une police est atteinte par indice de glyphe tant qu’on ne \
précise rien d’autre, à deux octets par glyphe, ce qui rend accessibles tous \
les glyphes qu’elle contient. Atteinte par un encodage, elle est adressée par \
des codes d’un octet : 255 au plus, chacun représentant un caractère, et le \
fichier indique quel caractère chacun d’eux représente.",
shown: "Composé avec des codes d’un octet",
samples: [
"HQF Development",
"Une facture, une fiche de paie, un bon de livraison.",
"Un octet par lettre, et le fichier dit laquelle.",
],
codes: "Les codes retenus",
settled: "L’encodage est ajusté aux mots de cette page : l’espace garde le code 32, et \
chaque autre caractère prend le plus petit code libre à partir de 33, si bien \
qu’aucun octet du flux ne demande d’échappement au logiciel de lecture. Les \
premiers sont %codes%.",
weight: "Ce que pèse le flux",
weighed: "Écrites par indice de glyphe, les trois lignes ci-dessus coûtent %identity% \
octets de flux de page ; écrites par des codes d’un octet, %simple% octets, \
soit %saved% de moins. Les deux chiffres sont mesurés par le programme qui a \
créé cette page, et non saisis à la main.",
spacing: "Ce que permet l’octet 32",
pushed: "Des mots qu’un logiciel de lecture peut écarter.",
spread: "Les deux lignes ci-dessus contiennent les mêmes caractères. La seconde est \
écrite avec un espacement des mots, qui avance la position d’écriture sur \
l’octet 32 et sur aucun autre code. Par indice de glyphe, aucun code ne vaut \
l’octet 32 seul : l’opérateur n’a rien à atteindre et les mots ne bougent \
jamais ; l’espace de cet encodage est en 32, donc ils bougent.",
page: "Une page de code nommée par sa norme",
named: "La ligne ci-dessus est composée avec la page de code Windows 1251, nommée une \
fois au lieu d’être détaillée code par code : le fichier annonce l’encodage \
Windows et redéfinit par-dessus chaque code que la page de code remplit \
autrement, si bien qu’un logiciel de lecture trouve chaque lettre cyrillique \
là où elle la place. Ses premiers octets sont %bytes%.",
caveat: "Un octet adresse 255 caractères à la fois : une page qui mêle plusieurs \
alphabets reste atteinte par indice de glyphe. Aucune ligature ni petite \
capitale ne passe non plus par un encodage : ce sont des glyphes qu’aucun \
caractère ne représente, et un code représente un caractère. Un fichier qui se \
déclare conforme à l’archivage annonce l’encodage Windows comme référence de \
lecture de ses codes, ce que fait celui-ci.",
bases: "Trente encodages selon lesquels lire un code",
bases_lead: "Ce que l’octet 0xC9 représente dans chacun : le point de code du \
caractère, et le caractère lui-même là où cette police sait le dessiner.",
nothing: "rien",
own: "Un encodage réglé à la main",
slot: "le code {code} représente {point}, dont le glyphe s’appelle {name}",
named_of: "La ligne ci-dessus n’est tracée qu’avec ces quatre codes. Base de \
l’encodage : {base} ; le fichier nomme {count} codes qui s’en écartent.",
from_nothing: "aucun encodage",
stand_ins: "Une police sans la ligature U+FB01 reçoit « {fi} » à sa place, et une \
police sans le signe ohm U+2126, « {ohm} ».",
notdef: "Pour un caractère qu’elle ne sait pas dessiner, une police dessine son \
glyphe {index}, que tout sous-ensemble conserve.",
};
/// The page in Dutch.
const DUTCH: Words = Words {
title: "Een pagina aangesproken met codes van één byte",
lead: "Elke letter op de drie regels hieronder kost één byte van de paginastroom van dit bestand in plaats van twee. Een lettertype wordt bereikt via glyphindex, tenzij anders aangegeven, twee bytes per glyph, waardoor elke glyph van de letter bereikbaar is. Via een codering wordt het in plaats daarvan aangesproken met codes van één byte: hoogstens 255, elk voor één teken, en het bestand zegt voor welk teken elke code staat.",
shown: "Gezet met codes van één byte",
samples: [
"HQF Development",
"Een factuur, een loonstrook, een pakbon.",
"Eén byte per letter, en het bestand zegt welke.",
],
codes: "De codes die het vastlegde",
settled: "De codering is afgestemd op de woorden die deze pagina tekent: de spatie houdt code 32, en elk ander teken krijgt de laagste vrije code vanaf 33, dus niets in de stroom is een byte die leessoftware moet escapen. De eerste zijn %codes%.",
weight: "Wat de stroom weegt",
weighed: "Getekend via glyphindex kosten de drie regels hierboven %identity% bytes paginastroom; via codes van één byte %simple% bytes, dat is %saved% minder. Beide getallen zijn gemeten door het programma dat deze pagina tekende, niet met de hand geschreven.",
spacing: "Wat de losse byte 32 oplevert",
pushed: "Woorden die leessoftware uit elkaar kan duwen.",
spread: "De twee regels hierboven bevatten dezelfde tekens. De tweede is getekend met woordafstand ingesteld, die de pen verplaatst bij de losse byte 32 en bij geen andere code. Onder glyphindices is geen enkele code een losse byte 32, dus heeft de operator niets te bereiken en bewegen de woorden nooit; de spatie van deze codering staat op 32, dus bewegen ze wel.",
page: "Een codetabel genoemd bij haar norm",
named: "De regel hierboven is gezet via Windows-codetabel 1251, één keer genoemd in plaats van code voor code uitgeschreven: het bestand noemt de Windows-codering en noemt daarbovenop elke code die de tabel anders invult, dus een lezer vindt elke Cyrillische letter waar de tabel haar zet. De eerste bytes zijn %bytes%.",
caveat: "Eén byte bereikt hoogstens 255 tekens, dus een pagina met meerdere schriften wordt nog steeds via glyphindex bereikt. Ook een ligatuur of kleinkapitaal wordt niet via een codering getekend: dat zijn glyphs waar geen teken voor staat, en een code staat voor een teken. Een bestand dat archiefconformiteit claimt, noemt de Windows-codering als die waartegen zijn codes gelezen worden, en dit bestand doet dat.",
bases: "Dertig coderingen waartegen een code gelezen kan worden",
bases_lead: "Waar de byte 0xC9 in elk voor staat: het codepunt van het teken, en het teken zelf waar deze letter het kan tekenen.",
nothing: "niets",
own: "Een codering met de hand vastgelegd",
slot: "code {code} staat voor {point}, waarvan de glyph {name} heet",
named_of: "De regel hierboven is getekend met alleen die vier codes. De codering vertrekt van {base}, en het bestand noemt {count} codes die ervan afwijken.",
from_nothing: "helemaal geen codering",
stand_ins: "Een letter zonder de ligatuur U+FB01 krijgt „{fi}” in de plaats, en een zonder het ohmteken U+2126, „{ohm}”.",
notdef: "Wat een letter tekent voor een teken dat hij niet kan tekenen, is zijn glyph {index}, die elke subset behoudt.",
};
/// The page in Portuguese.
const PORTUGUESE: Words = Words {
title: "Uma página endereçada por códigos de um byte",
lead: "Cada letra das três linhas abaixo custa um byte do fluxo de página deste ficheiro em vez de dois. Chega-se a um tipo de letra por índice de glifo salvo indicação em contrário, dois bytes por glifo, o que põe ao alcance cada glifo do tipo. Através de uma codificação é endereçado antes por códigos de um byte: no máximo 255, cada um por um carácter, e o ficheiro indica que carácter cada um representa.",
shown: "Composto por códigos de um byte",
samples: [
"HQF Development",
"Uma fatura, um recibo de vencimento, uma guia de remessa.",
"Um byte por letra, e o ficheiro diz qual.",
],
codes: "Os códigos que fixou",
settled: "A codificação ajusta-se às palavras que esta página desenha: o espaço mantém o código 32, e cada um dos outros caracteres toma o código livre mais baixo a partir de 33, por isso nada no fluxo é um byte que o programa de leitura tenha de escapar. Os primeiros são %codes%.",
weight: "O que pesa o fluxo",
weighed: "Desenhadas por índice de glifo, as três linhas acima custam %identity% bytes de fluxo de página; por códigos de um byte, %simple% bytes, ou seja %saved% a menos. Os dois números são medidos pelo programa que criou esta página, não escritos à mão.",
spacing: "O que o byte 32 sozinho permite",
pushed: "Palavras que um programa de leitura pode afastar.",
spread: "As duas linhas acima têm os mesmos caracteres. A segunda é desenhada com espaçamento entre palavras, que move a caneta no byte 32 sozinho e em nenhum outro código. Com índices de glifo nenhum código é um byte 32 sozinho, por isso o operador não tem nada a alcançar e as palavras nunca se mexem; o espaço desta codificação está no 32, por isso mexem-se.",
page: "Uma página de código nomeada pela sua norma",
named: "A linha acima é composta através da página de código 1251 do Windows, nomeada uma vez em vez de detalhada código a código: o ficheiro indica a codificação Windows e nomeia por cima cada código que a página preenche de outra forma, por isso um leitor encontra cada letra cirílica onde a página a põe. Os seus primeiros bytes são %bytes%.",
caveat: "Um byte endereça 255 caracteres de cada vez, por isso a uma página de vários alfabetos chega-se ainda por índice de glifo. Também nenhuma ligadura nem versalete é desenhada através de uma codificação: são glifos que nenhum carácter representa, e um código representa um carácter. Um ficheiro que declara conformidade de arquivo indica a codificação Windows como aquela contra a qual os seus códigos são lidos, e este fá-lo.",
bases: "Trinta codificações contra as quais um código pode ser lido",
bases_lead: "O que o byte 0xC9 representa em cada uma: o ponto de código do carácter, e o próprio carácter onde este tipo o sabe desenhar.",
nothing: "nada",
own: "Uma codificação fixada à mão",
slot: "o código {code} representa {point}, cujo glifo se chama {name}",
named_of: "A linha acima é desenhada só com esses quatro códigos. A codificação parte de {base}, e o ficheiro nomeia {count} códigos que diferem dela.",
from_nothing: "nenhuma codificação",
stand_ins: "A um tipo sem a ligadura U+FB01 dá-se «{fi}» no seu lugar, e a um sem o símbolo de ohm U+2126, «{ohm}».",
notdef: "O que um tipo desenha para um carácter que não sabe desenhar é o seu glifo {index}, que todo o subconjunto conserva.",
};
/// The page in Italian.
const ITALIAN: Words = Words {
title: "Una pagina indirizzata con codici di un byte",
lead: "Ogni lettera delle tre righe qui sotto costa un byte del flusso di pagina di questo file anziché due. A un font si arriva per indice di glifo salvo diversa indicazione, due byte per glifo, il che mette a portata ogni glifo del carattere. Attraverso una codifica lo si indirizza invece con codici di un byte: al massimo 255, ciascuno per un carattere, e il file indica quale carattere rappresenta ciascuno.",
shown: "Composto con codici di un byte",
samples: [
"HQF Development",
"Una fattura, una busta paga, un documento di trasporto.",
"Un byte per lettera, e il file dice quale.",
],
codes: "I codici che ha fissato",
settled: "La codifica è adattata alle parole che disegna questa pagina: lo spazio tiene il codice 32, e ogni altro carattere prende il codice libero più basso dal 33 in su, così niente nel flusso è un byte che il programma di lettura debba proteggere. I primi sono %codes%.",
weight: "Quanto pesa il flusso",
weighed: "Disegnate per indice di glifo, le tre righe qui sopra costano %identity% byte di flusso di pagina; con codici di un byte, %simple% byte, cioè %saved% in meno. Entrambe le cifre sono misurate dal programma che ha disegnato questa pagina, non scritte a mano.",
spacing: "Cosa compra il singolo byte 32",
pushed: "Parole che un programma di lettura può allargare.",
spread: "Le due righe qui sopra hanno gli stessi caratteri. La seconda è disegnata con la spaziatura tra parole impostata, che sposta la penna al singolo byte 32 e a nessun altro codice. Con gli indici di glifo nessun codice è un singolo byte 32, così l’operatore non ha nulla da raggiungere e le parole non si muovono; lo spazio di questa codifica sta al 32, così si muovono.",
page: "Una pagina di codici nominata dalla sua norma",
named: "La riga qui sopra è composta con la pagina di codici 1251 di Windows, nominata una volta anziché scritta codice per codice: il file indica la codifica Windows e nomina sopra ogni codice che la pagina riempie in modo diverso, così un programma di lettura trova ogni lettera cirillica dove la mette la pagina. I suoi primi byte sono %bytes%.",
caveat: "Un byte indirizza 255 caratteri alla volta, così a una pagina di più alfabeti si arriva ancora per indice di glifo. Nemmeno una legatura o un maiuscoletto si disegna attraverso una codifica: sono glifi che nessun carattere rappresenta, e un codice rappresenta un carattere. Un file che dichiara conformità d’archivio indica la codifica Windows come quella su cui si leggono i suoi codici, e questo lo fa.",
bases: "Trenta codifiche su cui si può leggere un codice",
bases_lead: "Ciò che il byte 0xC9 rappresenta in ciascuna: il punto di codice del carattere, e il carattere stesso dove questo carattere tipografico sa disegnarlo.",
nothing: "nulla",
own: "Una codifica fissata a mano",
slot: "il codice {code} rappresenta {point}, il cui glifo si chiama {name}",
named_of: "La riga qui sopra è disegnata solo con quei quattro codici. La codifica parte da {base}, e il file nomina {count} codici che ne differiscono.",
from_nothing: "nessuna codifica",
stand_ins: "A un carattere tipografico privo della legatura U+FB01 si dà «{fi}» al suo posto, e a uno privo del simbolo di ohm U+2126, «{ohm}».",
notdef: "Ciò che un carattere tipografico disegna per un carattere che non sa disegnare è il suo glifo {index}, che ogni sottoinsieme conserva.",
};
/// The page in Spanish.
const SPANISH: Words = Words {
title: "Una página direccionada con códigos de un byte",
lead: "Cada letra de las tres líneas de abajo cuesta un byte del flujo de página de este archivo en vez de dos. A una fuente se llega por índice de glifo salvo que se diga otra cosa, dos bytes por glifo, lo que da acceso a cada glifo de la fuente. A través de una codificación se la direcciona en cambio con códigos de un byte: 255 como mucho, cada uno por un carácter, y el archivo indica qué carácter representa cada uno.",
shown: "Compuesto con códigos de un byte",
samples: [
"HQF Development",
"Una factura, una nómina, un albarán.",
"Un byte por letra, y el archivo dice cuál.",
],
codes: "Los códigos que fijó",
settled: "La codificación se ajusta a las palabras que dibuja esta página: el espacio conserva el código 32, y cada uno de los demás caracteres toma el código libre más bajo desde el 33, así que nada en el flujo es un byte que el programa de lectura tenga que escapar. Los primeros son %codes%.",
weight: "Lo que pesa el flujo",
weighed: "Dibujadas por índice de glifo, las tres líneas de arriba cuestan %identity% bytes de flujo de página; con códigos de un byte, %simple% bytes, que es %saved% menos. Las dos cifras las mide el programa que dibujó esta página, no se escriben a mano.",
spacing: "Lo que da el byte 32 solo",
pushed: "Palabras que un programa de lectura puede separar.",
spread: "Las dos líneas de arriba tienen los mismos caracteres. La segunda se dibuja con espaciado de palabras, que mueve la pluma en el byte 32 solo y en ningún otro código. Con índices de glifo ningún código es un byte 32 solo, así que el operador no tiene nada que alcanzar y las palabras no se mueven; el espacio de esta codificación está en el 32, así que sí se mueven.",
page: "Una página de códigos nombrada por su norma",
named: "La línea de arriba se compone con la página de códigos 1251 de Windows, nombrada una vez en vez de detallada código a código: el archivo indica la codificación Windows y nombra además cada código que la página de códigos asigna de otra forma, así que un programa de lectura encuentra cada letra cirílica donde la pone la página. Sus primeros bytes son %bytes%.",
caveat: "Un byte direcciona 255 caracteres a la vez, así que a una página de varios alfabetos se sigue llegando por índice de glifo. Tampoco se dibuja ninguna ligadura ni versalita a través de una codificación: son glifos que ningún carácter representa, y un código representa un carácter. Un archivo que declara conformidad de archivo indica la codificación Windows como aquella con que se leen sus códigos, y este lo hace.",
bases: "Treinta codificaciones con que puede leerse un código",
bases_lead: "Lo que representa el byte 0xC9 en cada una: el punto de código del carácter, y el carácter mismo donde esta fuente puede dibujarlo.",
nothing: "nada",
own: "Una codificación fijada a mano",
slot: "el código {code} representa {point}, cuyo glifo se llama {name}",
named_of: "La línea de arriba se dibuja solo con esos cuatro códigos. La codificación parte de {base}, y el archivo nombra {count} códigos que difieren de ella.",
from_nothing: "ninguna codificación",
stand_ins: "A una fuente sin la ligadura U+FB01 se le da «{fi}» en su lugar, y a una sin el signo ohmio U+2126, «{ohm}».",
notdef: "Lo que dibuja una fuente para un carácter que no puede dibujar es su glifo {index}, que todo subconjunto conserva.",
};
/// The page in German.
const GERMAN: Words = Words {
title: "Eine Seite, über Ein-Byte-Codes angesprochen",
lead: "Jeder Buchstabe in den drei Zeilen unten kostet ein Byte im Seitenstrom dieser Datei statt zwei. Eine Schrift wird über den Glyphenindex angesprochen, sofern ihr nichts anderes gesagt wird, zwei Bytes je Glyph, womit jeder Glyph des Schnitts erreichbar ist. Über eine Kodierung erreicht, wird sie stattdessen mit Ein-Byte-Codes angesprochen: höchstens 255, jeder steht für ein Zeichen, und die Datei gibt an, für welches.",
shown: "Über Ein-Byte-Codes gesetzt",
samples: [
"HQF Development",
"Eine Rechnung, eine Gehaltsabrechnung, ein Lieferschein.",
"Ein Byte je Buchstabe, und die Datei sagt, welcher.",
],
codes: "Die festgelegten Codes",
settled: "Die Kodierung ist auf die Wörter zugeschnitten, die diese Seite zeichnet: Das Leerzeichen behält Code 32, und jedes andere Zeichen nimmt den niedrigsten freien Code ab 33, also steht im Strom kein Byte, das eine Lesesoftware maskieren müsste. Die ersten davon sind %codes%.",
weight: "Was der Strom wiegt",
weighed: "Über den Glyphenindex gezeichnet, kosten die drei Zeilen oben %identity% Bytes Seitenstrom; über Ein-Byte-Codes %simple% Bytes, also %saved% weniger. Beide Zahlen misst das Programm, das diese Seite erstellt hat, keine ist von Hand eingetragen.",
spacing: "Was das einzelne Byte 32 bringt",
pushed: "Wörter, die eine Lesesoftware auseinanderschieben kann.",
spread: "Die beiden Zeilen oben enthalten dieselben Zeichen. Die zweite ist mit Wortabstand gezeichnet, der den Stift beim einzelnen Byte 32 weiterschiebt und bei keinem anderen Code. Unter Glyphenindizes ist kein Code ein einzelnes Byte 32, also findet der Operator nichts und die Wörter bewegen sich nie; das Leerzeichen dieser Kodierung liegt auf 32, also bewegen sie sich.",
page: "Eine Codepage, nach ihrer Norm benannt",
named: "Die Zeile oben ist über die Windows-Codepage 1251 gesetzt, einmal benannt, statt Code für Code ausgeschrieben: Die Datei nennt die Windows-Kodierung und darüber jeden Code, den die Codepage anders belegt, also findet ein Leser jeden kyrillischen Buchstaben dort, wo die Codepage ihn hinsetzt. Ihre ersten Bytes sind %bytes%.",
caveat: "Ein Byte reicht für höchstens 255 Zeichen zugleich, also wird eine Seite mit mehreren Alphabeten weiter über den Glyphenindex erreicht. Auch Ligaturen und Kapitälchen lassen sich nicht über eine Kodierung zeichnen: Das sind Glyphen, für die kein Zeichen steht, und ein Code steht für ein Zeichen. Eine Datei, die Archivkonformität beansprucht, nennt die Windows-Kodierung als die, nach der ihre Codes gelesen werden, wie diese es tut.",
bases: "Dreißig Kodierungen, nach denen ein Code gelesen werden kann",
bases_lead: "Wofür das Byte 0xC9 in jeder steht: der Codepunkt des Zeichens und das Zeichen selbst, wo dieser Schnitt es zeichnen kann.",
nothing: "nichts",
own: "Eine von Hand festgelegte Kodierung",
slot: "Code {code} steht für {point}, dessen Glyph {name} heißt",
named_of: "Die Zeile oben ist allein über diese vier Codes gezeichnet. Die Kodierung geht von {base} aus, und die Datei nennt {count} Codes, die davon abweichen.",
from_nothing: "überhaupt keine Kodierung",
stand_ins: "Ein Schnitt ohne die Ligatur U+FB01 bekommt an ihrer Stelle „{fi}“, und einer ohne das Ohm-Zeichen U+2126 „{ohm}“.",
notdef: "Was ein Schnitt für ein Zeichen zeichnet, das er nicht zeichnen kann, ist sein Glyph {index}, den jede Teilmenge behält.",
};
/// Every language the example is written in. A language is added by writing its
/// own set of words and naming it here.
static WORDS: [(Language, &Words); 7] = [
(Language::English, &ENGLISH),
(Language::French, &FRENCH),
(Language::German, &GERMAN),
(Language::Spanish, &SPANISH),
(Language::Italian, &ITALIAN),
(Language::Portuguese, &PORTUGUESE),
(Language::Dutch, &DUTCH),
];
/// The left edge of everything on the page.
const LEFT: f64 = 72.0;
/// How wide a block of text is.
const WIDTH: f64 = 450.0;
/// The size the three samples are drawn at.
const SAMPLE: f64 = 14.0;
/// The size the two lines showing word spacing are drawn at.
const SPACED: f64 = 12.0;
/// How far apart the second of those two lines pushes its words.
const WORD_SPACING: f64 = 6.0;
/// The code page the Cyrillic line is set through.
const CODE_PAGE: BaseEncoding = BaseEncoding::Windows1251;
/// The line set through that code page, the same in every language.
const PAGED: &str = "Счёт-фактура № 117 — оплата до 30 сентября";
/// How many of that line's bytes the page prints.
const PRINTED_BYTES: usize = 8;
/// Sets a block of words with its first baseline at `top`, and hands back the
/// ordinate the block ends at.
fn block(
c: &mut Content,
handle: &FontHandle,
size: f64,
top: f64,
text: &str,
) -> Result<f64, Box<dyn std::error::Error>> {
let flow = TextFlow::new(handle, size);
let lines = flow.break_lines(text, WIDTH);
c.begin_text();
flow.draw(c, &lines, LEFT, top, WIDTH)?;
c.end_text();
Ok(top - flow.height(&lines))
}
/// What the three samples cost in a page stream, drawn through `handle`.
///
/// The two handles write the same operators around the same three lines, so
/// what one weighs against the other is what the codes weigh against the
/// indices.
fn stream_bytes(handle: &FontHandle, words: &Words) -> Result<usize, Box<dyn std::error::Error>> {
let mut c = Content::new();
let mut top = 700.0;
for sample in words.samples {
top = block(&mut c, handle, SAMPLE, top, sample)? - 10.0;
}
Ok(c.as_bytes().len())
}
/// What a file holding only the three samples weighs, drawn through `font`
/// reached by `encoding`: with the face's program carried, then with its
/// description alone, which leaves the glyphs to a face the reader finds.
fn file_weights(
font: &Font,
encoding: &SimpleEncoding,
words: &Words,
) -> Result<[usize; 2], Box<dyn std::error::Error>> {
let mut weights = [0; 2];
for (described_only, weight) in weights.iter_mut().enumerate() {
let mut doc = Document::new();
doc.set_license(licence::licensed());
let coded = font
.clone()
.with_encoding(Encoding::Simple(encoding.clone()))?;
let handle = doc.add_font(coded);
if described_only == 1 {
doc.carry_font_description_only(&handle)?;
}
let mut c = Content::new();
let mut top = 700.0;
for sample in words.samples {
top = block(&mut c, &handle, SAMPLE, top, sample)? - 10.0;
}
let mut page = Page::a4();
page.content = c.into_bytes();
doc.add_page(page)?;
*weight = doc.to_bytes()?.len();
}
Ok(weights)
}
/// The first `count` characters of the samples, each beside the code that
/// reaches it.
fn settled(encoding: &SimpleEncoding, words: &Words, count: usize) -> String {
let mut seen: Vec<char> = Vec::new();
for character in words.samples.concat().chars() {
if character != ' ' && !seen.contains(&character) {
seen.push(character);
}
}
seen.iter()
.filter_map(|character| {
encoding
.code(*character)
.map(|code| format!("{character} {code}"))
})
.take(count)
.collect::<Vec<String>>()
.join(" · ")
}
/// The first `count` codes `line` is written in through `encoding`, in
/// hexadecimal.
fn first_bytes(encoding: &SimpleEncoding, line: &str, count: usize) -> String {
line.chars()
.filter_map(|character| encoding.code(character))
.take(count)
.map(|code| format!("{code:02X}"))
.collect::<Vec<String>>()
.join(" ")
}
/// Draws the whole page.
fn build(
words: &Words,
font: Font,
encoding: &SimpleEncoding,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let one_byte = font
.clone()
.with_encoding(Encoding::Simple(encoding.clone()))?;
let page_encoding = SimpleEncoding::new(CODE_PAGE);
let paged = font
.clone()
.with_encoding(Encoding::Simple(page_encoding.clone()))?;
let mut doc = Document::new();
doc.set_license(licence::licensed());
let base_font = font.clone();
let text = doc.add_font(font);
let drawn = doc.add_font(one_byte);
let cyrillic = doc.add_font(paged);
let mut c = Content::new();
let mut top = 780.0;
top = block(&mut c, &text, 18.0, top, words.title)? - 14.0;
top = block(&mut c, &text, 10.0, top, words.lead)? - 22.0;
top = block(&mut c, &text, 12.0, top, words.shown)? - 12.0;
for sample in words.samples {
top = block(&mut c, &drawn, SAMPLE, top, sample)? - 8.0;
}
top -= 14.0;
top = block(&mut c, &text, 12.0, top, words.codes)? - 10.0;
let listed = words
.settled
.replace("%codes%", &settled(encoding, words, 8));
top = block(&mut c, &text, 10.0, top, &listed)? - 20.0;
let (indices, codes) = (stream_bytes(&text, words)?, stream_bytes(&drawn, words)?);
top = block(&mut c, &text, 12.0, top, words.weight)? - 10.0;
let said = words
.weighed
.replace("%identity%", &spaced(indices))
.replace("%simple%", &spaced(codes))
.replace("%saved%", &spaced(indices - codes));
top = block(&mut c, &text, 10.0, top, &said)? - 20.0;
top = block(&mut c, &text, 12.0, top, words.spacing)? - 12.0;
top = block(&mut c, &drawn, SPACED, top, words.pushed)? - 8.0;
c.set_word_spacing(WORD_SPACING)?;
top = block(&mut c, &drawn, SPACED, top, words.pushed)? - 18.0;
c.set_word_spacing(0.0)?;
top = block(&mut c, &text, 10.0, top, words.spread)? - 20.0;
top = block(&mut c, &text, 12.0, top, words.page)? - 12.0;
top = block(&mut c, &cyrillic, SAMPLE, top, PAGED)? - 10.0;
let bytes = words.named.replace(
"%bytes%",
&first_bytes(&page_encoding, PAGED, PRINTED_BYTES),
);
top = block(&mut c, &text, 10.0, top, &bytes)? - 20.0;
c.set_fill(Rgb::gray(0.35))?;
block(&mut c, &text, 9.0, top, words.caveat)?;
let mut page = Page::a4();
page.content = c.into_bytes();
doc.add_page(page)?;
let page = second_page(&mut doc, &base_font, &text, words)?;
doc.add_page(page)?;
Ok(doc.to_bytes()?)
}
/// What each of the thirty encodings is called, in the order
/// [`BaseEncoding::ALL`] holds them: names of standards, the same in every
/// language.
const BASE_NAMES: [&str; 30] = [
"WinAnsi",
"Standard",
"PDFDoc",
"ISO 8859-1",
"ISO 8859-2",
"ISO 8859-3",
"ISO 8859-4",
"ISO 8859-5",
"ISO 8859-6",
"ISO 8859-7",
"ISO 8859-8",
"ISO 8859-9",
"ISO 8859-10",
"ISO 8859-11",
"ISO 8859-13",
"ISO 8859-14",
"ISO 8859-15",
"ISO 8859-16",
"Windows-874",
"Windows-1250",
"Windows-1251",
"Windows-1253",
"Windows-1254",
"Windows-1255",
"Windows-1256",
"Windows-1257",
"Windows-1258",
"Mac Roman",
"IBM 037",
"IBM 500",
];
/// The byte the second page reads against every encoding.
const PROBED: u8 = 0xC9;
/// The four codes the encoding settled by hand holds, and what each stands for.
const SLOTS: [(u8, char); 4] = [
(0x41, '\u{0416}'),
(0x42, '\u{03A9}'),
(0x43, '\u{20AC}'),
(0x44, '\u{00C9}'),
];
/// The name `BASE_NAMES` gives `base`.
fn base_name(base: BaseEncoding) -> &'static str {
BaseEncoding::ALL
.iter()
.position(|each| *each == base)
.map_or("", |place| BASE_NAMES[place])
}
/// The second page: the byte 0xC9 read against every encoding, and an encoding
/// settled by hand.
fn second_page(
doc: &mut Document,
font: &Font,
text: &FontHandle,
words: &Words,
) -> Result<Page, Box<dyn std::error::Error>> {
let mut c = Content::new();
let mut top = 780.0;
top = block(&mut c, text, 16.0, top, words.bases)? - 10.0;
top = block(&mut c, text, 10.0, top, words.bases_lead)? - 16.0;
for (place, encoding_read) in BaseEncoding::ALL.into_iter().enumerate() {
let x = 150.0f64.mul_add(f64::from(u8::try_from(place / 10)?), LEFT);
let y = 14.0f64.mul_add(-f64::from(u8::try_from(place % 10)?), top);
let read = encoding_read.character(PROBED).map_or_else(
|| words.nothing.to_owned(),
|character| {
let point = format!("U+{:04X}", u32::from(character));
if font.uncovered(&character.to_string()).is_empty() {
format!("{point} {character}")
} else {
point
}
},
);
c.begin_text();
c.set_font(text, 9.0)?;
c.text_origin(x, y)?;
c.show_glyphs(&text.glyphs(base_name(encoding_read)));
c.end_text();
c.begin_text();
c.set_font(text, 9.0)?;
c.text_origin(x + 72.0, y)?;
c.show_glyphs(&text.glyphs(&read));
c.end_text();
}
// Ten rows of the thirty, and a gap under them.
top -= 158.0;
top = block(&mut c, text, 12.0, top, words.own)? - 10.0;
let mut own = SimpleEncoding::empty();
for (slot_code, character) in SLOTS {
own = own.slot(slot_code, character)?;
}
for (slotted, character) in SLOTS {
let glyph = glyph_name(character);
let back = character_named(&glyph).map_or_else(
|| words.nothing.to_owned(),
|read| format!("U+{:04X}", u32::from(read)),
);
let said = words
.slot
.replace("{code}", &format!("0x{slotted:02X}"))
.replace("{point}", &back)
.replace("{name}", &glyph);
top = block(&mut c, text, 9.5, top, &said)? - 2.0;
}
let encoding = Encoding::Simple(own.clone());
let handed = doc.add_font(font.clone().with_encoding(encoding.clone())?);
let drawn: String = SLOTS
.iter()
.map(|(slot_code, _)| char::from(*slot_code))
.collect();
let line: String = drawn
.bytes()
.filter_map(|byte| own.character(byte))
.collect();
top = block(&mut c, &handed, 16.0, top - 6.0, &line)? - 10.0;
let started = encoding
.simple()
.and_then(SimpleEncoding::base)
.map_or(words.from_nothing, base_name);
let said = words
.named_of
.replace("{base}", started)
.replace("{count}", &own.differences().count().to_string());
top = block(&mut c, text, 10.0, top, &said)? - 14.0;
let said = words
.stand_ins
.replace("{fi}", stand_in('\u{FB01}').unwrap_or_default())
.replace("{ohm}", stand_in('\u{2126}').unwrap_or_default());
top = block(&mut c, text, 10.0, top, &said)? - 14.0;
let said = words.notdef.replace("{index}", &NOTDEF.index().to_string());
block(&mut c, text, 10.0, top, &said)?;
let mut page = Page::a4();
page.content = c.into_bytes();
Ok(page)
}
fn main() -> std::process::ExitCode {
failure::reported(run())
}
fn run() -> Result<(), Box<dyn std::error::Error>> {
let language = Language::from_environment()?;
let words = Words::of(language);
let mut args = env::args().skip(1);
// A named file is written as named; the default one carries the language,
// so the two languages do not overwrite each other in `tmp/`.
let out = args
.next()
.unwrap_or_else(|| language.file_name(&out::default_path("simple_encoding")));
let face = args.next().map_or_else(default_font, PathBuf::from);
let font = Font::parse(fs::read(&face)?)?;
let encoding = SimpleEncoding::fitting(&font, &words.drawn())?;
let [carried, described] = file_weights(&font, &encoding, words)?;
let drawn = build(words, font, &encoding)?;
if let Some(parent) = Path::new(&out).parent() {
fs::create_dir_all(parent)?;
}
fs::write(&out, &drawn)?;
println!("wrote {out}: {} bytes", drawn.len());
println!(
"the samples alone weigh {carried} bytes with the face carried, {described} with \
its description only"
);
Ok(())
}
/// A count with a space between its thousands, which is how every language this
/// example is written in writes one.
fn spaced(count: usize) -> String {
let digits = count.to_string();
let mut out = String::with_capacity(digits.len() + digits.len() / 3);
for (index, digit) in digits.chars().enumerate() {
if index > 0 && (digits.len() - index).is_multiple_of(3) {
out.push('\u{00A0}');
}
out.push(digit);
}
out
}
#[cfg(test)]
mod tests {
use hqf_pdf::font::encoding::SimpleEncoding;
use hqf_pdf::{Document, Font};
use super::{
CODE_PAGE, PAGED, SAMPLE, SPACED, WIDTH, WORD_SPACING, WORDS, default_font, first_bytes,
language, spaced,
};
/// The lines two languages are allowed to write the same way: the name of
/// the firm is the same whoever reads it.
const SPARED: [&str; 2] = [
"\"HQF Development\"",
// Written the same way in Portuguese.
r#"nothing: "nada""#,
];
/// The committed font, parsed.
fn fixture() -> Font {
Font::parse(std::fs::read(default_font()).expect("the committed font is there"))
.expect("the committed font parses")
}
#[test]
fn every_language_draws_the_page_in_its_own_words() {
let untranslated = language::untranslated_lines(&WORDS, &SPARED);
assert!(
untranslated.is_empty(),
"the page says these in more than one language: {untranslated:?}"
);
}
/// A sample wider than the block is broken over two lines, which moves
/// everything under it up the page.
#[test]
fn every_language_writes_samples_that_fit_the_block_they_have() {
let mut doc = Document::new();
let handle = doc.add_font(fixture());
for (named, words) in WORDS {
for sample in words.samples {
let measured = handle.measure(sample, SAMPLE);
assert!(
measured <= WIDTH,
"the {} page draws {sample:?} over {measured:.1} points, \
and it has {WIDTH:.1}",
named.code()
);
}
}
}
/// Word spacing moves the pen without moving the measurement: the line it
/// is set on draws wider than the measurement says, by the spacing times
/// the number of spaces, and the block it is drawn in does not know that.
#[test]
fn every_language_pushes_its_words_apart_within_the_block() {
let mut doc = Document::new();
let handle = doc.add_font(fixture());
for (named, words) in WORDS {
let spaces = u32::try_from(words.pushed.chars().filter(|c| *c == ' ').count())
.expect("a line of few words");
let drawn =
WORD_SPACING.mul_add(f64::from(spaces), handle.measure(words.pushed, SPACED));
assert!(
drawn <= WIDTH,
"the {} page pushes {:?} out to {drawn:.1} points, and it has {WIDTH:.1}",
named.code(),
words.pushed
);
}
}
/// The page states the codes it settled, so it has to have settled them:
/// the encoding is fitted to what the page draws, and a character it left
/// out would be drawn as nothing at all.
#[test]
fn every_language_has_a_code_for_every_character_it_draws() {
let font = fixture();
for (named, words) in WORDS {
let encoding =
SimpleEncoding::fitting(&font, &words.drawn()).expect("one page fits 255 codes");
for character in words.drawn().chars() {
assert!(
encoding.code(character).is_some(),
"the {} page draws {character:?}, and no code reaches it",
named.code()
);
}
}
}
/// Every code the page draws through stands in the printable range, so the
/// stream holds no byte reading software has to escape.
#[test]
fn the_codes_the_page_draws_through_are_all_printable() {
let font = fixture();
for (_, words) in WORDS {
let encoding =
SimpleEncoding::fitting(&font, &words.drawn()).expect("one page fits 255 codes");
for character in words.drawn().chars() {
let code = encoding.code(character).expect("a code reaches it");
assert!(code >= 32, "{character:?} is reached by {code}");
}
}
}
/// The line set through the code page is one the page reaches whole: a
/// character the page has no code for would be drawn as nothing at all.
#[test]
fn the_code_page_reaches_every_character_of_its_line() {
let encoding = SimpleEncoding::new(CODE_PAGE);
let font = fixture();
for character in PAGED.chars() {
assert!(
encoding.code(character).is_some(),
"{CODE_PAGE:?} has no code for {character:?}"
);
assert!(
font.glyph(character).is_some(),
"the face has no glyph for {character:?}"
);
}
assert_eq!(first_bytes(&encoding, PAGED, 8), "D1 F7 B8 F2 2D F4 E0 EA");
}
#[test]
fn a_count_is_written_with_a_space_between_its_thousands() {
assert_eq!(spaced(7), "7");
assert_eq!(spaced(999), "999");
assert_eq!(spaced(1000), "1\u{00A0}000");
assert_eq!(spaced(8117), "8\u{00A0}117");
}
}
|