root/src/prt2pdf/prt2pdf.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. xstrerror_l
  2. store_page
  3. start_object
  4. print_bars
  5. printstring
  6. printme_top
  7. print_margin_label
  8. start_page
  9. end_page
  10. do_text
  11. read_u16_be
  12. read_u32_be
  13. read_u16_be_from_mem
  14. read_u32_be_from_mem
  15. emit_utf8_from_codepoint
  16. utf16be_to_utf8
  17. next_utf8_cp
  18. is_win1252_defined_codepoint
  19. is_win1252_compatible_utf8
  20. mac_roman_to_utf8
  21. decode_name_to_utf8
  22. score_name_record
  23. read_cff_postscript_name
  24. read_cff_postscript_name_from_mem
  25. read_font_name
  26. read_font_name_from_mem
  27. embed_font
  28. embed_font_from_memory
  29. dopages
  30. cleanup_globals
  31. showhelp
  32. parse_tab_stops
  33. main

   1 /*
   2  * vim: filetype=c:tabstop=2:softtabstop=2:shiftwidth=2:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * scspell-id: 563cbc3d-f631-11ec-a429-80ee73e9b8e7
   5  *
   6  * =================================================================================================================================
   7  *
   8  * Copyright (c) 1998 P. G. Womack, Diss, Norfolk, UK.
   9  * Copyright (c) 2006 John S. Urban <urbanjost@comcast.net>
  10  * Copyright (c) 2013-2016 Charles Anthony
  11  * Copyright (c) 2021-2025 Jeffrey H. Johnson
  12  * Copyright (c) 2021-2025 The DPS8M Development Team
  13  *
  14  * This software is made available under the terms of the ICU License.
  15  * See the LICENSE.md file at the top-level directory of this distribution.
  16  *
  17  * - Began with txt2pdf by P. G. Womack, Diss, Norfolk, UK.
  18  * - Derived from asa2pdf by John S. Urban - Apr 30, 2006
  19  * - Extensively modified by Charles Anthony - 2016
  20  * - Extended and rewritten by Jeffrey H. Johnson - 2024-2025
  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 /* if defined(_WIN32) */
  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 /* https://gitlab.com/dps8m/font-courier-multics/ */
  87 #include "CourierMultics-Roman.h"
  88 #include "CourierMultics-Bold.h"
  89 
  90 /*
  91  * =================================================================================================================================
  92  */
  93 
  94 /* https://gitlab.com/dps8m/font-rosy-multics/ */
  95 #include "ROSYMultics-Regular.h"
  96 #include "ROSYMultics-Bold.h"
  97 
  98 /*
  99  * =================================================================================================================================
 100  */
 101 
 102 #if defined(FREE)
 103 # undef FREE
 104 #endif /* if defined(FREE) */
 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 /* if !defined(__MINGW64__) */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 227 {
 228   int saved = errno;
 229   const char * ret = NULL;
 230   static /* __thread */ 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)) /*LINTOK: xstrerror_l*/
 236     ret = buf;
 237 #  else
 238   if (0 == strerror_r (errnum, buf, sizeof (buf))) /*LINTOK: xstrerror_l*/
 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); /*LINTOK: xstrerror_l*/
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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   /* cppcheck-suppress nullPointerOutOfMemory */
 294   n -> next = NULL;
 295   /* cppcheck-suppress nullPointerOutOfMemory */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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       /* cppcheck-suppress nullPointerOutOfMemory */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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) //-V550
 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); /* green */
 372     }
 373   else
 374     (void)fprintf (stdout, "%f g\n", GLOBAL_GRAY_SCALE); /* gray-scale value */
 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; /* Shift the starting point down by one bar */
 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); /* gray-scale value */
 404 }
 405 
 406 /*
 407  * =================================================================================================================================
 408  */
 409 
 410 static void
 411 printstring (char * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 412 {
 413   /* Print string as (escaped_string) where ()\ have a preceding \ character added */
 414   char c;
 415   (void)putchar ('(');
 416 
 417   while ((c = * buffer++) != '\0')
 418     {
 419       switch (c + GLOBAL_ADD)
 420         {
 421         case '(':
 422           /*FALLTHRU*/ /* fall through */ /* fallthrough */
 423         case ')':
 424           /*FALLTHRU*/ /* fall through */ /* fallthrough */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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; /* assuming fixed-space font Courier-Bold */
 452       (void)fprintf (stdout, "1.0 0.0 0.0 rg\n"); /* gray-scale value */
 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"); /* gray-scale value */
 462     }
 463 }
 464 
 465 /*
 466  * =================================================================================================================================
 467  */
 468 
 469 static void
 470 print_margin_label (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 471 {
 472   printme_top ();
 473 }
 474 
 475 /*
 476  * =================================================================================================================================
 477  */
 478 
 479 static void
 480 start_page (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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'; //-V1048
 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'; //-V1048
 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); //-V602
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
1040 {
1041   if (! buf || len == 0 || (len & 1))
1042     return NULL;
1043 
1044   /* Worst-case size: 2x input bytes + NUL */
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             { /* truncated surrogate */
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             { /* invalid pair */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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; /* overlong */
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; /* overlong */
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; /* overlong or out of range */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
1159 {
1160   /* Windows-1252 defines most of 0x20..0x7E and 0xA0..0xFF plus some in 0x80..0x9F. */
1161   /* Undefined: 0x81, 0x8D, 0x8F, 0x90, 0x9D. Control chars < 0x20 not allowed. */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
1232 {
1233   size_t cap = len * 3 + 1; /* Worst-case: each byte -> 3 bytes UTF-8 */
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])) //-V547
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)
     /* [previous][next][first][last][top][bottom][index][help] */
1275 {
1276   if (platformID == 3) /* Windows (3) and Unicode (0) are generally UTF-16BE. */
1277     { /* encID 1 (Unicode BMP) or 10 (UCS-4, yet often still UTF-16BE for name table). */
1278       return utf16be_to_utf8 (buf, len); /* Treat as UTF-16BE; most fonts store UTF-16BE. */
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     { /* Fallback: assume ASCII/Latin-1-ish */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
1305 {
1306   int score = 0;
1307 
1308   if (nameID == 6) /* Prefer PostScript name (ID 6), then full name (ID 4) */
1309     score += 1000;
1310   else if (nameID == 4)
1311     score += 500;
1312   else
1313     return -100000; /* not considered */
1314 
1315   if (platformID == 3) /* Platform/encoding preferences */
1316     {
1317       score += 200;
1318       if (encodingID == 1 || encodingID == 10)
1319         score += 50;
1320       if (languageID == 0x0409)
1321         score += 100; /* en-US */
1322     }
1323   else if (platformID == 1 && encodingID == 0)
1324     {
1325       score += 150; /* MacRoman (often ASCII for PS names) */
1326       if (languageID == 0)
1327         score += 25; /* default */
1328     }
1329   else if (platformID == 0)
1330     score += 120; /* Unicode platform */
1331 
1332   if (win1252_ok) /* WinAnsi compatibility bonus */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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]; /* CFF Header: major, minor, hdrSize, offSize */
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   /* Move to Name INDEX */
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   /* INDEX: count (u16), offSize (u8), offsets [(count+1)* offSize], then data */
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   /* Read first two offsets, then skip the rest */
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; /* ASCII PostScript name */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
1538 {
1539   FILE * fp = fopen (font_path, "rb");
1540   if (! fp)
1541     return NULL;
1542 
1543   /* SFNT header: skip version (4 bytes), then numTables (u16), skip 6 bytes */
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); /* checksum, ignored */
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           /* Decode to UTF-8 */
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   /* Fallback: CFF Name INDEX (for CFF-based OpenType or if name table missing) */
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; /* may be NULL if not available */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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; /* Skip version */
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; /* Skip searchRange, entrySelector, rangeShift */
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; /* checksum */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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); /* checksum */
1960       uint32_t offset = read_u32_be (font_fp);
1961       (void)read_u32_be (font_fp); /* length */
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); /* lsb */
2008             }
2009           else
2010             {
2011               /* for glyphs beyond num_h_metrics, the advance width is the same as the last one. */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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; /* Skip version */
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; /* Skip searchRange, entrySelector, rangeShift */
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; /* checksum */
2087       uint32_t offset = read_u32_be_from_mem (& p, end);
2088       (void)read_u32_be_from_mem (& p, end); /* length */
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); /* lsb */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
2158 {
2159   int i, catalog_id, font_id0, font_id1;
2160   long start_xref;
2161 
2162   (void)printf ("%%PDF-1.0\n");
2163 
2164   /* Recommended header for PDF to be detected as binary */
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     { /* Obvious re-jiggering required if font is ever moved to extern */
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     { /* Obvious re-jiggering required if font is ever moved to extern */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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   /* cppcheck-suppress nullPointerOutOfMemory */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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; /* Default is 72 points per inch */
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; /* scale value default */
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 /* if defined(_WIN32) */
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': /* underline 1 */
2574         GLOBAL_ONE_UNDERLINE = 1;
2575 
2576         break;
2577 
2578       case 'O': /* O-glyph handling */
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': /* overstrike zero */
2595         GLOBAL_ZERO_OVERSTRIKE = 1;
2596 
2597         break;
2598 
2599       case '7': /* overstrike seven */
2600         GLOBAL_SEVEN_OVERSTRIKE = 1;
2601 
2602         break;
2603 
2604       case 'Z': /* overstrike zed */
2605         GLOBAL_ZED_OVERSTRIKE = 1;
2606 
2607         break;
2608 
2609       case 'a': /* alternate shade order */
2610         GLOBAL_SHADE_ALTERNATE_START = 1;
2611 
2612         break;
2613 
2614       case 'c': /* Hex color string */
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': /* dot-matrix font */
2635         GLOBAL_DOT_MATRIX = 1;
2636 
2637         break;
2638 
2639       case 'L': /* Left margin */
2640         GLOBAL_PAGE_MARGIN_LEFT = strtod (optarg, NULL) * GLOBAL_UNIT_MULTIPLIER;
2641 
2642         break;
2643 
2644       case 'R': /* Right margin */
2645         GLOBAL_PAGE_MARGIN_RIGHT = strtod (optarg, NULL) * GLOBAL_UNIT_MULTIPLIER;
2646 
2647         break;
2648 
2649       case 'B': /* Bottom margin */
2650         GLOBAL_PAGE_MARGIN_BOTTOM = strtod (optarg, NULL) * GLOBAL_UNIT_MULTIPLIER;
2651 
2652         break;
2653 
2654       case 'T': /* Top margin */
2655         GLOBAL_PAGE_MARGIN_TOP = strtod (optarg, NULL) * GLOBAL_UNIT_MULTIPLIER;
2656 
2657         break;
2658 
2659       case 'H': /* Height */
2660         GLOBAL_PAGE_DEPTH = strtod (optarg, NULL) * GLOBAL_UNIT_MULTIPLIER;
2661 
2662         break;
2663 
2664       case 'W': /* Width */
2665         GLOBAL_PAGE_WIDTH = strtod (optarg, NULL) * GLOBAL_UNIT_MULTIPLIER;
2666 
2667         break;
2668 
2669       case 'g': /* grayscale value for bars */
2670         GLOBAL_GRAY_SCALE = strtod (optarg, NULL);
2671 
2672         break;
2673 
2674       case 'G': /* green bars */
2675         GLOBAL_GREEN_BAR = 1;
2676 
2677         break;
2678 
2679       case 'l': /* lines per page */
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': /* shade edge-to-edge */
2692         GLOBAL_SHADE_EDGE_TO_EDGE = 1;
2693 
2694         break;
2695 
2696       case 'u': /* unit_divisor */
2697         GLOBAL_UNIT_MULTIPLIER = strtod (optarg, NULL);
2698 
2699         break;
2700 
2701       case 'i': /* increment for bars */
2702         GLOBAL_SHADE_STEP = (int)strtod (optarg, NULL);
2703 
2704         break;
2705 
2706       case 'S': /* right shift */
2707         GLOBAL_SHIFT = (int)MAX (0, strtod (optarg, NULL));
2708 
2709         break;
2710 
2711       case 't': /* tab stops */
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': /* regular font */
2723         GLOBAL_FONT_REGULAR_PATH = optarg;
2724 
2725         break;
2726 
2727       case 'b': /* bold font */
2728         GLOBAL_FONT_BOLD_PATH = optarg;
2729 
2730         break;
2731 
2732       case 'P': /* help + page size */
2733         GLOBAL_PAGE_HELP = 1;
2734         /*FALLTHRU*/ /* fall through */ /* fallthrough */
2735       case 'h': /* help */
2736         showhelp (1, argv [0]);
2737         exit (EXIT_SUCCESS);
2738         /*NOTREACHED*/ /* unreachable */
2739         break;
2740 
2741       case 'V': /* build info */
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 /* if !defined(__clang_version__) || defined(__INTEL_COMPILER) */
2755 # else
2756           (void)fprintf (stderr, "Compiler: %s\n", __VERSION__);
2757 # endif /* if defined(__GNUC__) */
2758 #endif /* if defined(__VERSION__) */
2759           exit (EXIT_SUCCESS);
2760           /*NOTREACHED*/ /* unreachable */
2761           break;
2762         }
2763 
2764       case 'v': /* version */
2765         (void)fprintf (stderr, "prt2pdf version %d.%d.%d\n", GLOBAL_VERSION, GLOBAL_VERSION_MIN, GLOBAL_VERSION_PATCH);
2766         exit (EXIT_SUCCESS);
2767         /*NOTREACHED*/ /* unreachable */
2768         break;
2769 
2770       case '?': /* switch info */
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         /*NOTREACHED*/ /* unreachable */
2779 #if !defined(__SUNPRO_C) && !defined(__SUNPRO_CC) && !defined(__SUNPRO_CC_COMPAT)
2780         return 1;
2781 #endif /* if !defined(__SUNPRO_C) && !defined(__SUNPRO_CC) && !defined(__SUNPRO_CC_COMPAT) */
2782       /*NOTREACHED*/ /* unreachable */
2783       default: /* unknown */
2784         (void)fprintf (stderr, "FATAL: Bugcheck! Aborting at %s[%s:%d]\n", __func__, __FILE__, __LINE__);
2785         abort ();
2786         /*NOTREACHED*/ /* unreachable */
2787 #if !defined(__SUNPRO_C) && !defined(__SUNPRO_CC) && !defined(__SUNPRO_CC_COMPAT)
2788         return 1;
2789 #endif /* if !defined(__SUNPRO_C) && !defined(__SUNPRO_CC) && !defined(__SUNPRO_CC_COMPAT) */
2790         /*NOTREACHED*/ /* unreachable */
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  */

/* [previous][next][first][last][top][bottom][index][help] */