This source file includes following definitions.
- xstrerror_l
- store_page
- start_object
- print_bars
- printstring
- printme_top
- print_margin_label
- start_page
- end_page
- do_text
- read_u16_be
- read_u32_be
- read_u16_be_from_mem
- read_u32_be_from_mem
- emit_utf8_from_codepoint
- utf16be_to_utf8
- next_utf8_cp
- is_win1252_defined_codepoint
- is_win1252_compatible_utf8
- mac_roman_to_utf8
- decode_name_to_utf8
- score_name_record
- read_cff_postscript_name
- read_cff_postscript_name_from_mem
- read_font_name
- read_font_name_from_mem
- embed_font
- embed_font_from_memory
- dopages
- cleanup_globals
- showhelp
- parse_tab_stops
- main
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 #if !defined(_POSIX_C_SOURCE)
26 # define _POSIX_C_SOURCE 200809L
27 #endif
28
29 #if !defined(_GNU_SOURCE)
30 # define _GNU_SOURCE
31 #endif
32
33 #if !defined(_NETBSD_SOURCE)
34 # define _NETBSD_SOURCE
35 #endif
36
37 #if !defined(_OPENBSD_SOURCE)
38 # define _OPENBSD_SOURCE
39 #endif
40
41
42
43
44
45 #include <ctype.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <math.h>
49 #include <stdint.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <sys/stat.h>
54 #include <time.h>
55 #include <unistd.h>
56
57
58
59
60
61 #if !defined(_AIX)
62 # include <getopt.h>
63 #endif
64
65
66
67
68
69 #if defined(_WIN32)
70 # include <io.h>
71 #endif
72
73
74
75
76
77 #include <locale.h>
78 #if defined(__APPLE__)
79 # include <xlocale.h>
80 #endif
81
82
83
84
85
86
87 #include "CourierMultics-Roman.h"
88 #include "CourierMultics-Bold.h"
89
90
91
92
93
94
95 #include "ROSYMultics-Regular.h"
96 #include "ROSYMultics-Bold.h"
97
98
99
100
101
102 #if defined(FREE)
103 # undef FREE
104 #endif
105 #define FREE(p) \
106 do \
107 { \
108 free ((p)); \
109 (p) = NULL; \
110 } \
111 while (0)
112
113
114
115
116
117 #undef MAX
118 #define MAX(x, y) ((x) > (y) ? (x) : (y))
119
120
121
122
123
124 static char * GLOBAL_SHADE_COLOR = NULL;
125 static float GLOBAL_FONT_SIZE;
126 static float GLOBAL_GRAY_SCALE;
127 static float GLOBAL_LEAD_SIZE;
128 static float GLOBAL_LINES_PER_PAGE;
129 static float GLOBAL_PAGE_DEPTH;
130 static float GLOBAL_PAGE_MARGIN_BOTTOM;
131 static float GLOBAL_PAGE_MARGIN_LEFT;
132 static float GLOBAL_PAGE_MARGIN_RIGHT;
133 static float GLOBAL_PAGE_MARGIN_TOP;
134 static float GLOBAL_PAGE_WIDTH;
135 static float GLOBAL_UNIT_MULTIPLIER = 72.0;
136 static float GLOBAL_YPOS;
137 static int GLOBAL_ADD = 0;
138 static int GLOBAL_GREEN_BAR;
139 static int GLOBAL_NUM_PAGES = 0;
140 static int GLOBAL_NUM_XREFS = 0;
141 static int GLOBAL_OBJECT_ID = 1;
142 static int GLOBAL_PAGE_HELP = 0;
143 static int GLOBAL_PAGE_TREE_ID;
144 static int GLOBAL_SHADE_ALTERNATE_START = 0;
145 static int GLOBAL_SHADE_EDGE_TO_EDGE = 0;
146 static int GLOBAL_SHADE_STEP = 1;
147 static int GLOBAL_SHIFT = 0;
148 static int GLOBAL_STREAM_ID, GLOBAL_STREAM_LEN_ID;
149 static int GLOBAL_VERSION = 4;
150 static int GLOBAL_VERSION_MIN = 4;
151 static int GLOBAL_VERSION_PATCH = 1;
152 static int GLOBAL_ZERO_OVERSTRIKE = 0;
153 static int GLOBAL_SEVEN_OVERSTRIKE = 0;
154 static int GLOBAL_ZED_OVERSTRIKE = 0;
155 static int GLOBAL_O_MODE = 0;
156 static int GLOBAL_ONE_UNDERLINE = 0;
157 static int GLOBAL_DOT_MATRIX = 0;
158 static long GLOBAL_STREAM_START;
159 static long * GLOBAL_XREFS = NULL;
160 static int GLOBAL_TAB_PROCESSING = 0;
161 static int * GLOBAL_TAB_STOPS = NULL;
162 static int GLOBAL_TAB_INTERVAL = 0;
163 static int GLOBAL_TAB_RELATIVE_INTERVAL = 0;
164
165
166
167
168
169 typedef struct _PageList
170 {
171 struct _PageList * next;
172 int page_id;
173 #if !defined(__MINGW64__)
174 char pad [sizeof (void (*) (void)) - sizeof (int)];
175 #endif
176 } PageList;
177
178
179
180
181
182 static PageList * GLOBAL_PAGE_LIST = NULL;
183 static PageList * * GLOBAL_INSERT_PAGE = & GLOBAL_PAGE_LIST;
184
185
186
187
188
189 #define MAX_OVERSTRIKES 8
190
191
192
193
194
195 typedef struct
196 {
197 char chars [MAX_OVERSTRIKES];
198 int num_chars;
199 char under_char;
200 int under_bold;
201 int under_from_input;
202 } PageCell;
203
204
205
206
207
208 #define MAX_COLS 256
209 #define MAX_LINES 256
210
211
212
213
214
215 static PageCell page_grid [MAX_LINES] [MAX_COLS];
216 static int current_line = 0;
217
218
219
220
221
222 #if !defined(NO_LOCALE)
223 # define XSTR_EMAXLEN 32767
224
225 static const char *
226 xstrerror_l (int errnum)
227 {
228 int saved = errno;
229 const char * ret = NULL;
230 static char buf [XSTR_EMAXLEN];
231
232 # if defined(__APPLE__) || defined(_AIX) || defined(__MINGW32__) || defined(__MINGW64__) || \
233 defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
234 # if defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
235 if (0 == strerror_s (buf, sizeof (buf), errnum))
236 ret = buf;
237 # else
238 if (0 == strerror_r (errnum, buf, sizeof (buf)))
239 ret = buf;
240 # endif
241 # else
242 # if defined(__NetBSD__)
243 locale_t loc = LC_GLOBAL_LOCALE;
244 # else
245 locale_t loc = uselocale ((locale_t)0);
246 # endif
247 locale_t copy = loc;
248 if (LC_GLOBAL_LOCALE == copy)
249 copy = duplocale (copy);
250
251 if ((locale_t)0 != copy)
252 {
253 ret = strerror_l (errnum, copy);
254 if (LC_GLOBAL_LOCALE == loc)
255 freelocale (copy);
256 }
257 # endif
258
259 if (! ret)
260 {
261 int n_buf = snprintf (buf, sizeof (buf), "Unknown error %d", errnum);
262 if (0 > n_buf || (size_t)n_buf >= sizeof (buf))
263 {
264 (void)fprintf (stderr, "FATAL: snprintf buffer overflow at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
265 exit (EXIT_FAILURE);
266 }
267
268 ret = buf;
269 }
270
271 errno = saved;
272 return ret;
273 }
274 #else
275 # define xstrerror_l strerror
276 #endif
277
278
279
280
281
282 static void
283 store_page (int id)
284 {
285 PageList * n = (PageList *)malloc (sizeof (* n));
286
287 if (n == NULL)
288 {
289 (void)fprintf (stderr, "ERROR: Unable to allocate array for page %d\n", GLOBAL_NUM_PAGES + 1);
290 (void)fprintf (stderr, "FATAL: Bugcheck! Aborting at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
291 abort ();
292 }
293
294 n -> next = NULL;
295
296 n -> page_id = id;
297 * GLOBAL_INSERT_PAGE = n;
298 GLOBAL_INSERT_PAGE = & n -> next;
299
300 GLOBAL_NUM_PAGES++;
301 }
302
303
304
305
306
307 static void
308 start_object (int id)
309 {
310 if (id >= GLOBAL_NUM_XREFS)
311 {
312 long * new_xrefs;
313 int delta, new_num_xrefs;
314 delta = GLOBAL_NUM_XREFS / 5;
315
316 if (delta < 1000)
317 delta += 1000;
318
319 new_num_xrefs = GLOBAL_NUM_XREFS + delta;
320 new_xrefs = (long *)malloc (new_num_xrefs * sizeof (* new_xrefs));
321
322 if (new_xrefs == NULL)
323 {
324 (void)fprintf (stderr, "ERROR: Unable to allocate array for object %d\n", id);
325 (void)fprintf (stderr, "FATAL: Bugcheck! Aborting at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
326 abort ();
327 }
328
329
330 (void)memcpy (new_xrefs, GLOBAL_XREFS, GLOBAL_NUM_XREFS * sizeof (* GLOBAL_XREFS));
331 FREE (GLOBAL_XREFS);
332 GLOBAL_XREFS = new_xrefs;
333 GLOBAL_NUM_XREFS = new_num_xrefs;
334 }
335
336 GLOBAL_XREFS [id] = ftell (stdout);
337 (void)printf ("%d 0 obj\n", id);
338 }
339
340
341
342
343
344 static void
345 print_bars (void)
346 {
347 float x1;
348 float yyy1;
349 float height;
350 float width;
351
352 if (GLOBAL_SHADE_COLOR)
353 {
354 unsigned int r_val, g_val, b_val;
355 (void)sscanf (GLOBAL_SHADE_COLOR, "%2x%2x%2x", & r_val, & g_val, & b_val);
356
357 float r = (float)r_val / 255.0f;
358 float g = (float)g_val / 255.0f;
359 float b = (float)b_val / 255.0f;
360
361 (void)fprintf (stdout, "%f %f %f rg\n", r, g, b);
362 }
363 else if (GLOBAL_GREEN_BAR)
364 {
365 if (GLOBAL_GRAY_SCALE == 0.800781F)
366 GLOBAL_GRAY_SCALE = 0.32;
367 float r, g, b;
368 r = GLOBAL_GRAY_SCALE + (1.0 - GLOBAL_GRAY_SCALE) * 0.60;
369 g = GLOBAL_GRAY_SCALE + (1.0 - GLOBAL_GRAY_SCALE) * 0.82;
370 b = GLOBAL_GRAY_SCALE + (1.0 - GLOBAL_GRAY_SCALE) * 0.60;
371 (void)fprintf (stdout, "%f %f %f rg\n", r, g, b);
372 }
373 else
374 (void)fprintf (stdout, "%f g\n", GLOBAL_GRAY_SCALE);
375
376 (void)fprintf (stdout, "%d i\n", 1);
377
378 if (GLOBAL_SHADE_EDGE_TO_EDGE)
379 {
380 x1 = 0;
381 width = GLOBAL_PAGE_WIDTH;
382 }
383 else
384 {
385 x1 = GLOBAL_PAGE_MARGIN_LEFT - 0.1 * GLOBAL_FONT_SIZE;
386 width = GLOBAL_PAGE_WIDTH - GLOBAL_PAGE_MARGIN_LEFT - GLOBAL_PAGE_MARGIN_RIGHT;
387 }
388
389 height = GLOBAL_SHADE_STEP * GLOBAL_LEAD_SIZE;
390 yyy1 = GLOBAL_PAGE_DEPTH - GLOBAL_PAGE_MARGIN_TOP - height - 0.22 * GLOBAL_FONT_SIZE;
391
392 float step = 2.0;
393 if (GLOBAL_SHADE_ALTERNATE_START)
394 yyy1 -= height;
395
396 while (yyy1 >= (GLOBAL_PAGE_MARGIN_BOTTOM - height))
397 {
398 (void)fprintf (stdout, "%f %f %f %f re f\n", x1, yyy1, width, height);
399 yyy1 = yyy1 - step * height;
400 }
401
402 (void)fprintf (stdout, "%d G\n", 0);
403 (void)fprintf (stdout, "%d g\n", 0);
404 }
405
406
407
408
409
410 static void
411 printstring (char * buffer)
412 {
413
414 char c;
415 (void)putchar ('(');
416
417 while ((c = * buffer++) != '\0')
418 {
419 switch (c + GLOBAL_ADD)
420 {
421 case '(':
422
423 case ')':
424
425 case '\\':
426 (void)putchar ('\\');
427 }
428
429 (void)putchar (c + GLOBAL_ADD);
430 }
431
432 (void)putchar (')');
433 }
434
435
436
437
438
439 static void
440 printme_top (void)
441 {
442 char * varname;
443 if ((varname = getenv ("IMPACT_TOP")) != (char *)NULL)
444 {
445 char IMPACT_TOP [256];
446 float text_size = 20.0;
447 float charwidth;
448 float xvalue;
449 float yvalue;
450 (void)strncpy (IMPACT_TOP, varname, 255);
451 charwidth = text_size * 0.60;
452 (void)fprintf (stdout, "1.0 0.0 0.0 rg\n");
453 yvalue = GLOBAL_PAGE_DEPTH - text_size;
454 xvalue = GLOBAL_PAGE_MARGIN_LEFT + ((GLOBAL_PAGE_WIDTH - GLOBAL_PAGE_MARGIN_LEFT - GLOBAL_PAGE_MARGIN_RIGHT) / 2.0)
455 - (strlen (IMPACT_TOP) * charwidth / 2.0);
456
457 (void)fprintf (stdout, "BT /F1 %f Tf %f %f Td", text_size, xvalue, yvalue);
458 printstring (IMPACT_TOP);
459 (void)fprintf (stdout, " Tj ET\n");
460
461 (void)fprintf (stdout, "0.0 0.0 0.0 rg\n");
462 }
463 }
464
465
466
467
468
469 static void
470 print_margin_label (void)
471 {
472 printme_top ();
473 }
474
475
476
477
478
479 static void
480 start_page (void)
481 {
482 static int std_warned = 0;
483 GLOBAL_STREAM_ID = GLOBAL_OBJECT_ID++;
484 GLOBAL_STREAM_LEN_ID = GLOBAL_OBJECT_ID++;
485 start_object (GLOBAL_STREAM_ID);
486 (void)printf ("<< /Length %d 0 R >>\n", GLOBAL_STREAM_LEN_ID);
487 (void)printf ("stream\n");
488 GLOBAL_STREAM_START = ftell (stdout);
489
490 for (int line = 0; line < MAX_LINES; line++)
491 {
492 for (int col = 0; col < MAX_COLS; col++)
493 {
494 page_grid [line] [col] . num_chars = 0;
495 (void)memset (page_grid [line] [col] . chars, 0, sizeof (page_grid [line] [col] . chars));
496 page_grid [line] [col] . under_char = '\0';
497 page_grid [line] [col] . under_bold = 0;
498 page_grid [line] [col] . under_from_input = 0;
499 }
500 }
501
502 print_bars ();
503 print_margin_label ();
504 (void)printf ("BT\n/F0 %g Tf\n", GLOBAL_FONT_SIZE);
505 GLOBAL_YPOS = GLOBAL_PAGE_DEPTH - GLOBAL_PAGE_MARGIN_TOP - GLOBAL_LEAD_SIZE;
506 (void)printf ("%g %g Td\n", GLOBAL_PAGE_MARGIN_LEFT, GLOBAL_YPOS);
507 (void)printf ("%g TL\n", GLOBAL_LEAD_SIZE);
508
509 if (! std_warned)
510 {
511 (void)printf ("%% NOTE: This program reads from standard input and writes to standard output!\n");
512 std_warned = 1;
513 }
514 }
515
516
517
518
519
520 static void
521 end_page (void)
522 {
523 long stream_len;
524 int page_id = GLOBAL_OBJECT_ID++;
525
526 store_page (page_id);
527 (void)printf ("ET\n");
528 stream_len = ftell (stdout) - GLOBAL_STREAM_START;
529 (void)printf ("endstream\nendobj\n");
530 start_object (GLOBAL_STREAM_LEN_ID);
531 (void)printf ("%ld\nendobj\n", (long)stream_len);
532 start_object (page_id);
533 (void)printf ("<</Type/Page/Parent %d 0 R/Contents %d 0 R>>\nendobj\n", GLOBAL_PAGE_TREE_ID, GLOBAL_STREAM_ID);
534 }
535
536
537
538
539
540 static void
541 do_text (void)
542 {
543 char buffer [8192] = { 0 };
544 int c;
545 int i = 0, ic;
546 int first_char = 1;
547
548 start_page ();
549 current_line = 0;
550
551 for (i = 0; i < GLOBAL_SHIFT; i++)
552 buffer [i] = ' ';
553
554 while ((ic = getchar ()) != EOF)
555 {
556 c = ic;
557 if (first_char)
558 {
559 first_char = 0;
560 if (c == '\f')
561 continue;
562 }
563
564 if (c == '\r' || c == '\n' || c == '\f')
565 {
566 buffer [i] = 0;
567
568 if (GLOBAL_TAB_PROCESSING)
569 {
570 char expanded_buffer [8192] = { 0 };
571 int expanded_i = 0;
572 int col = 0;
573
574 for (int j = 0; buffer [j] != '\0'; j++)
575 {
576 if (buffer [j] == '\t')
577 {
578 int next_tab_stop = -1;
579
580 if (GLOBAL_TAB_STOPS)
581 {
582 for (int k = 0; GLOBAL_TAB_STOPS [k] != 0; k++)
583 {
584 if (GLOBAL_TAB_STOPS [k] > col)
585 {
586 next_tab_stop = GLOBAL_TAB_STOPS [k];
587 break;
588 }
589 }
590
591 if (next_tab_stop == -1)
592 {
593 int last_tab_stop = 0;
594
595 for (int k = 0; GLOBAL_TAB_STOPS [k] != 0; k++)
596 last_tab_stop = GLOBAL_TAB_STOPS [k];
597
598 if (GLOBAL_TAB_INTERVAL > 0)
599 {
600 if (col >= last_tab_stop)
601 next_tab_stop =
602 last_tab_stop + ((col - last_tab_stop) / GLOBAL_TAB_INTERVAL + 1) *
603 GLOBAL_TAB_INTERVAL;
604 }
605 else if (GLOBAL_TAB_RELATIVE_INTERVAL > 0)
606 if (col >= last_tab_stop)
607 next_tab_stop =
608 last_tab_stop + ((col - last_tab_stop) / GLOBAL_TAB_RELATIVE_INTERVAL + 1) *
609 GLOBAL_TAB_RELATIVE_INTERVAL;
610 }
611 }
612 else if (GLOBAL_TAB_INTERVAL > 0)
613 next_tab_stop = (col / GLOBAL_TAB_INTERVAL + 1) * GLOBAL_TAB_INTERVAL;
614
615 if (next_tab_stop != -1)
616 {
617 int spaces = next_tab_stop - col;
618
619 for (int k = 0; k < spaces; k++)
620 {
621 if (expanded_i < 8190)
622 expanded_buffer [expanded_i++] = ' ';
623 }
624
625 col += spaces;
626 }
627 else
628 {
629 if (expanded_i < 8190)
630 {
631 expanded_buffer [expanded_i++] = ' ';
632 col++;
633 }
634 }
635 }
636 else
637 {
638 if (expanded_i < 8190)
639 {
640 expanded_buffer [expanded_i++] = buffer [j];
641 col++;
642 }
643 }
644 }
645
646 strncpy (buffer, expanded_buffer, 8192);
647 }
648
649 size_t len = strlen (buffer);
650 int col = 0;
651 for (size_t j = 0; j < len; j++)
652 {
653 if (col >= MAX_COLS)
654 continue;
655
656 char new_char = buffer [j];
657
658 if (new_char == '\b')
659 {
660 if (col > 0)
661 col--;
662 continue;
663 }
664
665 PageCell * cell = & page_grid [current_line] [col];
666
667 switch (new_char)
668 {
669 case '0':
670 {
671 char char_to_render = '0';
672 int slash = 0;
673
674 if (GLOBAL_ZERO_OVERSTRIKE)
675 slash = 1;
676
677 if (GLOBAL_O_MODE == 1)
678 char_to_render = 'O';
679 else if (GLOBAL_O_MODE == 2)
680 char_to_render = 'O';
681 else if (GLOBAL_O_MODE == 3)
682 char_to_render = '0';
683
684 if (slash)
685 {
686 if (cell -> num_chars < MAX_OVERSTRIKES - 1)
687 {
688 cell -> chars [cell -> num_chars++] = char_to_render;
689 cell -> chars [cell -> num_chars++] = '/';
690 }
691 }
692 else
693 {
694 if (cell->num_chars < MAX_OVERSTRIKES)
695 cell->chars [cell -> num_chars++] = char_to_render;
696 }
697
698 break;
699 }
700
701 case 'O':
702 {
703 char char_to_render = 'O';
704
705 if (GLOBAL_O_MODE == 1)
706 char_to_render = '0';
707 else if (GLOBAL_O_MODE == 2)
708 char_to_render = 'O';
709 else if (GLOBAL_O_MODE == 3)
710 char_to_render = '0';
711
712 if (cell -> num_chars < MAX_OVERSTRIKES)
713 cell -> chars [cell -> num_chars++] = char_to_render;
714
715 break;
716 }
717
718 case '1':
719 if (GLOBAL_ONE_UNDERLINE)
720 {
721 if (cell -> under_char == '_' && cell -> under_from_input)
722 cell -> under_bold = 1;
723 else
724 cell -> under_char = '_';
725 }
726
727 if (cell -> num_chars < MAX_OVERSTRIKES)
728 cell -> chars [cell -> num_chars++] = '1';
729
730 break;
731
732 case '7':
733 if (GLOBAL_SEVEN_OVERSTRIKE)
734 {
735 if (cell -> num_chars < MAX_OVERSTRIKES - 1)
736 {
737 cell -> chars [cell -> num_chars++] = '7';
738 cell -> chars [cell -> num_chars++] = '-';
739 }
740 }
741 else
742 if (cell -> num_chars < MAX_OVERSTRIKES)
743 cell -> chars [cell -> num_chars++] = '7';
744
745 break;
746
747 case 'Z':
748 if (GLOBAL_ZED_OVERSTRIKE)
749 {
750 if (cell -> num_chars < MAX_OVERSTRIKES - 1)
751 {
752 cell -> chars [cell -> num_chars++] = 'Z';
753 cell -> chars [cell -> num_chars++] = '-';
754 }
755 }
756 else
757 if (cell -> num_chars < MAX_OVERSTRIKES)
758 cell -> chars [cell -> num_chars++] = 'Z';
759
760 break;
761
762 case '_':
763 if (cell -> num_chars > 0)
764 {
765 int all_underscores = 1;
766
767 for (int k = 0; k < cell -> num_chars; k++)
768 if (cell -> chars [k] != '_')
769 {
770 all_underscores = 0;
771 break;
772 }
773
774 if (all_underscores)
775 {
776 if (cell -> num_chars < MAX_OVERSTRIKES)
777 cell -> chars [cell->num_chars++] = new_char;
778 }
779 else
780 if (cell -> under_char == '_')
781 cell -> under_bold = 1;
782 else
783 {
784 cell -> under_char = '_';
785 cell -> under_from_input = 1;
786 }
787 }
788 else
789 if (cell -> num_chars < MAX_OVERSTRIKES)
790 cell -> chars [cell -> num_chars++] = new_char;
791
792 break;
793
794 default:
795 if (cell -> num_chars < MAX_OVERSTRIKES)
796 cell -> chars [cell -> num_chars++] = new_char;
797
798 break;
799 }
800 col++;
801 }
802
803 if (c == '\n' || c == '\f')
804 {
805 (void)printf ("ET\nBT\n");
806 float char_width = GLOBAL_FONT_SIZE * 0.6;
807
808 for (int j = 0; j < MAX_COLS; j++)
809 {
810 PageCell * cell = & page_grid [current_line] [j];
811 if (cell -> num_chars == 0 && ! cell -> under_char)
812 continue;
813
814 int freq [256] = { 0 };
815 for (int k = 0; k < cell -> num_chars; k++)
816 freq [(int)cell -> chars [k]]++;
817
818 for (int k = 0; k < cell -> num_chars; k++)
819 {
820 char current_char = cell -> chars [k];
821 if (current_char == ' ')
822 continue;
823
824 int first_occurrence = -1;
825 for (int l = 0; l < cell -> num_chars; l++)
826 {
827 if (cell -> chars [l] == current_char)
828 {
829 first_occurrence = l;
830 break;
831 }
832 }
833
834 if (k != first_occurrence)
835 continue;
836
837 int is_bold = (freq [(int)current_char] > 1);
838
839 if (current_char == '_')
840 {
841 (void)printf ("ET\n");
842 float line_width = is_bold ? 1.5 : 0.5;
843 (void)printf ("%f w\n", line_width);
844 float y_underline = GLOBAL_YPOS - 1;
845 float x_start = GLOBAL_PAGE_MARGIN_LEFT + (j * char_width);
846 (void)printf ("%f %f m %f %f l S\n", x_start, y_underline, x_start + char_width, y_underline);
847 (void)printf ("BT\n");
848 }
849 else
850 {
851 (void)printf ("/F%d %g Tf\n", is_bold ? 1 : 0, GLOBAL_FONT_SIZE);
852 (void)printf ("1 0 0 1 %f %f Tm\n", GLOBAL_PAGE_MARGIN_LEFT + (j * char_width), GLOBAL_YPOS);
853 char c_str [2] = { current_char, '\0' };
854 printstring (c_str);
855 (void)printf (" Tj\n");
856 }
857 }
858
859 if (cell -> under_char)
860 {
861 (void)printf ("ET\n");
862 float line_width = cell -> under_bold ? 1.5 : 0.5;
863 (void)printf ("%f w\n", line_width);
864 float y_underline = GLOBAL_YPOS - 1;
865 float x_start = GLOBAL_PAGE_MARGIN_LEFT + (j * char_width);
866 (void)printf ("%f %f m %f %f l S\n", x_start, y_underline, x_start + char_width, y_underline);
867 (void)printf ("BT\n");
868 }
869 }
870
871 current_line++;
872 GLOBAL_YPOS -= GLOBAL_LEAD_SIZE;
873 }
874
875 if (c == '\f')
876 {
877 end_page ();
878 start_page ();
879 current_line = 0;
880 }
881 else if (c == '\n')
882 {
883 if (current_line > GLOBAL_LINES_PER_PAGE)
884 {
885 end_page ();
886 start_page ();
887 current_line = 0;
888 }
889 }
890
891 i = GLOBAL_SHIFT;
892 for (int k = 0; k < GLOBAL_SHIFT; k++)
893 buffer [k] = ' ';
894 }
895 else
896 {
897 if (i < 8190)
898 buffer [i++] = (char)c;
899 }
900 }
901
902 end_page ();
903 }
904
905
906
907
908
909 static uint16_t
910 read_u16_be (FILE * fp)
911 {
912 uint8_t b [2];
913 size_t ret = fread (b, 1, 2, fp);
914 if (ret != 2)
915 {
916 if (feof (fp))
917 (void)fprintf (stderr, "ERROR: Unexpected EOF while reading from file\n");
918 else if (ferror (fp))
919 (void)fprintf (stderr, "ERROR: Unable to read from file: %s (Error %d)\n", xstrerror_l (errno), errno);
920 else
921 (void)fprintf (stderr, "ERROR: Unexpected short read from file\n");
922
923 exit (EXIT_FAILURE);
924 }
925
926 return (uint16_t)((b [0] << 8) | b [1]);
927 }
928
929
930
931
932
933 static uint32_t
934 read_u32_be (FILE * fp)
935 {
936 uint8_t b [4];
937 size_t ret = fread (b, 1, 4, fp);
938 if (ret != 4)
939 {
940 if (feof (fp))
941 (void)fprintf (stderr, "ERROR: Unexpected EOF while reading from file\n");
942 else if (ferror (fp))
943 (void)fprintf (stderr, "ERROR: Unable to read from file: %s (Error %d)\n", xstrerror_l (errno), errno);
944 else
945 (void)fprintf (stderr, "ERROR: Unexpected short read from file\n");
946
947 exit (EXIT_FAILURE);
948 }
949
950 return ((uint32_t)b [0] << 24) | ((uint32_t)b [1] << 16) | ((uint32_t)b [2] << 8) | (uint32_t)b [3];
951 }
952
953
954
955
956
957 static uint16_t
958 read_u16_be_from_mem (const unsigned char * * p, const unsigned char * end)
959 {
960 if (* p + 2 > end)
961 {
962 (void)fprintf (stderr, "ERROR: Unexpected end of buffer\n");
963 exit (EXIT_FAILURE);
964 }
965 uint16_t val = (uint16_t)(((* p) [0] << 8) | (* p) [1]);
966 * p += 2;
967 return val;
968 }
969
970
971
972
973
974 static uint32_t
975 read_u32_be_from_mem (const unsigned char * * p, const unsigned char * end)
976 {
977 if (* p + 4 > end)
978 {
979 (void)fprintf (stderr, "ERROR: Unexpected end of buffer\n");
980 exit (EXIT_FAILURE);
981 }
982 uint32_t val = ((uint32_t)(* p) [0] << 24) | ((uint32_t)(* p) [1] << 16) | ((uint32_t)(* p) [2] << 8) | (uint32_t)(* p) [3];
983 * p += 4;
984 return val;
985 }
986
987
988
989
990
991 static size_t
992 emit_utf8_from_codepoint (uint32_t cp, char * out)
993 {
994 if (cp <= 0x7F)
995 {
996 if (out)
997 out [0] = (char)cp;
998 return 1;
999 }
1000 else if (cp <= 0x7FF)
1001 {
1002 if (out)
1003 {
1004 out [0] = (char)(0xC0 | (cp >> 6));
1005 out [1] = (char)(0x80 | (cp & 0x3F));
1006 }
1007 return 2;
1008 }
1009 else if (cp <= 0xFFFF)
1010 {
1011 if (out)
1012 {
1013 out [0] = (char)(0xE0 | (cp >> 12));
1014 out [1] = (char)(0x80 | ((cp >> 6) & 0x3F));
1015 out [2] = (char)(0x80 | (cp & 0x3F));
1016 }
1017 return 3;
1018 }
1019 else if (cp <= 0x10FFFF)
1020 {
1021 if (out)
1022 {
1023 out [0] = (char)(0xF0 | (cp >> 18));
1024 out [1] = (char)(0x80 | ((cp >> 12) & 0x3F));
1025 out [2] = (char)(0x80 | ((cp >> 6) & 0x3F));
1026 out [3] = (char)(0x80 | (cp & 0x3F));
1027 }
1028 return 4;
1029 }
1030
1031 return 0;
1032 }
1033
1034
1035
1036
1037
1038 static char *
1039 utf16be_to_utf8 (const uint8_t * buf, size_t len)
1040 {
1041 if (! buf || len == 0 || (len & 1))
1042 return NULL;
1043
1044
1045 size_t cap = len * 2 + 1;
1046
1047 char * out = (char *)malloc (cap);
1048 if (! out)
1049 {
1050 (void)fprintf (stderr, "FATAL: Out of memory! Aborting at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
1051 abort ();
1052 }
1053
1054 size_t o = 0;
1055 for (size_t i = 0; i + 1 < len;)
1056 {
1057 uint16_t u = ((uint16_t)buf [i] << 8) | buf [i + 1];
1058 i += 2;
1059 uint32_t cp;
1060 if (u >= 0xD800 && u <= 0xDBFF)
1061 {
1062 if (i + 1 >= len)
1063 {
1064 FREE (out);
1065 return NULL;
1066 }
1067 uint16_t u2 = ((uint16_t)buf [i] << 8) | buf [i + 1];
1068 if (u2 < 0xDC00 || u2 > 0xDFFF)
1069 {
1070 FREE (out);
1071 return NULL;
1072 }
1073 i += 2;
1074 cp = 0x10000 + (((uint32_t)u - 0xD800) << 10) + ((uint32_t)u2 - 0xDC00);
1075 }
1076 else if (u >= 0xDC00 && u <= 0xDFFF)
1077 {
1078 FREE (out);
1079 return NULL;
1080 }
1081 else
1082 cp = u;
1083 size_t w = emit_utf8_from_codepoint (cp, out + o);
1084 if (w == 0)
1085 {
1086 FREE (out);
1087 return NULL;
1088 }
1089 o += w;
1090 }
1091 out [o] = '\0';
1092
1093 return out;
1094 }
1095
1096
1097
1098
1099
1100 static int
1101 next_utf8_cp (const char * s, size_t len, size_t * idx, uint32_t * cp)
1102 {
1103 if (* idx >= len)
1104 return 0;
1105
1106 uint8_t b0 = (uint8_t)s [* idx];
1107 if (b0 < 0x80)
1108 {
1109 * cp = b0;
1110 (* idx)++;
1111 return 1;
1112 }
1113 else if ((b0 & 0xE0) == 0xC0 && * idx + 1 < len)
1114 {
1115 uint8_t b1 = (uint8_t)s [* idx + 1];
1116 if ((b1 & 0xC0) != 0x80)
1117 return -1;
1118 uint32_t c = ((b0 & 0x1F) << 6) | (b1 & 0x3F);
1119 if (c < 0x80)
1120 return -1;
1121 * cp = c;
1122 * idx += 2;
1123 return 1;
1124 }
1125 else if ((b0 & 0xF0) == 0xE0 && * idx + 2 < len)
1126 {
1127 uint8_t b1 = (uint8_t)s [* idx + 1], b2 = (uint8_t)s [* idx + 2];
1128 if ((b1 & 0xC0) != 0x80 || (b2 & 0xC0) != 0x80)
1129 return -1;
1130 uint32_t c = ((b0 & 0x0F) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F);
1131 if (c < 0x800)
1132 return -1;
1133 * cp = c;
1134 * idx += 3;
1135 return 1;
1136 }
1137 else if ((b0 & 0xF8) == 0xF0 && * idx + 3 < len)
1138 {
1139 uint8_t b1 = (uint8_t)s [* idx + 1], b2 = (uint8_t)s [* idx + 2], b3 = (uint8_t)s [* idx + 3];
1140 if ((b1 & 0xC0) != 0x80 || (b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80)
1141 return -1;
1142 uint32_t c = ((b0 & 0x07) << 18) | ((b1 & 0x3F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F);
1143 if (c < 0x10000 || c > 0x10FFFF)
1144 return -1;
1145 * cp = c;
1146 * idx += 4;
1147 return 1;
1148 }
1149
1150 return -1;
1151 }
1152
1153
1154
1155
1156
1157 static int
1158 is_win1252_defined_codepoint (uint32_t cp)
1159 {
1160
1161
1162 if (cp <= 0x1F)
1163 return 0;
1164
1165 if (cp <= 0x7E)
1166 return 1;
1167
1168 if (cp == 0x81 || cp == 0x8D || cp == 0x8F || cp == 0x90 || cp == 0x9D)
1169 return 0;
1170
1171 if (cp >= 0x80 && cp <= 0xFF)
1172 return 1;
1173
1174 return 0;
1175 }
1176
1177
1178
1179
1180
1181 static int
1182 is_win1252_compatible_utf8 (const char * s)
1183 {
1184 size_t len = strlen (s);
1185 size_t i = 0;
1186 uint32_t cp;
1187
1188 while (i < len)
1189 {
1190 int r = next_utf8_cp (s, len, & i, & cp);
1191 if (r <= 0)
1192 return 0;
1193
1194 if (! is_win1252_defined_codepoint (cp))
1195 return 0;
1196 }
1197
1198 return 1;
1199 }
1200
1201
1202
1203
1204
1205 static const uint16_t
1206 mac_roman_to_unicode [128] =
1207 {
1208 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1,
1209 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8,
1210 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3,
1211 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC,
1212 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF,
1213 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8,
1214 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211,
1215 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8,
1216 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB,
1217 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153,
1218 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA,
1219 0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02,
1220 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1,
1221 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4,
1222 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC,
1223 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7
1224 };
1225
1226
1227
1228
1229
1230 static char *
1231 mac_roman_to_utf8 (const uint8_t * buf, size_t len)
1232 {
1233 size_t cap = len * 3 + 1;
1234
1235 char * out = (char *)malloc (cap);
1236 if (! out)
1237 {
1238 (void)fprintf (stderr, "FATAL: Out of memory! Aborting at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
1239 abort ();
1240 }
1241
1242 size_t o = 0;
1243 for (size_t i = 0; i < len; i++)
1244 {
1245 uint8_t b = buf [i];
1246 uint32_t cp;
1247
1248 if (0x80 > b)
1249 cp = b;
1250 else
1251 {
1252 size_t idx = (size_t) (b - 0x80);
1253 if (idx >= sizeof (mac_roman_to_unicode) / sizeof (mac_roman_to_unicode [0]))
1254 {
1255 FREE (out);
1256 return NULL;
1257 }
1258 cp = mac_roman_to_unicode [idx];
1259 }
1260
1261 o += emit_utf8_from_codepoint (cp, out + o);
1262 }
1263
1264 out [o] = '\0';
1265
1266 return out;
1267 }
1268
1269
1270
1271
1272
1273 static char *
1274 decode_name_to_utf8 (uint16_t platformID, uint16_t encodingID, const uint8_t * buf, size_t len)
1275 {
1276 if (platformID == 3)
1277 {
1278 return utf16be_to_utf8 (buf, len);
1279 }
1280 else if (platformID == 0)
1281 return utf16be_to_utf8 (buf, len);
1282 else if (platformID == 1 && encodingID == 0)
1283 return mac_roman_to_utf8 (buf, len);
1284 else
1285 {
1286 char * s = (char *)malloc (len + 1);
1287 if (! s)
1288 {
1289 (void)fprintf (stderr, "FATAL: Out of memory! Aborting at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
1290 abort ();
1291 }
1292
1293 (void)memcpy (s, buf, len);
1294 s [len] = '\0';
1295 return s;
1296 }
1297 }
1298
1299
1300
1301
1302
1303 static int
1304 score_name_record (uint16_t nameID, uint16_t platformID, uint16_t encodingID, uint16_t languageID, int win1252_ok)
1305 {
1306 int score = 0;
1307
1308 if (nameID == 6)
1309 score += 1000;
1310 else if (nameID == 4)
1311 score += 500;
1312 else
1313 return -100000;
1314
1315 if (platformID == 3)
1316 {
1317 score += 200;
1318 if (encodingID == 1 || encodingID == 10)
1319 score += 50;
1320 if (languageID == 0x0409)
1321 score += 100;
1322 }
1323 else if (platformID == 1 && encodingID == 0)
1324 {
1325 score += 150;
1326 if (languageID == 0)
1327 score += 25;
1328 }
1329 else if (platformID == 0)
1330 score += 120;
1331
1332 if (win1252_ok)
1333 score += 75;
1334
1335 return score;
1336 }
1337
1338
1339
1340
1341
1342 static char *
1343 read_cff_postscript_name (FILE * fp, uint32_t cff_offset, uint32_t cff_length)
1344 {
1345 if (cff_offset == 0 || cff_length < 4)
1346 return NULL;
1347
1348 long save = ftell (fp);
1349
1350 if (save < 0)
1351 return NULL;
1352
1353 if (fseek (fp, (long)cff_offset, SEEK_SET) != 0)
1354 return NULL;
1355
1356 uint8_t hdr [4];
1357 if (fread (hdr, 1, 4, fp) != 4)
1358 {
1359 (void)fseek (fp, save, SEEK_SET);
1360 return NULL;
1361 }
1362
1363 uint8_t hdrSize = hdr [2];
1364
1365 if (fseek (fp, (long)cff_offset + hdrSize, SEEK_SET) != 0)
1366 {
1367 (void)fseek (fp, save, SEEK_SET);
1368 return NULL;
1369 }
1370
1371
1372 uint16_t count = read_u16_be (fp);
1373 if (count == 0 || count > 8192)
1374 {
1375 (void)fseek (fp, save, SEEK_SET);
1376 return NULL;
1377 }
1378
1379 int offSize = fgetc (fp);
1380 if (offSize < 1 || offSize > 4)
1381 {
1382 (void)fseek (fp, save, SEEK_SET);
1383 return NULL;
1384 }
1385
1386
1387 uint32_t off1 = 0, off2 = 0;
1388 for (int i = 0; i < count + 1; i++)
1389 {
1390 uint32_t off = 0;
1391 for (int j = 0; j < offSize; j++)
1392 {
1393 int ch = fgetc (fp);
1394 if (ch == EOF)
1395 {
1396 (void)fseek (fp, save, SEEK_SET);
1397 return NULL;
1398 }
1399
1400 off = (off << 8) | (uint32_t)(uint8_t)ch;
1401 }
1402
1403 if (i == 0)
1404 off1 = off;
1405
1406 if (i == 1)
1407 off2 = off;
1408 }
1409
1410 long data_start = ftell (fp);
1411
1412 if (off1 == 0 || off2 < off1)
1413 {
1414 (void)fseek (fp, save, SEEK_SET);
1415 return NULL;
1416 }
1417
1418 uint32_t len = off2 - off1;
1419 if (len == 0 || len > 8192)
1420 {
1421 (void)fseek (fp, save, SEEK_SET);
1422 return NULL;
1423 }
1424
1425 if (fseek (fp, data_start + (long)off1 - 1L, SEEK_SET) != 0)
1426 {
1427 (void)fseek (fp, save, SEEK_SET);
1428 return NULL;
1429 }
1430
1431 char * s = (char *)malloc (len + 1);
1432 if (! s)
1433 {
1434 (void)fprintf (stderr, "FATAL: Out of memory! Aborting at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
1435 abort ();
1436 }
1437
1438 if (fread (s, 1, len, fp) != len)
1439 {
1440 FREE (s);
1441 (void)fseek (fp, save, SEEK_SET);
1442 return NULL;
1443 }
1444 s [len] = '\0';
1445
1446 (void)fseek (fp, save, SEEK_SET);
1447 return s;
1448 }
1449
1450
1451
1452
1453
1454 static char *
1455 read_cff_postscript_name_from_mem (const unsigned char * data, size_t data_len, uint32_t cff_offset, uint32_t cff_length)
1456 {
1457 if (cff_offset == 0 || cff_length < 4 || cff_offset + cff_length > data_len)
1458 return NULL;
1459
1460 const unsigned char * p = data + cff_offset;
1461 const unsigned char * end = p + cff_length;
1462
1463 if (p + 4 > end)
1464 return NULL;
1465
1466 uint8_t hdrSize = p [2];
1467 p += hdrSize;
1468
1469 if (p + 3 > end)
1470 return NULL;
1471
1472 uint16_t count = read_u16_be_from_mem (& p, end);
1473
1474 if (count == 0 || count > 8192)
1475 return NULL;
1476
1477 uint8_t offSize = * p++;
1478
1479 if (offSize < 1 || offSize > 4)
1480 return NULL;
1481
1482 const unsigned char * offsets_end = p + (count + 1) * offSize;
1483
1484 if (offsets_end > end)
1485 return NULL;
1486
1487 uint32_t off1 = 0, off2 = 0;
1488 for (int i = 0; i < count + 1; i++)
1489 {
1490 uint32_t off = 0;
1491 for (int j = 0; j < offSize; j++)
1492 {
1493 if (p >= end)
1494 return NULL;
1495 off = (off << 8) | * p++;
1496 }
1497
1498 if (i == 0)
1499 off1 = off;
1500
1501 if (i == 1)
1502 off2 = off;
1503 }
1504
1505 const unsigned char * data_start = p;
1506
1507 if (off1 == 0 || off2 < off1)
1508 return NULL;
1509
1510 uint32_t len = off2 - off1;
1511
1512 if (len == 0 || len > 8192)
1513 return NULL;
1514
1515 const unsigned char * name_ptr = data_start + off1 - 1;
1516
1517 if (name_ptr + len > end)
1518 return NULL;
1519
1520 char * s = (char *)malloc (len + 1);
1521 if (! s)
1522 {
1523 (void)fprintf (stderr, "FATAL: Out of memory! Aborting at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
1524 abort ();
1525 }
1526 (void)memcpy (s, name_ptr, len);
1527 s [len] = '\0';
1528
1529 return s;
1530 }
1531
1532
1533
1534
1535
1536 static char *
1537 read_font_name (const char * font_path)
1538 {
1539 FILE * fp = fopen (font_path, "rb");
1540 if (! fp)
1541 return NULL;
1542
1543
1544 if (fseek (fp, 4, SEEK_SET) != 0)
1545 {
1546 (void)fclose (fp);
1547 return NULL;
1548 }
1549
1550 uint16_t numTables = read_u16_be (fp);
1551 if (numTables > 4096)
1552 {
1553 (void)fprintf (stderr, "ERROR: Font has too many tables (%u > 4096)\n", numTables);
1554 (void)fclose (fp);
1555 return NULL;
1556 }
1557
1558 if (fseek (fp, 6, SEEK_CUR) != 0)
1559 {
1560 (void)fclose (fp);
1561 return NULL;
1562 }
1563
1564 uint32_t name_offset = 0, name_length = 0;
1565 uint32_t cff_offset = 0, cff_length = 0;
1566
1567 for (uint16_t i = 0; i < numTables; i++)
1568 {
1569 char tag [5] = { 0 };
1570 if (fread (tag, 1, 4, fp) != 4)
1571 {
1572 if (feof (fp))
1573 (void)fprintf (stderr, "ERROR: Unexpected EOF while reading font table\n");
1574 else
1575 (void)fprintf (stderr, "ERROR: Unable to read font table: %s (Error %d)\n", xstrerror_l (errno), errno);
1576
1577 (void)fclose (fp);
1578 exit (EXIT_FAILURE);
1579 }
1580
1581 (void)read_u32_be (fp);
1582 uint32_t offset = read_u32_be (fp);
1583 uint32_t length = read_u32_be (fp);
1584
1585 if (strncmp (tag, "name", 4) == 0)
1586 {
1587 name_offset = offset;
1588 name_length = length;
1589 }
1590 else if (strncmp (tag, "CFF ", 4) == 0)
1591 {
1592 cff_offset = offset;
1593 cff_length = length;
1594 }
1595 }
1596
1597 char * best = NULL;
1598 int best_score = -100000;
1599
1600 if (name_offset && name_length)
1601 {
1602 if (fseek (fp, (long)name_offset, SEEK_SET) != 0)
1603 {
1604 (void)fclose (fp);
1605 return NULL;
1606 }
1607
1608 uint16_t format = read_u16_be (fp);
1609
1610 uint16_t count = read_u16_be (fp);
1611 if (count > 4096)
1612 {
1613 (void)fprintf (stderr, "ERROR: Font has too many name records (%u > 4096)\n", count);
1614 (void)fclose (fp);
1615 return NULL;
1616 }
1617
1618 uint16_t stringOffset = read_u16_be (fp);
1619 (void)format;
1620 long storage_base = (long)name_offset + stringOffset;
1621
1622 for (uint16_t i = 0; i < count; i++)
1623 {
1624 uint16_t platformID = read_u16_be (fp);
1625 uint16_t encodingID = read_u16_be (fp);
1626 uint16_t languageID = read_u16_be (fp);
1627 uint16_t nameID = read_u16_be (fp);
1628 uint16_t length = read_u16_be (fp);
1629 if (length > 16384)
1630 {
1631 (void)fprintf (stderr, "ERROR: Font name is too long (%u > 16384)\n", length);
1632 (void)fclose (fp);
1633
1634 if (best)
1635 FREE (best);
1636
1637 return NULL;
1638 }
1639
1640 uint16_t offset = read_u16_be (fp);
1641
1642 if (! (nameID == 6 || nameID == 4))
1643 continue;
1644
1645 long pos = ftell (fp);
1646
1647 if (pos < 0)
1648 {
1649 (void)fclose (fp);
1650
1651 if (best)
1652 FREE (best);
1653
1654 return NULL;
1655 }
1656
1657 if (fseek (fp, storage_base + offset, SEEK_SET) != 0)
1658 {
1659 (void)fclose (fp);
1660
1661 if (best)
1662 FREE (best);
1663
1664 return NULL;
1665 }
1666
1667 uint8_t * raw = (uint8_t *)malloc (length ? (size_t)length : 1);
1668 if (! raw)
1669 {
1670 (void)fprintf (stderr, "FATAL: Out of memory! Aborting at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
1671 abort ();
1672 }
1673
1674 if (length > 0 && fread (raw, 1, length, fp) != length)
1675 {
1676 FREE (raw);
1677 (void)fclose (fp);
1678
1679 if (best)
1680 FREE (best);
1681
1682 return NULL;
1683 }
1684
1685
1686 char * utf8 = decode_name_to_utf8 (platformID, encodingID, raw, length);
1687 FREE (raw);
1688
1689 if (utf8)
1690 {
1691 int win_ok = is_win1252_compatible_utf8 (utf8);
1692 int score = score_name_record (nameID, platformID, encodingID, languageID, win_ok);
1693 if (score > best_score)
1694 {
1695 if (best)
1696 FREE (best);
1697
1698 best = utf8;
1699 best_score = score;
1700 }
1701 else
1702 FREE (utf8);
1703 }
1704
1705 if (fseek (fp, pos, SEEK_SET) != 0)
1706 {
1707 (void)fclose (fp);
1708
1709 if (best)
1710 FREE (best);
1711
1712 return NULL;
1713 }
1714 }
1715
1716 if (best)
1717 {
1718 (void)fclose (fp);
1719 return best;
1720 }
1721 }
1722
1723
1724 if (cff_offset && cff_length)
1725 {
1726 char * ps = read_cff_postscript_name (fp, cff_offset, cff_length);
1727 (void)fclose (fp);
1728 return ps;
1729 }
1730
1731 (void)fclose (fp);
1732 return NULL;
1733 }
1734
1735
1736
1737
1738
1739 static char *
1740 read_font_name_from_mem (const unsigned char * font_data, size_t font_size)
1741 {
1742 if (font_size < 12)
1743 return NULL;
1744
1745 const unsigned char * p = font_data;
1746 const unsigned char * end = font_data + font_size;
1747
1748 p += 4;
1749 uint16_t numTables = read_u16_be_from_mem (& p, end);
1750
1751 if (numTables > 4096)
1752 {
1753 (void)fprintf (stderr, "ERROR: Font has too many tables (%u > 4096)\n", numTables);
1754 return NULL;
1755 }
1756
1757 p += 6;
1758
1759 uint32_t name_offset = 0, name_length = 0;
1760 uint32_t cff_offset = 0, cff_length = 0;
1761
1762 for (uint16_t i = 0; i < numTables; i++)
1763 {
1764 if (p + 16 > end)
1765 return NULL;
1766
1767 char tag [5] = { 0 };
1768 (void)memcpy (tag, p, 4);
1769 p += 4;
1770 p += 4;
1771 uint32_t offset = read_u32_be_from_mem (& p, end);
1772 uint32_t length = read_u32_be_from_mem (& p, end);
1773
1774 if (strncmp (tag, "name", 4) == 0)
1775 {
1776 name_offset = offset;
1777 name_length = length;
1778 }
1779 else if (strncmp (tag, "CFF ", 4) == 0)
1780 {
1781 cff_offset = offset;
1782 cff_length = length;
1783 }
1784 }
1785
1786 char * best = NULL;
1787 int best_score = -100000;
1788
1789 if (name_offset && name_length && name_offset + name_length <= font_size)
1790 {
1791 const unsigned char * name_table = font_data + name_offset;
1792 const unsigned char * p_name = name_table;
1793 const unsigned char * end_name = name_table + name_length;
1794
1795 if (p_name + 6 > end_name)
1796 return NULL;
1797
1798 (void)read_u16_be_from_mem (& p_name, end_name);
1799 uint16_t count = read_u16_be_from_mem (& p_name, end_name);
1800
1801 if (count > 4096)
1802 {
1803 (void)fprintf (stderr, "ERROR: Font has too many name records (%u > 4096)\n", count);
1804 return NULL;
1805 }
1806
1807 uint16_t stringOffset = read_u16_be_from_mem (& p_name, end_name);
1808 const unsigned char * storage_base = name_table + stringOffset;
1809
1810 for (uint16_t i = 0; i < count; i++)
1811 {
1812 if (p_name + 12 > end_name)
1813 break;
1814
1815 uint16_t platformID = read_u16_be_from_mem (& p_name, end_name);
1816 uint16_t encodingID = read_u16_be_from_mem (& p_name, end_name);
1817 uint16_t languageID = read_u16_be_from_mem (& p_name, end_name);
1818 uint16_t nameID = read_u16_be_from_mem (& p_name, end_name);
1819 uint16_t length = read_u16_be_from_mem (& p_name, end_name);
1820 if (length > 16384)
1821 {
1822 (void)fprintf (stderr, "ERROR: Font name is too long (%u > 16384)\n", length);
1823
1824 if (best)
1825 FREE (best);
1826
1827 return NULL;
1828 }
1829 uint16_t offset = read_u16_be_from_mem (& p_name, end_name);
1830
1831 if (! (nameID == 6 || nameID == 4))
1832 continue;
1833
1834 if (storage_base + offset + length > end_name)
1835 continue;
1836
1837 const uint8_t * raw = storage_base + offset;
1838 char * utf8 = decode_name_to_utf8 (platformID, encodingID, raw, length);
1839
1840 if (utf8)
1841 {
1842 int win_ok = is_win1252_compatible_utf8 (utf8);
1843 int score = score_name_record (nameID, platformID, encodingID, languageID, win_ok);
1844 if (score > best_score)
1845 {
1846 if (best)
1847 FREE (best);
1848
1849 best = utf8;
1850 best_score = score;
1851 }
1852 else
1853 FREE (utf8);
1854 }
1855 }
1856
1857 if (best)
1858 return best;
1859 }
1860
1861 if (cff_offset && cff_length)
1862 return read_cff_postscript_name_from_mem (font_data, font_size, cff_offset, cff_length);
1863
1864 return NULL;
1865 }
1866
1867
1868
1869
1870
1871 static char * GLOBAL_FONT_REGULAR_PATH = NULL;
1872 static char * GLOBAL_FONT_BOLD_PATH = NULL;
1873
1874 static void
1875 embed_font (const char * font_path, const char * base_font)
1876 {
1877 int fd = open (font_path, O_RDONLY);
1878 if (fd == -1)
1879 {
1880 (void)fprintf (stderr, "ERROR: Failed to open font '%s': %s (Error %d)\n", font_path, xstrerror_l (errno), errno);
1881 exit (EXIT_FAILURE);
1882 }
1883
1884 struct stat st;
1885 if (fstat (fd, & st) == -1)
1886 {
1887 (void)fprintf (stderr, "ERROR: Failed to fstat font '%s': %s (Error %d)\n", font_path, xstrerror_l (errno), errno);
1888 (void)close (fd);
1889 exit (EXIT_FAILURE);
1890 }
1891
1892 char * allocated_name = read_font_name (font_path);
1893 const char * internal_name = allocated_name;
1894 if (NULL == internal_name)
1895 {
1896 internal_name = base_font;
1897 (void)fprintf (stderr, "WARNING: Could not read %s font name from '%s'; using fallback '%s'\n",
1898 base_font, font_path, internal_name);
1899 }
1900
1901 long font_size = st . st_size;
1902 unsigned char * font_data = malloc (font_size);
1903 if (! font_data)
1904 {
1905 (void)fprintf (stderr, "ERROR: Failed to allocate memory for font data\n");
1906 (void)fprintf (stderr, "FATAL: Bugcheck! Aborting at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
1907 (void)close (fd);
1908 abort ();
1909 }
1910
1911 if (read (fd, font_data, font_size) != font_size)
1912 {
1913 (void)fprintf (stderr, "ERROR: Failed to read font '%s': %s (Error %d)\n", font_path, xstrerror_l (errno), errno);
1914 (void)close (fd);
1915 FREE (font_data);
1916 exit (EXIT_FAILURE);
1917 }
1918
1919 (void)close (fd);
1920
1921 int font_descriptor_id = GLOBAL_OBJECT_ID++;
1922 int font_file_id = GLOBAL_OBJECT_ID++;
1923
1924 FILE * font_fp = fopen (font_path, "rb");
1925 if (! font_fp)
1926 {
1927 (void)fprintf (stderr, "ERROR: Failed to re-open font '%s': %s (Error %d)\n", font_path, xstrerror_l (errno), errno);
1928 exit (EXIT_FAILURE);
1929 }
1930
1931 uint32_t head_offset = 0, hhea_offset = 0, hmtx_offset = 0, maxp_offset = 0;
1932 uint16_t num_h_metrics = 0;
1933
1934 (void)fseek (font_fp, 4, SEEK_SET);
1935
1936 uint16_t num_tables = read_u16_be (font_fp);
1937 if (num_tables > 256)
1938 {
1939 (void)fprintf (stderr, "ERROR: Font has too many tables (%u > 256)\n", num_tables);
1940 (void)fclose (font_fp);
1941 exit (EXIT_FAILURE);
1942 }
1943
1944 (void)fseek (font_fp, 12, SEEK_SET);
1945
1946 for (int i = 0; i < num_tables; i++)
1947 {
1948 char tag [5] = { 0 };
1949 if (fread (tag, 1, 4, font_fp) != 4)
1950 {
1951 if (feof (font_fp))
1952 (void)fprintf (stderr, "ERROR: Unexpected EOF while parsing font tables\n");
1953 else
1954 (void)fprintf (stderr, "ERROR: Error parsing font tables: %s (Error %d)\n", xstrerror_l (errno), errno);
1955 (void)fclose (font_fp);
1956 exit (EXIT_FAILURE);
1957 }
1958
1959 (void)read_u32_be (font_fp);
1960 uint32_t offset = read_u32_be (font_fp);
1961 (void)read_u32_be (font_fp);
1962
1963 if (strcmp (tag, "head") == 0)
1964 head_offset = offset;
1965
1966 if (strcmp (tag, "hhea") == 0)
1967 hhea_offset = offset;
1968
1969 if (strcmp (tag, "hmtx") == 0)
1970 hmtx_offset = offset;
1971
1972 if (strcmp (tag, "maxp") == 0)
1973 maxp_offset = offset;
1974 }
1975
1976 int16_t x_min = 0, y_min = 0, x_max = 0, y_max = 0;
1977 if (head_offset)
1978 {
1979 (void)fseek (font_fp, head_offset + 36, SEEK_SET);
1980 x_min = (int16_t)read_u16_be (font_fp);
1981 y_min = (int16_t)read_u16_be (font_fp);
1982 x_max = (int16_t)read_u16_be (font_fp);
1983 y_max = (int16_t)read_u16_be (font_fp);
1984 }
1985
1986 if (maxp_offset)
1987 (void)fseek (font_fp, maxp_offset + 4, SEEK_SET);
1988
1989 if (hhea_offset)
1990 {
1991 (void)fseek (font_fp, hhea_offset + 34, SEEK_SET);
1992 num_h_metrics = read_u16_be (font_fp);
1993 }
1994
1995 (void)printf ("<</Type/Font/Subtype/TrueType/BaseFont/%s/Encoding/WinAnsiEncoding"
1996 "/FirstChar 32/LastChar 255/FontDescriptor %d 0 R/Widths [", internal_name, font_descriptor_id);
1997
1998 if (hmtx_offset && num_h_metrics > 0)
1999 {
2000 (void)fseek (font_fp, hmtx_offset, SEEK_SET);
2001 for (int i = 0; i < 224; i++)
2002 {
2003 uint16_t advance_width;
2004 if (i < num_h_metrics)
2005 {
2006 advance_width = read_u16_be (font_fp);
2007 (void)read_u16_be (font_fp);
2008 }
2009 else
2010 {
2011
2012 (void)fseek (font_fp, -2, SEEK_CUR);
2013 advance_width = read_u16_be (font_fp);
2014 (void)fseek (font_fp, 0, SEEK_CUR);
2015 }
2016
2017 (void)printf ("%d ", (int)(advance_width * 1000.0f / 2048.0f));
2018 }
2019 }
2020 (void)printf ("]>>\nendobj\n");
2021
2022 start_object (font_descriptor_id);
2023 (void)printf ("<</Type/FontDescriptor/FontName/%s/Flags 32/FontBBox [%d %d %d %d]/Ascent %d/Descent %d"
2024 "/CapHeight %d/ItalicAngle 0/FontFile2 %d 0 R>>\nendobj\n", internal_name,
2025 (int)(x_min * 1000.0f / 2048.0f), (int)(y_min * 1000.0f / 2048.0f), (int)(x_max * 1000.0f / 2048.0f),
2026 (int)(y_max * 1000.0f / 2048.0f), (int)(y_max * 1000.0f / 2048.0f), (int)(y_min * 1000.0f / 2048.0f),
2027 (int)(y_max * 1000.0f / 2048.0f), font_file_id);
2028
2029 (void)fclose (font_fp);
2030 start_object (font_file_id);
2031 (void)printf ("<</Length %ld /Length1 %ld>>\nstream\n", font_size, font_size);
2032 (void)fwrite (font_data, 1, font_size, stdout);
2033 (void)printf ("\nendstream\nendobj\n");
2034
2035 if (allocated_name)
2036 FREE (allocated_name);
2037
2038 FREE (font_data);
2039 }
2040
2041
2042
2043
2044
2045 static void
2046 embed_font_from_memory (const unsigned char * font_data, size_t font_size, const char * base_font)
2047 {
2048 char * allocated_name = read_font_name_from_mem (font_data, font_size);
2049 const char * internal_name = allocated_name;
2050
2051 if (NULL == internal_name)
2052 {
2053 internal_name = base_font;
2054 (void)fprintf (stderr, "WARNING: Could not read %s font name from embedded font; using fallback '%s'\n",
2055 base_font, internal_name);
2056 }
2057
2058 int font_descriptor_id = GLOBAL_OBJECT_ID++;
2059 int font_file_id = GLOBAL_OBJECT_ID++;
2060
2061 const unsigned char * p = font_data;
2062 const unsigned char * end = font_data + font_size;
2063
2064 uint32_t head_offset = 0, hhea_offset = 0, hmtx_offset = 0;
2065 uint16_t num_h_metrics = 0;
2066
2067 p += 4;
2068 uint16_t num_tables = read_u16_be_from_mem (& p, end);
2069 if (num_tables > 256)
2070 {
2071 (void)fprintf (stderr, "ERROR: Font has too many tables (%u > 256)\n", num_tables);
2072 exit (EXIT_FAILURE);
2073 }
2074
2075 p += 6;
2076
2077 for (int i = 0; i < num_tables; i++)
2078 {
2079 char tag [5] = { 0 };
2080
2081 if (p + 16 > end)
2082 exit (EXIT_FAILURE);
2083
2084 (void)memcpy (tag, p, 4);
2085 p += 4;
2086 p += 4;
2087 uint32_t offset = read_u32_be_from_mem (& p, end);
2088 (void)read_u32_be_from_mem (& p, end);
2089
2090 if (strcmp (tag, "head") == 0)
2091 head_offset = offset;
2092
2093 if (strcmp (tag, "hhea") == 0)
2094 hhea_offset = offset;
2095
2096 if (strcmp (tag, "hmtx") == 0)
2097 hmtx_offset = offset;
2098 }
2099
2100 int16_t x_min = 0, y_min = 0, x_max = 0, y_max = 0;
2101 if (head_offset)
2102 {
2103 const unsigned char * head_p = font_data + head_offset + 36;
2104 x_min = (int16_t)read_u16_be_from_mem (& head_p, end);
2105 y_min = (int16_t)read_u16_be_from_mem (& head_p, end);
2106 x_max = (int16_t)read_u16_be_from_mem (& head_p, end);
2107 y_max = (int16_t)read_u16_be_from_mem (& head_p, end);
2108 }
2109
2110 if (hhea_offset)
2111 {
2112 const unsigned char * hhea_p = font_data + hhea_offset + 34;
2113 num_h_metrics = read_u16_be_from_mem (& hhea_p, end);
2114 }
2115
2116 (void)printf ("<</Type/Font/Subtype/TrueType/BaseFont/%s/Encoding/WinAnsiEncoding"
2117 "/FirstChar 32/LastChar 255/FontDescriptor %d 0 R/Widths [", internal_name, font_descriptor_id);
2118
2119 if (hmtx_offset && num_h_metrics > 0)
2120 {
2121 const unsigned char * hmtx_p = font_data + hmtx_offset;
2122 uint16_t advance_width = 0;
2123 for (int i = 0; i < 224; i++)
2124 {
2125 if (i < num_h_metrics)
2126 {
2127 advance_width = read_u16_be_from_mem (& hmtx_p, end);
2128 (void)read_u16_be_from_mem (& hmtx_p, end);
2129 }
2130 (void)printf ("%d ", (int)(advance_width * 1000.0f / 2048.0f));
2131 }
2132 }
2133
2134 (void)printf ("]>>\nendobj\n");
2135
2136 start_object (font_descriptor_id);
2137 (void)printf ("<</Type/FontDescriptor/FontName/%s/Flags 32/FontBBox [%d %d %d %d]/Ascent %d/Descent %d"
2138 "/CapHeight %d/ItalicAngle 0/FontFile2 %d 0 R>>\nendobj\n", internal_name,
2139 (int)(x_min * 1000.0f / 2048.0f), (int)(y_min * 1000.0f / 2048.0f), (int)(x_max * 1000.0f / 2048.0f),
2140 (int)(y_max * 1000.0f / 2048.0f), (int)(y_max * 1000.0f / 2048.0f), (int)(y_min * 1000.0f / 2048.0f),
2141 (int)(y_max * 1000.0f / 2048.0f), font_file_id);
2142
2143 start_object (font_file_id);
2144 (void)printf ("<</Length %zu /Length1 %zu>>\nstream\n", font_size, font_size);
2145 (void)fwrite (font_data, 1, font_size, stdout);
2146 (void)printf ("\nendstream\nendobj\n");
2147
2148 if (allocated_name)
2149 FREE (allocated_name);
2150 }
2151
2152
2153
2154
2155
2156 static void
2157 dopages (void)
2158 {
2159 int i, catalog_id, font_id0, font_id1;
2160 long start_xref;
2161
2162 (void)printf ("%%PDF-1.0\n");
2163
2164
2165 (void)fprintf (stdout, "%%%c%c%c%c\n", 128, 129, 130, 131);
2166 (void)fprintf (stdout, "%% PDF: Adobe Portable Document Format\n");
2167
2168 GLOBAL_LEAD_SIZE = (GLOBAL_PAGE_DEPTH - GLOBAL_PAGE_MARGIN_TOP - GLOBAL_PAGE_MARGIN_BOTTOM) / GLOBAL_LINES_PER_PAGE;
2169 GLOBAL_FONT_SIZE = GLOBAL_LEAD_SIZE;
2170
2171 GLOBAL_OBJECT_ID = 1;
2172 GLOBAL_PAGE_TREE_ID = GLOBAL_OBJECT_ID++;
2173
2174 do_text ();
2175
2176 font_id0 = GLOBAL_OBJECT_ID++;
2177 start_object (font_id0);
2178
2179 if (GLOBAL_FONT_REGULAR_PATH)
2180 embed_font (GLOBAL_FONT_REGULAR_PATH, "Regular");
2181 else
2182 {
2183 if (GLOBAL_DOT_MATRIX)
2184 {
2185 if (sizeof (ROSYMultics_Regular_ttf) != ROSYMultics_Regular_ttf_len)
2186 {
2187 (void)fprintf (stderr,
2188 "FATAL: Internal error: sizeof (ROSYMultics_Regular_ttf) [%ld] != ROSYMultics_Regular_ttf_len [%ld]\n",
2189 (long)sizeof (ROSYMultics_Regular_ttf), (long)ROSYMultics_Regular_ttf_len);
2190 abort ();
2191 }
2192
2193 embed_font_from_memory (ROSYMultics_Regular_ttf, sizeof (ROSYMultics_Regular_ttf), "Regular");
2194 }
2195 else
2196 {
2197 if (sizeof (CourierMultics_Roman_ttf) != CourierMultics_Roman_ttf_len)
2198 {
2199 (void)fprintf (stderr,
2200 "FATAL: Internal error: sizeof (CourierMultics_Roman_ttf) [%ld] != CourierMultics_Roman_ttf_len [%ld]\n",
2201 (long)sizeof (CourierMultics_Roman_ttf), (long)CourierMultics_Roman_ttf_len);
2202 abort ();
2203 }
2204
2205 embed_font_from_memory (CourierMultics_Roman_ttf, sizeof (CourierMultics_Roman_ttf), "Regular");
2206 }
2207 }
2208
2209 font_id1 = GLOBAL_OBJECT_ID++;
2210 start_object (font_id1);
2211
2212 if (GLOBAL_FONT_BOLD_PATH)
2213 embed_font (GLOBAL_FONT_BOLD_PATH, "Bold");
2214 else
2215 {
2216 if (GLOBAL_DOT_MATRIX)
2217 {
2218 if (sizeof (ROSYMultics_Bold_ttf) != ROSYMultics_Bold_ttf_len)
2219 {
2220 (void)fprintf (stderr,
2221 "FATAL: Internal error: sizeof (ROSYMultics_Bold_ttf) [%ld] != ROSYMultics_Bold_ttf_len [%ld]\n",
2222 (long)sizeof (ROSYMultics_Bold_ttf), (long)ROSYMultics_Bold_ttf_len);
2223 abort ();
2224 }
2225
2226 embed_font_from_memory (ROSYMultics_Bold_ttf, sizeof (ROSYMultics_Bold_ttf), "Bold");
2227 }
2228 else
2229 {
2230 if (sizeof (CourierMultics_Bold_ttf) != CourierMultics_Bold_ttf_len)
2231 {
2232 (void)fprintf (stderr,
2233 "FATAL: Internal error: sizeof (CourierMultics_Bold_ttf) [%ld] != CourierMultics_Bold_ttf_len [%ld]\n",
2234 (long)sizeof (CourierMultics_Bold_ttf), (long)CourierMultics_Bold_ttf_len);
2235 abort ();
2236 }
2237
2238 embed_font_from_memory (CourierMultics_Bold_ttf, sizeof (CourierMultics_Bold_ttf), "Bold");
2239 }
2240 }
2241
2242 start_object (GLOBAL_PAGE_TREE_ID);
2243
2244 (void)printf ("<</Type /Pages /Count %d\n", GLOBAL_NUM_PAGES);
2245
2246 PageList * ptr = GLOBAL_PAGE_LIST;
2247 (void)printf ("/Kids[\n");
2248
2249 while (ptr != NULL)
2250 {
2251 (void)printf ("%d 0 R\n", ptr -> page_id);
2252 ptr = ptr -> next;
2253 }
2254
2255 (void)printf ("]\n");
2256
2257 (void)printf ("/Resources <</ProcSet[/PDF/Text]/Font<</F0 %d 0 R /F1 %d 0 R>> >>\n", font_id0, font_id1);
2258 (void)printf ("/MediaBox [ 0 0 %g %g ]\n", GLOBAL_PAGE_WIDTH, GLOBAL_PAGE_DEPTH);
2259 (void)printf (">>\nendobj\n");
2260 catalog_id = GLOBAL_OBJECT_ID++;
2261 start_object (catalog_id);
2262 (void)printf ("<</Type/Catalog/Pages %d 0 R>>\nendobj\n", GLOBAL_PAGE_TREE_ID);
2263
2264 int info_id = GLOBAL_OBJECT_ID++;
2265 start_object (info_id);
2266 char producer_str [128];
2267 (void)snprintf (producer_str, sizeof (producer_str), "DPS8M prt2pdf version %d.%d.%d",
2268 GLOBAL_VERSION, GLOBAL_VERSION_MIN, GLOBAL_VERSION_PATCH);
2269
2270 time_t now = time (NULL);
2271 struct tm * t = localtime (& now);
2272 char pdf_date [128];
2273 (void)strftime (pdf_date, sizeof (pdf_date) - 1, "D:%Y%m%d%H%M%S", t);
2274
2275 (void)printf ("<</Producer(%s)/CreationDate(%s)/ModDate(%s)>>\nendobj\n", producer_str, pdf_date, pdf_date);
2276
2277 start_xref = ftell (stdout);
2278 (void)printf ("xref\n");
2279 (void)printf ("0 %d\n", GLOBAL_OBJECT_ID);
2280 (void)printf ("0000000000 65535 f \n");
2281
2282 for (i = 1; i < GLOBAL_OBJECT_ID; i++)
2283 (void)printf ("%010ld 00000 n \n", GLOBAL_XREFS [i]);
2284
2285 (void)printf ("trailer\n<<\n/Size %d\n/Root %d 0 R\n/Info %d 0 R\n>>\n", GLOBAL_OBJECT_ID, catalog_id, info_id);
2286 (void)printf ("startxref\n%ld\n%%%%EOF\n", (long)start_xref);
2287 }
2288
2289
2290
2291
2292
2293 static void
2294 cleanup_globals (void)
2295 {
2296 PageList * ptr = GLOBAL_PAGE_LIST;
2297
2298 while (ptr != NULL)
2299 {
2300 PageList * next = ptr -> next;
2301 free (ptr);
2302 ptr = next;
2303 }
2304
2305 GLOBAL_PAGE_LIST = NULL;
2306
2307 FREE (GLOBAL_XREFS);
2308 FREE (GLOBAL_TAB_STOPS);
2309 }
2310
2311
2312
2313
2314
2315 static void
2316 showhelp (int itype, char * program)
2317 {
2318 const char * program_name = (program && * program) ? program : "prt2pdf";
2319 switch (itype)
2320 {
2321 case 1:
2322 (void)fprintf (stderr, "prt2pdf: A filter to convert DPS8M Simulator print output files to PDF format\n");
2323 (void)fprintf (stderr, "\n");
2324 (void)fprintf (stderr, "prt2pdf reads data from standard input and writes PDF to standard output.\n");
2325 (void)fprintf (stderr, "\n");
2326 (void)fprintf (stderr, "Invoke as:\n");
2327 (void)fprintf (stderr, " %s [ options ] <infile.prt >outfile.pdf\n", program_name);
2328 (void)fprintf (stderr, "\n");
2329 if (GLOBAL_PAGE_HELP)
2330 {
2331 (void)fprintf (stderr, "Printable page area options:\n");
2332 (void)fprintf (stderr, " Physical paper size is specified using -H for height, -W for width, and -u\n");
2333 (void)fprintf (stderr, " to indicate the points per unit (72 makes H and W in inches, and 1 is used\n");
2334 (void)fprintf (stderr, " when units are in font points).\n");
2335 (void)fprintf (stderr, "\n");
2336 (void)fprintf (stderr, " For example:\n");
2337 (void)fprintf (stderr, " -u 72 -H 8.5 -W 11 = page height and width (in inches)\n");
2338 (void)fprintf (stderr, " -u 72 -B 0.5 -L 0.5 -R 0.5 -T 0.5 = margins (top, bottom, left, right)\n");
2339 (void)fprintf (stderr, "\n");
2340 (void)fprintf (stderr, " Common media sizes (with -u 1):\n");
2341 (void)fprintf (stderr, " +-------------------+-----+------+\n");
2342 (void)fprintf (stderr, " | Name | W | H |\n");
2343 (void)fprintf (stderr, " +-------------------+-----+------+\n");
2344 (void)fprintf (stderr, " | Letterdj (11x8.5) | 792 | 612 | (Landscape)\n");
2345 (void)fprintf (stderr, " | A4dj | 842 | 595 | (Landscape)\n");
2346 (void)fprintf (stderr, " | Letter (8.5x11) | 612 | 792 | (Portrait)\n");
2347 (void)fprintf (stderr, " | Legal | 612 | 1008 | (Portrait)\n");
2348 (void)fprintf (stderr, " | A5 | 420 | 595 | (Portrait)\n");
2349 (void)fprintf (stderr, " | A4 | 595 | 842 | (Portrait)\n");
2350 (void)fprintf (stderr, " | A3 | 842 | 1190 | (Portrait)\n");
2351 (void)fprintf (stderr, " +-------------------+-----+------+\n");
2352 (void)fprintf (stderr, "\n");
2353 }
2354 (void)fprintf (stderr, "Font embedding options:\n");
2355 (void)fprintf (stderr, " -d = use dot-matrix style font (disabling letter-quality) -or-\n");
2356 (void)fprintf (stderr, " -r <path> = path to custom Regular monospaced TrueType font to embed\n");
2357 (void)fprintf (stderr, " -b <path> = path to custom Bold monospaced TrueType font to embed\n");
2358 (void)fprintf (stderr, "\n");
2359 (void)fprintf (stderr, "Shading bar options:\n");
2360 (void)fprintf (stderr, " -c <rrggbb> = custom hex color for shading bars (e.g., -c badbfe) -or-\n");
2361 (void)fprintf (stderr, " -G = use green shading bars (instead of the default gray)\n");
2362 (void)fprintf (stderr, " -g <float> = scale adjustment value for gray or green shading bars,\n");
2363 (void)fprintf (stderr, " where 0.0 is fully dark and 1.0 is fully light;\n");
2364 (void)fprintf (stderr, " default is 0.32 for green bars, and 0.80 for gray bars\n");
2365 (void)fprintf (stderr, " -m = extend shading bars through the left and right margins\n");
2366 (void)fprintf (stderr, " -i 1 = repeat shade pattern every N lines\n");
2367 (void)fprintf (stderr, " -a = reverse shading bar ordering\n");
2368 (void)fprintf (stderr, "\n");
2369 (void)fprintf (stderr, "Text options:\n");
2370 (void)fprintf (stderr, " -l 66 = lines per page\n");
2371 (void)fprintf (stderr, " -t 10 = process tabs (e.g., -t 8 -or- -t 10,20,30 -or- GNU style)\n");
2372 (void)fprintf (stderr, " -O [0|o|O] = special handling for 'O' and zero ('0') glyphs:\n");
2373 (void)fprintf (stderr, " -O0 = print 'O' as zero ('0') and zero ('0') as 'O'\n");
2374 (void)fprintf (stderr, " -Oo = print both 'O' and zero ('0') as letter 'O'\n");
2375 (void)fprintf (stderr, " -OO = print both 'O' and zero ('0)' as zero ('0')\n");
2376 (void)fprintf (stderr, " -0 = print zero ('0') as slash-overstruck zero\n");
2377 (void)fprintf (stderr, " -1 = print one ('1') with underline\n");
2378 (void)fprintf (stderr, " -7 = print seven ('7') as bar-overstruck seven\n");
2379 (void)fprintf (stderr, " -Z = print capital 'Z' as bar-overstruck 'Z'\n");
2380 (void)fprintf (stderr, "\n");
2381 (void)fprintf (stderr, "Help options:\n");
2382 (void)fprintf (stderr, " -h = display this help text\n");
2383 (void)fprintf (stderr, " -P = display this help text (with extra paper size options)\n");
2384 (void)fprintf (stderr, " -v = display version information\n");
2385 (void)fprintf (stderr, " -V = display compiler version information\n");
2386 (void)fprintf (stderr, "\n");
2387 (void)fprintf (stderr, "Environment variables:\n");
2388 (void)fprintf (stderr, " $IMPACT_TOP = text to be printed in large red letters at top of pages\n");
2389 (void)fprintf (stderr, " $IMPACT_GRAY = sets the scale adjustment value (same as the -g switch)\n");
2390 (void)fprintf (stderr, "\n");
2391 (void)fprintf (stderr, "This software is made available under the terms of the ICU License.\n");
2392 (void)fprintf (stderr, "For complete license details, see the LICENSE file included with the\n");
2393 (void)fprintf (stderr, "software or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md\n");
2394
2395 break;
2396
2397 default:
2398 (void)fprintf (stderr, "ERROR: Internal error processing help request\n");
2399 (void)fprintf (stderr, "FATAL: Bugcheck! Aborting at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
2400 abort ();
2401 }
2402 }
2403
2404
2405
2406
2407
2408 static void
2409 parse_tab_stops (const char * s)
2410 {
2411 if (! s || ! * s)
2412 return;
2413
2414 char * str = (char *)malloc (strlen (s) + 1);
2415 if (NULL == str)
2416 {
2417 (void)fprintf (stderr, "FATAL: Out of memory! Aborting at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
2418 abort ();
2419 }
2420
2421
2422 strcpy (str, s);
2423
2424 if (strchr (s, ','))
2425 {
2426 int count = 0;
2427 for (const char * p = s; * p; p++)
2428 if (* p == ',')
2429 count++;
2430
2431 GLOBAL_TAB_STOPS = (int *)malloc (sizeof (int) * (count + 2));
2432
2433 if (NULL == GLOBAL_TAB_STOPS)
2434 {
2435 (void)fprintf (stderr, "FATAL: Out of memory! Aborting at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
2436 abort ();
2437 }
2438
2439 int i = 0;
2440 int last_val = 0;
2441 char * token = strtok (str, ",");
2442
2443 while (token)
2444 {
2445 char * endp;
2446 if (* token == '/' || * token == '+')
2447 {
2448 char prefix = * token;
2449 token++;
2450
2451 long val = strtol (token, & endp, 10);
2452
2453 if (endp == token || * endp != '\0' || val <= 0)
2454 {
2455 (void)fprintf (stderr, "ERROR: Illegal tab stop specification: '%s'\n", s);
2456 FREE (str);
2457 exit (EXIT_FAILURE);
2458 }
2459
2460 if (strtok (NULL, ","))
2461 {
2462 (void)fprintf (stderr, "ERROR: '/' or '+' must be at the end of the tab stop list: '%s'\n", s);
2463 FREE (str);
2464 exit (EXIT_FAILURE);
2465 }
2466
2467 if (prefix == '/')
2468 GLOBAL_TAB_INTERVAL = (int)val;
2469 else
2470 GLOBAL_TAB_RELATIVE_INTERVAL = (int)val;
2471
2472 break;
2473 }
2474
2475 long val = strtol (token, & endp, 10);
2476
2477 if (endp != token + strlen (token))
2478 {
2479 (void)fprintf (stderr, "ERROR: Illegal character in tab stop specification: '%s'\n", s);
2480 FREE (str);
2481 exit (EXIT_FAILURE);
2482 }
2483
2484 if (val <= last_val)
2485 {
2486 (void)fprintf (stderr, "ERROR: Impossible tab stop specification order: '%s'\n", s);
2487 FREE (str);
2488 exit (EXIT_FAILURE);
2489 }
2490
2491 GLOBAL_TAB_STOPS [i++] = (int)val;
2492 last_val = (int)val;
2493 token = strtok (NULL, ",");
2494 }
2495
2496 GLOBAL_TAB_STOPS [i] = 0;
2497 GLOBAL_TAB_PROCESSING = 1;
2498 }
2499 else
2500 {
2501 char * endp;
2502 long val = strtol (str, & endp, 10);
2503
2504 if (endp != str + strlen (str) || val < 0)
2505 {
2506 (void)fprintf (stderr, "ERROR: Illegal tab stop specification: '%s'\n", s);
2507 FREE (str);
2508 exit (EXIT_FAILURE);
2509 }
2510
2511 if (val <= 1)
2512 GLOBAL_TAB_PROCESSING = 0;
2513 else
2514 {
2515 GLOBAL_TAB_INTERVAL = (int)val;
2516 GLOBAL_TAB_PROCESSING = 1;
2517 }
2518 }
2519
2520 FREE (str);
2521 }
2522
2523
2524
2525
2526
2527 int
2528 main (int argc, char * * argv)
2529 {
2530 #if !defined(NO_LOCALE)
2531 (void)setlocale (LC_ALL, "");
2532 #endif
2533
2534 char * varname;
2535
2536 int prindex;
2537 int c;
2538 GLOBAL_PAGE_DEPTH = 612.0;
2539 GLOBAL_PAGE_WIDTH = 792.0;
2540 GLOBAL_PAGE_MARGIN_TOP = 36.0 - 24.0;
2541 GLOBAL_PAGE_MARGIN_BOTTOM = 36.0 - 24.0;
2542 GLOBAL_PAGE_MARGIN_LEFT = 40.0 - 14.0;
2543 GLOBAL_PAGE_MARGIN_RIGHT = 39.0 - 14.0;
2544 GLOBAL_LINES_PER_PAGE = 65.0;
2545 GLOBAL_GRAY_SCALE = 0.800781;
2546 GLOBAL_GREEN_BAR = 0;
2547 GLOBAL_TAB_PROCESSING = 1;
2548 GLOBAL_TAB_INTERVAL = 10;
2549
2550 #if defined(_WIN32)
2551 _setmode (_fileno (stdin), O_BINARY);
2552 _setmode (_fileno (stdout), O_BINARY);
2553 #endif
2554
2555 varname = getenv ("IMPACT_GRAY");
2556 if (varname == (char *)NULL)
2557 GLOBAL_GRAY_SCALE = 0.800781;
2558 else if (varname [0] == '\0')
2559 GLOBAL_GRAY_SCALE = 0.800781;
2560 else
2561 {
2562 (void)sscanf (varname, "%f", & GLOBAL_GRAY_SCALE);
2563 if (GLOBAL_GRAY_SCALE < 0)
2564 GLOBAL_GRAY_SCALE = 0.800781;
2565 }
2566
2567 opterr = 0;
2568
2569 while ((c = getopt (argc, argv, "aB:b:c:dg:H:hi:L:l:mGR:r:S:T:t:u:W:vV017PZO:"))
2570 != -1)
2571 switch (c)
2572 {
2573 case '1':
2574 GLOBAL_ONE_UNDERLINE = 1;
2575
2576 break;
2577
2578 case 'O':
2579 if (optarg == NULL || (0 != strcmp (optarg, "0") && 0 != strcmp (optarg, "o") && 0 != strcmp (optarg, "O")))
2580 {
2581 (void)fprintf (stderr, "ERROR: -O argument requires an option, one of: '0', 'o', or 'O' (example: -O0)\n");
2582 exit (EXIT_FAILURE);
2583 }
2584
2585 if (0 == strcmp (optarg, "0"))
2586 GLOBAL_O_MODE = 1;
2587 else if (0 == strcmp (optarg, "o"))
2588 GLOBAL_O_MODE = 2;
2589 else if (0 == strcmp (optarg, "O"))
2590 GLOBAL_O_MODE = 3;
2591
2592 break;
2593
2594 case '0':
2595 GLOBAL_ZERO_OVERSTRIKE = 1;
2596
2597 break;
2598
2599 case '7':
2600 GLOBAL_SEVEN_OVERSTRIKE = 1;
2601
2602 break;
2603
2604 case 'Z':
2605 GLOBAL_ZED_OVERSTRIKE = 1;
2606
2607 break;
2608
2609 case 'a':
2610 GLOBAL_SHADE_ALTERNATE_START = 1;
2611
2612 break;
2613
2614 case 'c':
2615 if (strlen (optarg) != 6)
2616 {
2617 (void)fprintf (stderr, "ERROR: -c argument must be a 6-character hex color string (rrggbb)\n");
2618 exit (EXIT_FAILURE);
2619 }
2620
2621 for (int i = 0; i < 6; i++)
2622 {
2623 if (! isxdigit ((unsigned char) optarg [i]))
2624 {
2625 (void)fprintf (stderr, "ERROR: -c argument accepts only hex characters (rrggbb)\n");
2626 exit (EXIT_FAILURE);
2627 }
2628 }
2629
2630 GLOBAL_SHADE_COLOR = optarg;
2631
2632 break;
2633
2634 case 'd':
2635 GLOBAL_DOT_MATRIX = 1;
2636
2637 break;
2638
2639 case 'L':
2640 GLOBAL_PAGE_MARGIN_LEFT = strtod (optarg, NULL) * GLOBAL_UNIT_MULTIPLIER;
2641
2642 break;
2643
2644 case 'R':
2645 GLOBAL_PAGE_MARGIN_RIGHT = strtod (optarg, NULL) * GLOBAL_UNIT_MULTIPLIER;
2646
2647 break;
2648
2649 case 'B':
2650 GLOBAL_PAGE_MARGIN_BOTTOM = strtod (optarg, NULL) * GLOBAL_UNIT_MULTIPLIER;
2651
2652 break;
2653
2654 case 'T':
2655 GLOBAL_PAGE_MARGIN_TOP = strtod (optarg, NULL) * GLOBAL_UNIT_MULTIPLIER;
2656
2657 break;
2658
2659 case 'H':
2660 GLOBAL_PAGE_DEPTH = strtod (optarg, NULL) * GLOBAL_UNIT_MULTIPLIER;
2661
2662 break;
2663
2664 case 'W':
2665 GLOBAL_PAGE_WIDTH = strtod (optarg, NULL) * GLOBAL_UNIT_MULTIPLIER;
2666
2667 break;
2668
2669 case 'g':
2670 GLOBAL_GRAY_SCALE = strtod (optarg, NULL);
2671
2672 break;
2673
2674 case 'G':
2675 GLOBAL_GREEN_BAR = 1;
2676
2677 break;
2678
2679 case 'l':
2680 GLOBAL_LINES_PER_PAGE = strtod (optarg, NULL);
2681
2682 if (isinf (GLOBAL_LINES_PER_PAGE) || isnan (GLOBAL_LINES_PER_PAGE) ||
2683 '-' == optarg [0] || 0 == GLOBAL_LINES_PER_PAGE)
2684 {
2685 (void)fprintf (stderr, "ERROR: -l option requires a positive (non-zero) numeric argument\n");
2686 exit (EXIT_FAILURE);
2687 }
2688
2689 break;
2690
2691 case 'm':
2692 GLOBAL_SHADE_EDGE_TO_EDGE = 1;
2693
2694 break;
2695
2696 case 'u':
2697 GLOBAL_UNIT_MULTIPLIER = strtod (optarg, NULL);
2698
2699 break;
2700
2701 case 'i':
2702 GLOBAL_SHADE_STEP = (int)strtod (optarg, NULL);
2703
2704 break;
2705
2706 case 'S':
2707 GLOBAL_SHIFT = (int)MAX (0, strtod (optarg, NULL));
2708
2709 break;
2710
2711 case 't':
2712 if (! optarg || 0 == strcmp (optarg, "0") || '-' == optarg [0])
2713 {
2714 (void)fprintf (stderr, "ERROR: -t option requires a positive (non-zero) numeric argument\n");
2715 exit (EXIT_FAILURE);
2716 }
2717
2718 parse_tab_stops (optarg);
2719
2720 break;
2721
2722 case 'r':
2723 GLOBAL_FONT_REGULAR_PATH = optarg;
2724
2725 break;
2726
2727 case 'b':
2728 GLOBAL_FONT_BOLD_PATH = optarg;
2729
2730 break;
2731
2732 case 'P':
2733 GLOBAL_PAGE_HELP = 1;
2734
2735 case 'h':
2736 showhelp (1, argv [0]);
2737 exit (EXIT_SUCCESS);
2738
2739 break;
2740
2741 case 'V':
2742 {
2743 #if defined(__VERSION__)
2744 # if defined(__GNUC__)
2745 # if !defined(__clang_version__) || defined(__INTEL_COMPILER)
2746 char xcmp [2];
2747 (void)snprintf (xcmp, sizeof (xcmp), "%.1s", __VERSION__);
2748 if (! isdigit ((int)xcmp [0]))
2749 (void)fprintf (stderr, "Compiler: %s\n", __VERSION__);
2750 else
2751 (void)fprintf (stderr, "Compiler: GCC %s\n", __VERSION__);
2752 # else
2753 (void)fprintf (stderr, "Compiler: Clang %s\n", __clang_version__);
2754 # endif
2755 # else
2756 (void)fprintf (stderr, "Compiler: %s\n", __VERSION__);
2757 # endif
2758 #endif
2759 exit (EXIT_SUCCESS);
2760
2761 break;
2762 }
2763
2764 case 'v':
2765 (void)fprintf (stderr, "prt2pdf version %d.%d.%d\n", GLOBAL_VERSION, GLOBAL_VERSION_MIN, GLOBAL_VERSION_PATCH);
2766 exit (EXIT_SUCCESS);
2767
2768 break;
2769
2770 case '?':
2771 if (isprint (optopt))
2772 (void)fprintf (stderr, "ERROR: Illegal usage of option '-%c'", optopt);
2773 else
2774 (void)fprintf (stderr, "ERROR: Unknown option character '\\x%x'", optopt);
2775 (void)fprintf (stderr, " - proper usage details follow.\n\n");
2776 showhelp (1, argv [0]);
2777 exit (EXIT_FAILURE);
2778
2779 #if !defined(__SUNPRO_C) && !defined(__SUNPRO_CC) && !defined(__SUNPRO_CC_COMPAT)
2780 return 1;
2781 #endif
2782
2783 default:
2784 (void)fprintf (stderr, "FATAL: Bugcheck! Aborting at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
2785 abort ();
2786
2787 #if !defined(__SUNPRO_C) && !defined(__SUNPRO_CC) && !defined(__SUNPRO_CC_COMPAT)
2788 return 1;
2789 #endif
2790
2791 }
2792
2793 if (GLOBAL_DOT_MATRIX && (GLOBAL_FONT_REGULAR_PATH || GLOBAL_FONT_BOLD_PATH))
2794 {
2795 (void)fprintf (stderr, "ERROR: The -d option cannot be used with -r or -b\n");
2796 exit (EXIT_FAILURE);
2797 }
2798
2799 if (GLOBAL_SHADE_STEP < 1)
2800 {
2801 (void)fprintf (stderr, "WARNING: prt2pdf resetting -i %d to -i 1\n", GLOBAL_SHADE_STEP);
2802 GLOBAL_SHADE_STEP = 1;
2803 }
2804
2805 for (prindex = optind; prindex < argc; prindex++)
2806 (void)fprintf (stderr, "WARNING: Non-option argument %s\n", argv [prindex]);
2807
2808 dopages ();
2809
2810 cleanup_globals ();
2811
2812 exit (EXIT_SUCCESS);
2813 }
2814
2815
2816
2817