LCOV - code coverage report
Current view: top level - libs/libgroff - font.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 731 963 75.9 %
Date: 2026-01-16 17:51:41 Functions: 47 52 90.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright 1989-2025 Free Software Foundation, Inc.
       2             :      Written by James Clark (jjc@jclark.com)
       3             : 
       4             : This file is part of groff, the GNU roff typesetting system.
       5             : 
       6             : groff is free software; you can redistribute it and/or modify it under
       7             : the terms of the GNU General Public License as published by the Free
       8             : Software Foundation, either version 3 of the License, or
       9             : (at your option) any later version.
      10             : 
      11             : groff is distributed in the hope that it will be useful, but WITHOUT ANY
      12             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14             : for more details.
      15             : 
      16             : You should have received a copy of the GNU General Public License
      17             : along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      18             : 
      19             : #ifdef HAVE_CONFIG_H
      20             : #include <config.h>
      21             : #endif
      22             : 
      23             : #include <assert.h>
      24             : #include <ctype.h>
      25             : #include <errno.h>
      26             : #include <limits.h> // INT_MAX, INT_MIN, LONG_MAX
      27             : #include <math.h>
      28             : #include <stdcountof.h>
      29             : #include <stdlib.h>
      30             : #include <string.h> // strerror()
      31             : #include <wchar.h>
      32             : 
      33             : #include "lib.h"
      34             : 
      35             : #include "errarg.h"
      36             : #include "error.h"
      37             : #include "cset.h"
      38             : #include "font.h"
      39             : #include "unicode.h"
      40             : #include "paper.h"
      41             : 
      42             : const char *const WS = " \t\n\r";
      43             : 
      44             : struct font_char_metric {
      45             :   char type;
      46             :   int code;
      47             :   int width;
      48             :   int height;
      49             :   int depth;
      50             :   int pre_math_space;
      51             :   int italic_correction;
      52             :   int subscript_correction;
      53             :   char *special_device_coding;
      54             :   struct font_char_metric *next;
      55             :   int end_code;
      56             : };
      57             : 
      58             : struct font_kern_list {
      59             :   glyph *glyph1;
      60             :   glyph *glyph2;
      61             :   int amount;
      62             :   font_kern_list *next;
      63             : 
      64             :   font_kern_list(glyph *, glyph *, int,
      65             :                  font_kern_list * = 0 /* nullptr */);
      66             : };
      67             : 
      68             : struct font_widths_cache {
      69             :   font_widths_cache *next;
      70             :   int point_size;
      71             :   int *width;
      72             : 
      73             :   font_widths_cache(int, int, font_widths_cache * = 0 /* nullptr */);
      74             :   ~font_widths_cache();
      75             : };
      76             : 
      77             : /* text_file */
      78             : 
      79             : struct text_file {
      80             :   FILE *fp;
      81             :   char *path;
      82             :   int lineno;
      83             :   int linebufsize;
      84             :   bool recognize_comments;
      85             :   bool silent;
      86             :   char *buf;
      87             :   text_file(FILE *fp, char *p);
      88             :   ~text_file();
      89             :   bool next_line();
      90             :   void error(const char *format,
      91             :              const errarg &arg1 = empty_errarg,
      92             :              const errarg &arg2 = empty_errarg,
      93             :              const errarg &arg3 = empty_errarg);
      94             :   void fatal(const char *format,
      95             :              const errarg &arg1 = empty_errarg,
      96             :              const errarg &arg2 = empty_errarg,
      97             :              const errarg &arg3 = empty_errarg);
      98             : };
      99             : 
     100       24353 : text_file::text_file(FILE *p, char *s) : fp(p), path(s), lineno(0),
     101             :   linebufsize(128), recognize_comments(true), silent(false),
     102       24353 :   buf(0 /* nullptr */)
     103             : {
     104       24353 : }
     105             : 
     106       48706 : text_file::~text_file()
     107             : {
     108       24353 :   delete[] buf;
     109       24353 :   free(path);
     110       24353 :   if (fp)
     111       24353 :     fclose(fp);
     112       24353 : }
     113             : 
     114     8191050 : bool text_file::next_line()
     115             : {
     116     8191050 :   if (0 /* nullptr */ == fp)
     117           0 :     return false;
     118     8191050 :   if (0 /* nullptr */ == buf)
     119       24353 :     buf = new char[linebufsize];
     120             :   for (;;) {
     121     8396559 :     lineno++;
     122     8396559 :     int length = 0;
     123             :     for (;;) {
     124   147389771 :       int c = getc(fp);
     125   147389771 :       if (EOF == c)
     126       18940 :         break;
     127   147370831 :       if (is_invalid_input_char(c))
     128           0 :         error("invalid input character code %1", int(c));
     129             :       else {
     130   147370831 :         if (length + 1 >= linebufsize) {
     131        9830 :           int newbufsize = linebufsize * 2;
     132        9830 :           if (newbufsize < 0) // integer multiplication wrapped
     133           0 :             fatal("line length exceeds %1 bytes; aborting",
     134           0 :                   linebufsize);
     135        9830 :           char *old_buf = buf;
     136        9830 :           buf = new char[newbufsize];
     137        9830 :           memcpy(buf, old_buf, linebufsize);
     138        9830 :           delete[] old_buf;
     139        9830 :           linebufsize = newbufsize;
     140             :         }
     141   147370831 :         buf[length++] = c;
     142   147370831 :         if ('\n' == c)
     143     8377619 :           break;
     144             :       }
     145   138993212 :     }
     146     8396559 :     if (0 == length)
     147       18940 :       break;
     148     8377619 :     buf[length] = '\0';
     149     8377619 :     char *ptr = buf;
     150     8413705 :     while (csspace(*ptr))
     151       36086 :       ptr++;
     152     8377619 :     if (*ptr != 0 /* nullptr */ && (!recognize_comments || *ptr != '#'))
     153     8172110 :       return true;
     154      205509 :   }
     155       18940 :   return false;
     156             : }
     157             : 
     158          30 : void text_file::error(const char *format,
     159             :                       const errarg &arg1,
     160             :                       const errarg &arg2,
     161             :                       const errarg &arg3)
     162             : {
     163          30 :   if (!silent)
     164           0 :     error_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
     165          30 : }
     166             : 
     167           0 : void text_file::fatal(const char *format,
     168             :                       const errarg &arg1,
     169             :                       const errarg &arg2,
     170             :                       const errarg &arg3)
     171             : {
     172           0 :   if (!silent)
     173           0 :     fatal_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
     174           0 : }
     175             : 
     176    27559191 : static int glyph_to_ucs_codepoint(glyph *g)
     177             : {
     178    27559191 :   const char *nm = glyph_to_name(g);
     179    27559191 :   if (nm != 0 /* nullptr */) {
     180    27430180 :     if ((valid_unicode_code_sequence(nm) != 0 /* nullptr */)
     181    27430180 :         && (strchr(nm, '_') == 0)) {
     182             :       char *ignore;
     183        1550 :       return static_cast<int>(strtol(nm + 1, &ignore, 16));
     184             :     }
     185             :   }
     186    27557641 :   return -1;
     187             : }
     188             : 
     189     7390576 : int glyph_to_unicode(glyph *g)
     190             : {
     191     7390576 :   const char *nm = glyph_to_name(g);
     192     7390576 :   if (nm != 0 /* nullptr */) {
     193             :     // ASCII character?
     194     7363422 :     if (nm[0] == 'c' && nm[1] == 'h' && nm[2] == 'a' && nm[3] == 'r'
     195     7251377 :         && (nm[4] >= '0' && nm[4] <= '9')) {
     196     7251377 :       int n = (nm[4] - '0');
     197     7251377 :       if (nm[5] == '\0')
     198     7361237 :         return n;
     199     7251355 :       if (n > 0 && (nm[5] >= '0' && nm[5] <= '9')) {
     200     7251355 :         n = 10*n + (nm[5] - '0');
     201     7251355 :         if (nm[6] == '\0')
     202     1741365 :           return n;
     203     5509990 :         if (nm[6] >= '0' && nm[6] <= '9') {
     204     5509990 :           n = 10*n + (nm[6] - '0');
     205     5509990 :           if (nm[7] == '\0' && n < 128)
     206     5509990 :             return n;
     207             :         }
     208             :       }
     209             :     }
     210             :     // Unicode character?
     211      112045 :     if (valid_unicode_code_sequence(nm) != 0 /* nullptr */) {
     212             :       char *ignore;
     213       18970 :       return (int)strtol(nm + 1, &ignore, 16);
     214             :     }
     215             :     // If 'nm' is a single letter 'x', the glyph name is '\x'.
     216       93075 :     char buf[] = { '\\', '\0', '\0' };
     217       93075 :     if (nm[1] == '\0') {
     218        2175 :       buf[1] = nm[0];
     219        2175 :       nm = buf;
     220             :     }
     221             :     // groff glyphs that map to Unicode?
     222       93075 :     const char *unicode = glyph_name_to_unicode(nm);
     223       93075 :     if ((unicode != 0 /* nullptr */)
     224       91026 :         && (strchr(unicode, '_') == 0 /* nullptr */)) {
     225             :       char *ignore;
     226       90890 :       return (int)strtol(unicode, &ignore, 16);
     227             :     }
     228             :   }
     229       29339 :   return -1;
     230             : }
     231             : 
     232             : /* font functions */
     233             : 
     234       21902 : font::font(const char *fn) : ligatures(0),
     235             :   kern_hash_table(0 /* nullptr */),
     236             :   space_width(0), special(false), internalname(0 /* nullptr */),
     237             :   slant(0.0), zoom(0), ch_index(0 /* nullptr */), nindices(0),
     238             :   ch(0 /* nullptr */), wch(0 /* nullptr */), ch_used(0), ch_size(0),
     239       21902 :   widths_cache(0 /* nullptr */)
     240             : {
     241       21902 :   filename = new char[strlen(fn) + 1];
     242       21902 :   strcpy(filename, fn);
     243       21902 : }
     244             : 
     245        5876 : font::~font()
     246             : {
     247      269673 :   for (int i = 0; i < ch_used; i++)
     248      266753 :     if (ch[i].special_device_coding)
     249       31933 :       delete[] ch[i].special_device_coding;
     250        2920 :   delete[] ch;
     251        2920 :   delete[] ch_index;
     252        2920 :   if (kern_hash_table) {
     253       55944 :     for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++) {
     254       55833 :       font_kern_list *kerns = kern_hash_table[i];
     255      118113 :       while (kerns) {
     256       62280 :         font_kern_list *tem = kerns;
     257       62280 :         kerns = kerns->next;
     258       62280 :         delete tem;
     259             :       }
     260             :     }
     261         111 :     delete[] kern_hash_table;
     262             :   }
     263        2920 :   delete[] filename;
     264        2920 :   delete[] internalname;
     265        3134 :   while (widths_cache) {
     266         214 :     font_widths_cache *tem = widths_cache;
     267         214 :     widths_cache = widths_cache->next;
     268         214 :     delete tem;
     269             :   }
     270             :   struct font_char_metric *wcp, *nwcp;
     271        2958 :   for (wcp = wch; wcp != 0 /* nullptr */; wcp = nwcp) {
     272          38 :     nwcp = wcp->next;
     273          38 :     if (wcp->special_device_coding)
     274          38 :       delete[] wcp->special_device_coding;
     275          38 :     delete wcp;
     276             :   }
     277        2956 : }
     278             : 
     279     6163818 : static int scale_round(int n, int x, int y)
     280             : {
     281     6163818 :   assert(x >= 0 && y > 0);
     282     6163818 :   int y2 = y/2;
     283     6163818 :   if (0 == x)
     284           0 :     return 0;
     285     6163818 :   if (n >= 0) {
     286     5710933 :     if (n <= (INT_MAX - y2) / x)
     287     5710933 :       return (n * x + y2) / y;
     288           0 :     return int(n * double(x) / double(y) + .5);
     289             :   }
     290             :   else {
     291      452885 :     if (-(unsigned)n <= (-(unsigned)INT_MIN - y2) / x)
     292      452885 :       return (n * x - y2) / y;
     293           0 :     return int(n * double(x) / double(y) - .5);
     294             :   }
     295             : }
     296             : 
     297           0 : static int scale_round(int n, int x, int y, int z)
     298             : {
     299           0 :   assert(x >= 0 && y > 0 && z > 0);
     300           0 :   if (0 == x)
     301           0 :     return 0;
     302           0 :   if (n >= 0)
     303           0 :     return int((n * double(x) / double(y)) * (double(z) / 1000.0) + .5);
     304             :   else
     305           0 :     return int((n * double(x) / double(y)) * (double(z) / 1000.0) - .5);
     306             : }
     307             : 
     308     6932330 : inline int font::scale(int w, int sz)
     309             : {
     310     6932330 :   if (zoom)
     311           0 :     return scale_round(w, sz, unitwidth, zoom);
     312             :   else
     313     6932330 :     return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
     314             : }
     315             : 
     316             : // Returns whether scaling by arguments was successful.  Used for paper
     317             : // size conversions.
     318           0 : bool font::unit_scale(double *value, char unit)
     319             : {
     320             :   // Paper sizes are handled in inches.
     321           0 :   double divisor = 0;
     322           0 :   switch (unit) {
     323           0 :   case 'i':
     324           0 :     divisor = 1;
     325           0 :     break;
     326           0 :   case 'p':
     327           0 :     divisor = 72;
     328           0 :     break;
     329           0 :   case 'P':
     330           0 :     divisor = 6;
     331           0 :     break;
     332           0 :   case 'c':
     333           0 :     divisor = 2.54;
     334           0 :     break;
     335           0 :   default:
     336           0 :     assert(0 == "unit not in [cipP]");
     337             :     break;
     338             :   }
     339           0 :   if (divisor) {
     340           0 :     *value /= divisor;
     341           0 :     return true;
     342             :   }
     343           0 :   return false;
     344             : }
     345             : 
     346       20778 : int font::get_skew(glyph *g, int point_size, int sl)
     347             : {
     348       20778 :   int h = get_height(g, point_size);
     349       20778 :   return int(h * tan((slant + sl) * PI / 180.0) + .5);
     350             : }
     351             : 
     352    14512011 : bool font::contains(glyph *g)
     353             : {
     354    14512011 :   int idx = glyph_to_index(g);
     355    14512011 :   assert(idx >= 0);
     356             :   // Explicitly enumerated glyph?
     357    14512011 :   if (idx < nindices && ch_index[idx] >= 0)
     358    11828112 :     return true;
     359     2683899 :   int uc = glyph_to_ucs_codepoint(g);
     360     2683899 :   if (uc > 0) {
     361         515 :     font_char_metric *wcp = get_font_wchar_metric(uc);
     362         515 :     if (wcp != 0 /* nullptr */)
     363          12 :       return true;
     364             :   }
     365     2683887 :   if (is_unicode) {
     366             :     // Unicode font
     367             :     // ASCII or Unicode character, or groff glyph name that maps to Unicode?
     368     2648631 :     if (glyph_to_unicode(g) >= 0)
     369     2637486 :       return true;
     370             :     // Numbered character?
     371       11145 :     if (glyph_to_number(g) >= 0)
     372        9282 :       return true;
     373             :   }
     374       37119 :   return false;
     375             : }
     376             : 
     377       84600 : bool font::is_special()
     378             : {
     379       84600 :   return special;
     380             : }
     381             : 
     382        2288 : font_widths_cache::font_widths_cache(int ps, int ch_size,
     383        2288 :                                      font_widths_cache *p)
     384        2288 : : next(p), point_size(ps)
     385             : {
     386        2288 :   width = new int[ch_size];
     387      525414 :   for (int i = 0; i < ch_size; i++)
     388      523126 :     width[i] = -1;
     389        2288 : }
     390             : 
     391         428 : font_widths_cache::~font_widths_cache()
     392             : {
     393         214 :   delete[] width;
     394         214 : }
     395             : 
     396        1550 : struct font_char_metric *font::get_font_wchar_metric(int uc)
     397             : {
     398             :   struct font_char_metric *wcp;
     399        2108 :   for (wcp = wch; wcp != 0 /* nullptr */; wcp = wcp->next) {
     400         620 :     if (wcp->code <= uc && uc <= wcp->end_code) {
     401          62 :       return wcp;
     402             :     }
     403             :   }
     404        1488 :   return 0 /* nullptr */;
     405             : }
     406             : 
     407    16885971 : int font::get_width(glyph *g, int point_size)
     408             : {
     409    16885971 :   int idx = glyph_to_index(g);
     410    16885971 :   assert(idx >= 0);
     411             :   int real_size;
     412    16885971 :   if (0 == zoom) // 0 means "don't zoom"
     413    16885971 :     real_size = point_size;
     414             :   else
     415             :   {
     416           0 :     if (point_size <= (INT_MAX - 500) / zoom)
     417           0 :       real_size = (point_size * zoom + 500) / 1000;
     418             :     else
     419           0 :       real_size = int(point_size * double(zoom) / 1000.0 + .5);
     420             :   }
     421    16885971 :   int uc = glyph_to_ucs_codepoint(g);
     422    16885971 :   font_char_metric *wcp = 0 /* nullptr */;
     423    16885971 :   if (uc > 0)
     424         374 :     wcp = get_font_wchar_metric(uc);
     425    16885971 :   if (wcp != 0 && !(idx < nindices && ch_index[idx] >= 0)) {
     426          22 :     return scale(wcp->width, point_size);
     427             :   }
     428    16885949 :   if (idx < nindices && ch_index[idx] >= 0) {
     429             :     // Explicitly enumerated glyph
     430    13299342 :     int i = ch_index[idx];
     431    13299342 :     if (real_size == unitwidth || font::use_unscaled_charwidths)
     432      438919 :       return ch[i].width;
     433             : 
     434    12860423 :     if (!widths_cache)
     435         688 :       widths_cache = new font_widths_cache(real_size, ch_size);
     436    12859735 :     else if (widths_cache->point_size != real_size) {
     437             :       font_widths_cache **p;
     438      204934 :       for (p = &widths_cache; *p; p = &(*p)->next)
     439      203334 :         if ((*p)->point_size == real_size)
     440        4624 :           break;
     441        6224 :       if (*p) {
     442        4624 :         font_widths_cache *tem = *p;
     443        4624 :         *p = (*p)->next;
     444        4624 :         tem->next = widths_cache;
     445        4624 :         widths_cache = tem;
     446             :       }
     447             :       else
     448        1600 :         widths_cache = new font_widths_cache(real_size, ch_size,
     449        1600 :                                              widths_cache);
     450             :     }
     451    12860423 :     int &w = widths_cache->width[i];
     452    12860423 :     if (w < 0)
     453       65205 :       w = scale(ch[i].width, point_size);
     454    12860423 :     return w;
     455             :   }
     456     3586607 :   if (is_unicode) {
     457             :     // Unicode font
     458     3586607 :     int width = 24; // XXX: Add a request to override this.
     459     3586607 :     int w = wcwidth(get_code(g));
     460     3586607 :     if (w > 1 && !font::use_unscaled_charwidths)
     461         169 :       width *= w;
     462     3586607 :     if (real_size == unitwidth || font::use_unscaled_charwidths)
     463     3586607 :       return width;
     464             :     else
     465           0 :       return scale(width, point_size);
     466             :   }
     467           0 :   assert(0 == "glyph is not indexed and device lacks Unicode support");
     468             :   abort(); // -Wreturn-type
     469             : }
     470             : 
     471     2722069 : int font::get_height(glyph *g, int point_size)
     472             : {
     473     2722069 :   int idx = glyph_to_index(g);
     474     2722069 :   assert(idx >= 0);
     475     2722069 :   if (idx < nindices && ch_index[idx] >= 0) {
     476             :     // Explicitly enumerated glyph
     477     1636828 :     return scale(ch[ch_index[idx]].height, point_size);
     478             :   }
     479     1085241 :   int uc = glyph_to_ucs_codepoint(g);
     480     1085241 :   font_char_metric *wcp = 0 /* nullptr */;
     481     1085241 :   if (uc > 0)
     482         102 :     wcp = get_font_wchar_metric(uc);
     483     1085241 :   if (wcp != 0 /* nullptr */) {
     484           8 :     return scale(wcp->height, point_size);
     485             :   }
     486     1085233 :   if (is_unicode) {
     487             :     // Unicode font
     488     1085233 :     return 0;
     489             :   }
     490           0 :   assert(0 == "glyph is not indexed and device lacks Unicode support");
     491             :   abort(); // -Wreturn-type
     492             : }
     493             : 
     494     2701291 : int font::get_depth(glyph *g, int point_size)
     495             : {
     496     2701291 :   int idx = glyph_to_index(g);
     497     2701291 :   assert(idx >= 0);
     498     2701291 :   if (idx < nindices && ch_index[idx] >= 0) {
     499             :     // Explicitly enumerated glyph
     500     1621173 :     return scale(ch[ch_index[idx]].depth, point_size);
     501             :   }
     502     1080118 :   int uc = glyph_to_ucs_codepoint(g);
     503     1080118 :   font_char_metric *wcp = 0 /* nullptr */;
     504     1080118 :   if (uc > 0)
     505          91 :     wcp = get_font_wchar_metric(uc);
     506     1080118 :   if (wcp != 0 /* nullptr */) {
     507           8 :     return scale(wcp->depth, point_size);
     508             :   }
     509     1080110 :   if (is_unicode) {
     510             :     // Unicode font
     511     1080110 :     return 0;
     512             :   }
     513           0 :   assert(0 == "glyph is not indexed and device lacks Unicode support");
     514             :   abort(); // -Wreturn-type
     515             : }
     516             : 
     517     2539271 : int font::get_italic_correction(glyph *g, int point_size)
     518             : {
     519     2539271 :   int idx = glyph_to_index(g);
     520     2539271 :   assert(idx >= 0);
     521     2539271 :   if (idx < nindices && ch_index[idx] >= 0) {
     522             :     // Explicitly enumerated glyph
     523     1493217 :     return scale(ch[ch_index[idx]].italic_correction, point_size);
     524             :   }
     525     1046054 :   int uc = glyph_to_ucs_codepoint(g);
     526     1046054 :   font_char_metric *wcp = 0 /* nullptr */;
     527     1046054 :   if (uc > 0)
     528          78 :     wcp = get_font_wchar_metric(uc);
     529     1046054 :   if (wcp != 0 /* nullptr */) {
     530           8 :     return scale(wcp->italic_correction, point_size);
     531             :   }
     532     1046046 :   if (is_unicode) {
     533             :     // Unicode font
     534     1046046 :     return 0;
     535             :   }
     536           0 :   assert(0 == "glyph is not indexed and device lacks Unicode support");
     537             :   abort(); // -Wreturn-type
     538             : }
     539             : 
     540       32205 : int font::get_left_italic_correction(glyph *g, int point_size)
     541             : {
     542       32205 :   int idx = glyph_to_index(g);
     543       32205 :   assert(idx >= 0 /* nullptr */);
     544       32205 :   if (idx < nindices && ch_index[idx] >= 0) {
     545             :     // Explicitly enumerated glyph
     546       22570 :     return scale(ch[ch_index[idx]].pre_math_space, point_size);
     547             :   }
     548        9635 :   int uc = glyph_to_ucs_codepoint(g);
     549        9635 :   font_char_metric *wcp = 0 /* nullptr */;
     550        9635 :   if (uc > 0 )
     551           0 :     wcp = get_font_wchar_metric(uc);
     552        9635 :   if (wcp != 0 /* nullptr */) {
     553           0 :     return scale(wcp->pre_math_space, point_size);
     554             :   }
     555        9635 :   if (is_unicode) {
     556             :     // Unicode font
     557        9635 :     return 0;
     558             :   }
     559           0 :   assert(0 == "glyph is not indexed and device lacks Unicode support");
     560             :   abort(); // -Wreturn-type
     561             : }
     562             : 
     563       21035 : int font::get_subscript_correction(glyph *g, int point_size)
     564             : {
     565       21035 :   int idx = glyph_to_index(g);
     566       21035 :   assert(idx >= 0);
     567       21035 :   if (idx < nindices && ch_index[idx] >= 0) {
     568             :     // Explicitly enumerated glyph
     569       15912 :     return scale(ch[ch_index[idx]].subscript_correction, point_size);
     570             :   }
     571        5123 :   int uc = glyph_to_ucs_codepoint(g);
     572        5123 :   font_char_metric *wcp = 0 /* nullptr */;
     573        5123 :   if (uc > 0)
     574          11 :     wcp = get_font_wchar_metric(uc);
     575        5123 :   if (wcp != 0 /* nullptr */) {
     576           0 :     return scale(wcp->subscript_correction, point_size);
     577             :   }
     578        5123 :   if (is_unicode) {
     579             :     // Unicode font
     580        5123 :     return 0;
     581             :   }
     582           0 :   assert(0 == "glyph is not indexed and device lacks Unicode support");
     583             :   abort(); // -Wreturn-type
     584             : }
     585             : 
     586           0 : void font::set_zoom(int factor)
     587             : {
     588           0 :   assert(factor >= 0);
     589           0 :   if (factor == 1000)
     590           0 :     zoom = 0;
     591             :   else
     592           0 :     zoom = factor;
     593           0 : }
     594             : 
     595      136167 : int font::get_zoom()
     596             : {
     597      136167 :   return zoom;
     598             : }
     599             : 
     600     1591979 : int font::get_space_width(int point_size)
     601             : {
     602     1591979 :   return scale(space_width, point_size);
     603             : }
     604             : 
     605     4278019 : font_kern_list::font_kern_list(glyph *g1, glyph *g2, int n, font_kern_list *p)
     606     4278019 : : glyph1(g1), glyph2(g2), amount(n), next(p)
     607             : {
     608     4278019 : }
     609             : 
     610    13134674 : inline int font::hash_kern(glyph *g1, glyph *g2)
     611             : {
     612    13134674 :   int n = ((glyph_to_index(g1) << 10) + glyph_to_index(g2))
     613    13134674 :           % KERN_HASH_TABLE_SIZE;
     614    13134674 :   return n < 0 ? -n : n;
     615             : }
     616             : 
     617     4278019 : void font::add_kern(glyph *g1, glyph *g2, int amount)
     618             : {
     619     4278019 :   if (!kern_hash_table) {
     620        5874 :     kern_hash_table = new font_kern_list *[int(KERN_HASH_TABLE_SIZE)];
     621     2960496 :     for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++)
     622     2954622 :       kern_hash_table[i] = 0;
     623             :   }
     624     4278019 :   font_kern_list **p = kern_hash_table + hash_kern(g1, g2);
     625     4278019 :   *p = new font_kern_list(g1, g2, amount, *p);
     626     4278019 : }
     627             : 
     628    10448388 : int font::get_kern(glyph *g1, glyph *g2, int point_size)
     629             : {
     630    10448388 :   if (kern_hash_table) {
     631    15059991 :     for (font_kern_list *p = kern_hash_table[hash_kern(g1, g2)]; p;
     632     6203336 :          p = p->next)
     633     6688736 :       if (g1 == p->glyph1 && g2 == p->glyph2)
     634      485400 :         return scale(p->amount, point_size);
     635             :   }
     636     9962988 :   return 0;
     637             : }
     638             : 
     639      153805 : bool font::has_ligature(int mask)
     640             : {
     641      153805 :   return (bool) (mask & ligatures);
     642             : }
     643             : 
     644      183183 : int font::get_character_type(glyph *g)
     645             : {
     646      183183 :   int idx = glyph_to_index(g);
     647      183183 :   assert(idx >= 0);
     648      183183 :   if (idx < nindices && ch_index[idx] >= 0) {
     649             :     // Explicitly enumerated glyph
     650      139475 :     return ch[ch_index[idx]].type;
     651             :   }
     652       43708 :   int uc = glyph_to_ucs_codepoint(g);
     653       43708 :   font_char_metric *wcp = 0 /* nullptr */;
     654       43708 :   if (uc > 0)
     655          13 :     wcp = get_font_wchar_metric(uc);
     656       43708 :   if (wcp != 0 /* nullptr */) {
     657           0 :     return wcp->type;
     658             :   }
     659       43708 :   if (is_unicode) {
     660             :     // Unicode font
     661       43708 :     return 0;
     662             :   }
     663           0 :   assert(0 == "glyph is not indexed and device lacks Unicode support");
     664             :   abort(); // -Wreturn-type
     665             : }
     666             : 
     667     5156757 : int font::get_code(glyph *g)
     668             : {
     669     5156757 :   int idx = glyph_to_index(g);
     670     5156757 :   assert(idx >= 0);
     671     5156757 :   if (idx < nindices && ch_index[idx] >= 0) {
     672             :     // Explicitly enumerated glyph
     673      437315 :     return ch[ch_index[idx]].code;
     674             :   }
     675     4719442 :   int uc = glyph_to_ucs_codepoint(g);
     676     4719442 :   font_char_metric *wcp = 0 /* nullptr */;
     677     4719442 :   if (uc > 0)
     678         366 :     wcp = get_font_wchar_metric(uc);
     679     4719442 :   if (wcp != 0 /* nullptr */) {
     680           4 :     return uc;
     681             :   }
     682     4719438 :   if (is_unicode) {
     683             :     // Unicode font
     684             :     // ASCII or Unicode character, or groff glyph name that maps to Unicode?
     685     4719438 :     int uni = glyph_to_unicode(g);
     686     4719438 :     if (uni >= 0)
     687     4701574 :       return uni;
     688             :     // Numbered character?
     689       17864 :     int n = glyph_to_number(g);
     690       17864 :     if (n >= 0)
     691       17864 :       return n;
     692             :   }
     693             :   // The caller must check 'contains(g)' before calling get_code(g).
     694           0 :   assert(0 == "glyph is not indexed and device lacks Unicode support");
     695             :   abort(); // -Wreturn-type
     696             : }
     697             : 
     698       31521 : const char *font::get_filename()
     699             : {
     700       31521 :   return filename;
     701             : }
     702             : 
     703      397174 : const char *font::get_internal_name()
     704             : {
     705      397174 :   return internalname;
     706             : }
     707             : 
     708          68 : const char *font::get_special_device_encoding(glyph *g)
     709             : {
     710          68 :   int idx = glyph_to_index(g);
     711          68 :   assert(idx >= 0);
     712          68 :   if (idx < nindices && ch_index[idx] >= 0) {
     713             :     // Explicitly enumerated glyph
     714          68 :     return ch[ch_index[idx]].special_device_coding;
     715             :   }
     716           0 :   int uc = glyph_to_ucs_codepoint(g);
     717           0 :   font_char_metric *wcp = 0 /* nullptr */;
     718           0 :   if (uc > 0)
     719           0 :     wcp = get_font_wchar_metric(uc);
     720           0 :   if (wcp != 0 /* nullptr */)
     721           0 :     return wcp->special_device_coding;
     722           0 :   if (is_unicode) {
     723             :     // Unicode font
     724           0 :     return 0;
     725             :   }
     726           0 :   assert(0 == "glyph is not indexed and device lacks Unicode support");
     727             :   abort(); // -Wreturn-type
     728             : }
     729             : 
     730           0 : const char *font::get_image_generator()
     731             : {
     732           0 :   return image_generator;
     733             : }
     734             : 
     735       33831 : void font::alloc_ch_index(int idx)
     736             : {
     737       33831 :   if (0 == nindices) {
     738       14205 :     nindices = 128;
     739       14205 :     if (idx >= nindices)
     740       11960 :       nindices = idx + 10;
     741       14205 :     ch_index = new int[nindices];
     742     8148147 :     for (int i = 0; i < nindices; i++)
     743     8133942 :       ch_index[i] = -1;
     744             :   }
     745             :   else {
     746       19626 :     int old_nindices = nindices;
     747       19626 :     nindices *= 2;
     748       19626 :     if (idx >= nindices)
     749        1014 :       nindices = idx + 10;
     750       19626 :     int *old_ch_index = ch_index;
     751       19626 :     ch_index = new int[nindices];
     752       19626 :     memcpy(ch_index, old_ch_index, sizeof(int) * old_nindices);
     753    12373148 :     for (int i = old_nindices; i < nindices; i++)
     754    12353522 :       ch_index[i] = -1;
     755       19626 :     delete[] old_ch_index;
     756             :   }
     757       33831 : }
     758             : 
     759       67655 : void font::extend_ch()
     760             : {
     761       67655 :   if (0 /* nullptr */ == ch)
     762       14205 :     ch = new font_char_metric[ch_size = 16];
     763             :   else {
     764       53450 :     int old_ch_size = ch_size;
     765       53450 :     ch_size *= 2;
     766       53450 :     font_char_metric *old_ch = ch;
     767       53450 :     ch = new font_char_metric[ch_size];
     768       53450 :     memcpy(ch, old_ch, old_ch_size * sizeof(font_char_metric));
     769       53450 :     delete[] old_ch;
     770             :   }
     771       67655 : }
     772             : 
     773       15049 : void font::compact()
     774             : {
     775             :   int i;
     776     8311003 :   for (i = nindices - 1; i >= 0; i--)
     777     8310159 :     if (ch_index[i] >= 0)
     778       14205 :       break;
     779       15049 :   i++;
     780       15049 :   if (i < nindices) {
     781       14205 :     int *old_ch_index = ch_index;
     782       14205 :     ch_index = new int[i];
     783       14205 :     memcpy(ch_index, old_ch_index, i*sizeof(int));
     784       14205 :     delete[] old_ch_index;
     785       14205 :     nindices = i;
     786             :   }
     787       15049 :   if (ch_used < ch_size) {
     788       14205 :     font_char_metric *old_ch = ch;
     789       14205 :     ch = new font_char_metric[ch_used];
     790       14205 :     memcpy(ch, old_ch, ch_used*sizeof(font_char_metric));
     791       14205 :     delete[] old_ch;
     792       14205 :     ch_size = ch_used;
     793             :   }
     794       15049 : }
     795             : 
     796     3284662 : void font::add_entry(glyph *g, const font_char_metric &metric)
     797             : {
     798     3284662 :   int idx = glyph_to_index(g);
     799     3284662 :   assert(idx >= 0);
     800     3284662 :   if (idx >= nindices)
     801       28287 :     alloc_ch_index(idx);
     802     3284662 :   assert(idx < nindices);
     803     3284662 :   if (ch_used + 1 >= ch_size)
     804       67655 :     extend_ch();
     805     3284662 :   assert(ch_used + 1 < ch_size);
     806     3284662 :   ch_index[idx] = ch_used;
     807     3284662 :   ch[ch_used++] = metric;
     808     3284662 : }
     809             : 
     810     3489775 : void font::copy_entry(glyph *new_glyph, glyph *old_glyph)
     811             : {
     812     3489775 :   int new_index = glyph_to_index(new_glyph);
     813     3489775 :   int old_index = glyph_to_index(old_glyph);
     814     3489775 :   assert(new_index >= 0 && old_index >= 0 && old_index < nindices);
     815     3489775 :   if (new_index >= nindices)
     816        5544 :     alloc_ch_index(new_index);
     817     3489775 :   ch_index[new_index] = ch_index[old_index];
     818     3489775 : }
     819             : 
     820       19018 : font *font::load_font(const char *fn, bool load_header_only)
     821             : {
     822       19018 :   font *f = new font(fn);
     823       19018 :   if (!f->load(load_header_only)) {
     824          36 :     delete f;
     825          36 :     return 0 /* nullptr */;
     826             :   }
     827       18982 :   return f;
     828             : }
     829             : 
     830       18307 : static char *trim_arg(char *p)
     831             : {
     832       18307 :   if (0 /* nullptr */ == p)
     833         680 :     return 0 /* nullptr */;
     834       17726 :   while (csspace(*p))
     835          99 :     p++;
     836       17627 :   char *q = strchr(p, '\0');
     837       17635 :   while (q > p && csspace(q[-1]))
     838           8 :     q--;
     839       17627 :   *q = '\0';
     840       17627 :   return p;
     841             : }
     842             : 
     843         551 : bool font::scan_papersize(const char *p, const char **size,
     844             :                           double *length, double *width)
     845             : {
     846             :   double l, w;
     847             :   char lu[2], wu[2];
     848         551 :   const char *pp = p;
     849         551 :   bool attempt_file_open = true;
     850             :   char line[255];
     851         551 : again:
     852         551 :   if (csdigit(*pp)) {
     853           0 :     if (sscanf(pp, "%lf%1[ipPc],%lf%1[ipPc]", &l, lu, &w, wu) == 4
     854           0 :         && l > 0 && w > 0
     855           0 :         && unit_scale(&l, lu[0]) && unit_scale(&w, wu[0])) {
     856           0 :       if (length)
     857           0 :         *length = l;
     858           0 :       if (width)
     859           0 :         *width = w;
     860           0 :       if (size)
     861           0 :         *size = "custom";
     862           0 :       return true;
     863             :     }
     864             :   }
     865             :   else {
     866             :     int i;
     867       18183 :     for (i = 0; i < NUM_PAPERSIZES; i++)
     868       18183 :       if (strcasecmp(papersizes[i].name, pp) == 0) {
     869         551 :         if (length)
     870         551 :           *length = papersizes[i].length;
     871         551 :         if (width)
     872         551 :           *width = papersizes[i].width;
     873         551 :         if (size)
     874         534 :           *size = papersizes[i].name;
     875         551 :         return true;
     876             :       }
     877           0 :     if (attempt_file_open) {
     878           0 :       FILE *fp = fopen(p, "r");
     879           0 :       if (fp != 0 /* nullptr */) {
     880           0 :         if (fgets(line, 254, fp)) {
     881             :           // Don't recurse on file names.
     882           0 :           attempt_file_open = false;
     883           0 :           char *linep = strchr(line, '\0');
     884             :           // skip final newline, if any
     885           0 :           if (*(--linep) == '\n')
     886           0 :             *linep = '\0';
     887           0 :           pp = line;
     888             :         }
     889           0 :         fclose(fp);
     890           0 :         goto again;
     891             :       }
     892             :     }
     893             :   }
     894           0 :   return false;
     895             : }
     896             : 
     897       20482 : bool font::load(bool load_header_only)
     898             : {
     899             :   char *path;
     900       20482 :   FILE *fp = open_file(filename, &path);
     901       20482 :   if (0 /* nullptr */ == fp)
     902           6 :     return false;
     903       40952 :   text_file t(fp, path);
     904       20476 :   t.silent = load_header_only;
     905       20476 :   char *p = 0 /* nullptr */;
     906       20476 :   bool saw_name_directive = false;
     907      113464 :   while (t.next_line()) {
     908      113434 :     p = strtok(t.buf, WS);
     909      113434 :     if (strcmp(p, "name") == 0) {
     910       20446 :       p = strtok(0 /* nullptr */, WS);
     911       20446 :       if (0 /* nullptr */ == p) {
     912           0 :         t.error("'name' directive requires an argument");
     913           0 :         return false;
     914             :       }
     915       20446 :       if (strcmp(p, filename) != 0) {
     916           0 :         t.error("font description file name '%1' does not match 'name'"
     917           0 :                 " argument '%2'", filename, p);
     918           0 :         return false;
     919             :       }
     920       20446 :       saw_name_directive = true;
     921             :     }
     922       92988 :     else if (strcmp(p, "spacewidth") == 0) {
     923       20446 :       p = strtok(0 /* nullptr */, WS);
     924             :       int n;
     925       20446 :       if (0 /* nullptr */ == p) {
     926           0 :         t.error("missing argument to 'spacewidth' directive");
     927           0 :         return false;
     928             :       }
     929       20446 :       if (sscanf(p, "%d", &n) != 1) {
     930           0 :         t.error("invalid argument '%1' to 'spacewidth' directive", p);
     931           0 :         return false;
     932             :       }
     933       20446 :       if (n <= 0) {
     934           0 :         t.error("'spacewidth' argument '%1' out of range", n);
     935           0 :         return false;
     936             :       }
     937       20446 :       space_width = n;
     938             :     }
     939       72542 :     else if (strcmp(p, "slant") == 0) {
     940        6385 :       p = strtok(0 /* nullptr */, WS);
     941             :       double n;
     942        6385 :       if (0 /* nullptr */ == p) {
     943           0 :         t.error("missing argument to 'slant' directive");
     944           0 :         return false;
     945             :       }
     946        6385 :       if (sscanf(p, "%lf", &n) != 1) {
     947           0 :         t.error("invalid argument '%1' to 'slant' directive", p);
     948           0 :         return false;
     949             :       }
     950        6385 :       if (n >= 90.0 || n <= -90.0) {
     951           0 :         t.error("'slant' directive argument '%1' out of range", n);
     952           0 :         return false;
     953             :       }
     954        6385 :       slant = n;
     955             :     }
     956       66157 :     else if (strcmp(p, "ligatures") == 0) {
     957             :       for (;;) {
     958       34164 :         p = strtok(0 /* nullptr */, WS);
     959       34164 :         if (0 /* nullptr */ == p || strcmp(p, "0") == 0)
     960             :           break;
     961       22925 :         if (strcmp(p, "ff") == 0)
     962         149 :           ligatures |= LIG_ff;
     963       22776 :         else if (strcmp(p, "fi") == 0)
     964       11239 :           ligatures |= LIG_fi;
     965       11537 :         else if (strcmp(p, "fl") == 0)
     966       11239 :           ligatures |= LIG_fl;
     967         298 :         else if (strcmp(p, "ffi") == 0)
     968         149 :           ligatures |= LIG_ffi;
     969         149 :         else if (strcmp(p, "ffl") == 0)
     970         149 :           ligatures |= LIG_ffl;
     971             :         else {
     972           0 :           t.error("unrecognized ligature '%1'", p);
     973           0 :           return false;
     974             :         }
     975             :       }
     976             :     }
     977       54918 :     else if (strcmp(p, "internalname") == 0) {
     978       17179 :       p = strtok(0 /* nullptr */, WS);
     979       17179 :       if (0 /* nullptr */ == p) {
     980           0 :         t.error("missing argument to 'internalname' directive");
     981           0 :         return false;
     982             :       }
     983       17179 :       internalname = new char[strlen(p) + 1];
     984       17179 :       strcpy(internalname, p);
     985             :     }
     986       37739 :     else if (strcmp(p, "special") == 0) {
     987         967 :       special = true;
     988             :     }
     989       36772 :     else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0 &&
     990       17468 :              strcmp(p, "charset-range") != 0) {
     991       16326 :       char *directive = p;
     992       16326 :       p = strtok(0 /* nullptr */, "\n");
     993       16326 :       handle_unknown_font_command(directive, trim_arg(p), t.path,
     994       16326 :                                   t.lineno);
     995             :     }
     996             :     else
     997             :       break;
     998             :   }
     999       20476 :   bool saw_charset_directive = false;
    1000       20476 :   char *directive = p;
    1001       20476 :   t.recognize_comments = false;
    1002       41665 :   while (directive) {
    1003       26616 :     if (strcmp(directive, "kernpairs") == 0) {
    1004       11251 :       if (load_header_only)
    1005        5377 :         return true;
    1006             :       for (;;) {
    1007     4283893 :         if (!t.next_line()) {
    1008          13 :           directive = 0 /* nullptr */;
    1009        5874 :           break;
    1010             :         }
    1011     4283880 :         char *c1 = strtok(t.buf, WS);
    1012     4283880 :         if (0 /* nullptr */ == c1)
    1013           0 :           continue;
    1014     4283880 :         char *c2 = strtok(0 /* nullptr */, WS);
    1015     4283880 :         if (0 /* nullptr */ == c2) {
    1016        5861 :           directive = c1;
    1017        5861 :           break;
    1018             :         }
    1019     4278019 :         p = strtok(0 /* nullptr */, WS);
    1020     4278019 :         if (0 /* nullptr */ == p) {
    1021           0 :           t.error("missing kern amount for kerning pair '%1 %2'", c1,
    1022           0 :                   c2);
    1023           0 :           return false;
    1024             :         }
    1025             :         int n;
    1026     4278019 :         if (sscanf(p, "%d", &n) != 1) {
    1027           0 :           t.error("invalid kern amount '%1' for kerning pair '%2 %3'",
    1028           0 :                   p, c1, c2);
    1029           0 :           return false;
    1030             :         }
    1031     4278019 :         glyph *g1 = name_to_glyph(c1);
    1032     4278019 :         glyph *g2 = name_to_glyph(c2);
    1033     4278019 :         add_kern(g1, g2, n);
    1034     4278019 :       }
    1035             :     }
    1036             :     // TODO: Rename this directive to "ranged-charset".
    1037       15365 :     else if (strcmp(directive, "charset-range") == 0) {
    1038        1142 :       if (load_header_only)
    1039          32 :         return true;
    1040        1110 :       saw_charset_directive = true;
    1041        1110 :       bool had_range = false;
    1042             :       for (;;) {
    1043       19164 :         if (!t.next_line()) {
    1044         828 :           directive = 0 /* nullptr */;
    1045         828 :           break;
    1046             :         }
    1047       18336 :         char *nm = strtok(t.buf, WS);
    1048       18336 :         assert(nm != 0 /* nullptr */);
    1049       18336 :         p = strtok(0 /* nullptr */, WS);
    1050       18336 :         if (0 /* nullptr */ == p) {
    1051         282 :           directive = nm;
    1052         282 :           break;
    1053             :         }
    1054       18054 :         unsigned int start_code = 0;
    1055       18054 :         unsigned int end_code = 0;
    1056       18054 :         int nrange = sscanf(nm, "u%X..u%X", &start_code, &end_code);
    1057             :         // TODO: Check for backwards range: end_code < start_code.
    1058       18054 :         if (2 == nrange) {
    1059       18054 :           had_range = true;
    1060       18054 :           font_char_metric *wcp = new font_char_metric;
    1061       18054 :           wcp->code = start_code;
    1062       18054 :           wcp->end_code = end_code;
    1063       18054 :           wcp->height = 0;
    1064       18054 :           wcp->depth = 0;
    1065       18054 :           wcp->pre_math_space = 0;
    1066       18054 :           wcp->italic_correction = 0;
    1067       18054 :           wcp->subscript_correction = 0;
    1068       18054 :           int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
    1069             :                               &wcp->width, &wcp->height, &wcp->depth,
    1070             :                               &wcp->italic_correction,
    1071             :                               &wcp->pre_math_space,
    1072             :                               &wcp->subscript_correction);
    1073       18054 :           if (nparms < 1) {
    1074           0 :             t.error("missing or invalid width for character range '%1'",
    1075           0 :                     nm);
    1076           0 :             return false;
    1077             :           }
    1078       18054 :           p = strtok(0 /* nullptr */, WS);
    1079       18054 :           if (0 /* nullptr */ == p) {
    1080           0 :             t.error("missing character type for '%1'", nm);
    1081           0 :             return false;
    1082             :           }
    1083             :           int type;
    1084       18054 :           if (sscanf(p, "%d", &type) != 1) {
    1085           0 :             t.error("invalid character type for '%1'", nm);
    1086           0 :             return false;
    1087             :           }
    1088       18054 :           if ((type < 0) || (type > 255)) {
    1089           0 :             t.error("character type '%1' out of range for '%2'", type,
    1090           0 :                     nm);
    1091           0 :             return false;
    1092             :           }
    1093       18054 :           wcp->type = type;
    1094             : 
    1095       18054 :           p = strtok(0 /* nullptr */, WS);
    1096       18054 :           if ((0 /* nullptr */ == p) || (strcmp(p, "--") == 0)) {
    1097           0 :             wcp->special_device_coding = 0 /* nullptr */;
    1098             :           }
    1099             :           else {
    1100       18054 :             wcp->special_device_coding = new char[strlen(p) + 1];
    1101       18054 :             strcpy(wcp->special_device_coding, p);
    1102             :           }
    1103       18054 :           wcp->next = wch;
    1104       18054 :           wch = wcp;
    1105       18054 :           p = 0 /* nullptr */;
    1106             :         }
    1107       18054 :       }
    1108             :       // TODO: Parallelize wording of "charset"'s diagnostic.
    1109        1110 :       if (!had_range) {
    1110           0 :         t.error("no glyphs described after 'charset-range' directive");
    1111           0 :         return false;
    1112             :       }
    1113             :     }
    1114       14223 :     else if (strcmp(directive, "charset") == 0) {
    1115       14209 :       if (load_header_only)
    1116           4 :         return true;
    1117       14205 :       saw_charset_directive = true;
    1118       14205 :       glyph *last_glyph = 0 /* nullptr */;
    1119             :       for (;;) {
    1120     3735768 :         if (!t.next_line()) {
    1121       14192 :           directive = 0 /* nullptr */;
    1122       14192 :           break;
    1123             :         }
    1124     3721576 :         char *nm = strtok(t.buf, WS);
    1125     3721576 :         assert(nm != 0 /* nullptr */);
    1126     3721576 :         p = strtok(0 /* nullptr */, WS);
    1127     3721576 :         if (0 /* nullptr */ == p) {
    1128          13 :           directive = nm;
    1129          13 :           break;
    1130             :         }
    1131     3721563 :         if (p[0] == '"') {
    1132      436901 :           if (0 /* nullptr */ == last_glyph) {
    1133           0 :             t.error("the first entry ('%1') in 'charset' subsection"
    1134           0 :                     " cannot be an alias", nm);
    1135           0 :             return false;
    1136             :           }
    1137      436901 :           if (strcmp(nm, "---") == 0) {
    1138           0 :             t.error("an unnamed character ('---') cannot be an alias");
    1139           0 :             return false;
    1140             :           }
    1141      436901 :           glyph *g = name_to_glyph(nm);
    1142      436901 :           copy_entry(g, last_glyph);
    1143             :         }
    1144             :         else {
    1145             :           font_char_metric metric;
    1146     3284662 :           metric.height = 0;
    1147     3284662 :           metric.depth = 0;
    1148     3284662 :           metric.pre_math_space = 0;
    1149     3284662 :           metric.italic_correction = 0;
    1150     3284662 :           metric.subscript_correction = 0;
    1151     3284662 :           int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
    1152             :                               &metric.width, &metric.height,
    1153             :                               &metric.depth,
    1154             :                               &metric.italic_correction,
    1155             :                               &metric.pre_math_space,
    1156             :                               &metric.subscript_correction);
    1157     3284662 :           if (nparms < 1) {
    1158           0 :             t.error("missing or invalid width for glyph '%1'", nm);
    1159           0 :             return false;
    1160             :           }
    1161     3284662 :           p = strtok(0 /* nullptr */, WS);
    1162     3284662 :           if (0 /* nullptr */ == p) {
    1163           0 :             t.error("missing character type for '%1'", nm);
    1164           0 :             return false;
    1165             :           }
    1166             :           int type;
    1167     3284662 :           if (sscanf(p, "%d", &type) != 1) {
    1168           0 :             t.error("invalid character type for '%1'", nm);
    1169           0 :             return false;
    1170             :           }
    1171     3284662 :           if (type < 0 || type > 255) {
    1172           0 :             t.error("character type '%1' out of range for '%2'", type,
    1173           0 :                     nm);
    1174           0 :             return false;
    1175             :           }
    1176     3284662 :           metric.type = type;
    1177     3284662 :           p = strtok(0 /* nullptr */, WS);
    1178     3284662 :           if (0 /* nullptr */ == p) {
    1179           0 :             t.error("missing index for '%1'", nm);
    1180           0 :             return false;
    1181             :           }
    1182             :           char *ptr;
    1183             :           long index;
    1184     3284662 :           errno = 0;
    1185     3284662 :           index = strtol(p, &ptr, 0);
    1186     3284662 :           if (ptr == p) {
    1187           0 :             t.error("invalid index '%1' for character '%2'", p, nm);
    1188           0 :             return false;
    1189             :           }
    1190             :           if (INT_MAX != LONG_MAX) {
    1191     3284662 :             if ((index > INT_MAX) || (index < INT_MIN)) {
    1192             :               // This is a fib since INT_MIN's absolute value is one
    1193             :               // greater than INT_MAX's (on two's complement machines),
    1194             :               // but we can pass 3 arguments at most to the error()
    1195             :               // function.  Also, 31 bits ought to be enough for anyone.
    1196           0 :               t.error("index %1 for character '%2' is out of range;"
    1197           0 :                       " must be within +/-%3", p, nm, INT_MAX);
    1198           0 :               return false;
    1199             :             }
    1200             :           }
    1201     3284662 :           if (errno != 0) {
    1202           0 :             t.error("cannot convert index '%1' to integer for character"
    1203           0 :                     " '%2': %3", p, nm, strerror(errno));
    1204           0 :             return false;
    1205             :           }
    1206     3284662 :           metric.code = static_cast<int>(index);
    1207     3284662 :           p = strtok(0 /* nullptr */, WS);
    1208     3284662 :           if ((0 /* nullptr */ == p) || (strcmp(p, "--") == 0)) {
    1209     1632360 :             metric.special_device_coding = 0;
    1210             :           }
    1211             :           else {
    1212     1652302 :             char *nam = new char[strlen(p) + 1];
    1213     1652302 :             strcpy(nam, p);
    1214     1652302 :             metric.special_device_coding = nam;
    1215             :           }
    1216     3284662 :           if (strcmp(nm, "---") == 0) {
    1217      231788 :             last_glyph = number_to_glyph(metric.code);
    1218      231788 :             add_entry(last_glyph, metric);
    1219             :           }
    1220             :           else {
    1221     3052874 :             last_glyph = name_to_glyph(nm);
    1222     3052874 :             add_entry(last_glyph, metric);
    1223     3052874 :             copy_entry(number_to_glyph(metric.code), last_glyph);
    1224             :           }
    1225             :         }
    1226     3721563 :       }
    1227       14205 :       if (0 /* nullptr */ == last_glyph) {
    1228           0 :         t.error("no glyphs defined in font description");
    1229           0 :         return false;
    1230             :       }
    1231             :     }
    1232             :     else {
    1233          14 :       t.error("unrecognized font description directive '%1' (missing"
    1234          14 :               " 'kernpairs' or 'charset'?)", directive);
    1235          14 :       return false;
    1236             :     }
    1237             :   }
    1238       15049 :   compact();
    1239       15049 :   t.lineno = 0;
    1240       15049 :   if (!saw_name_directive) {
    1241          16 :     t.error("font description 'name' directive missing");
    1242          16 :     return false;
    1243             :   }
    1244       15033 :   if (!is_unicode && !saw_charset_directive) {
    1245           0 :     t.error("font description 'charset' subsection missing");
    1246           0 :     return false;
    1247             :   }
    1248       15033 :   if (space_width == 0) {
    1249           0 :     t.error("font description 'spacewidth' directive missing");
    1250             :     // _Don't_ return false; compute a typical one for Western glyphs.
    1251           0 :     if (zoom)
    1252           0 :       space_width = scale_round(unitwidth, res, 72 * 3 * sizescale,
    1253             :                                 zoom);
    1254             :     else
    1255           0 :       space_width = scale_round(unitwidth, res, 72 * 3 * sizescale);
    1256             :   }
    1257       15033 :   return true;
    1258             : }
    1259             : 
    1260             : static struct numeric_directive {
    1261             :   const char *name;
    1262             :   int *ptr;
    1263             : } numeric_directive_table[] = {
    1264             :   { "res", &font::res },
    1265             :   { "hor", &font::hor },
    1266             :   { "vert", &font::vert },
    1267             :   { "unitwidth", &font::unitwidth },
    1268             :   { "paperwidth", &font::paperwidth },
    1269             :   { "paperlength", &font::paperlength },
    1270             :   { "spare1", &font::biggestfont },
    1271             :   { "biggestfont", &font::biggestfont },
    1272             :   { "spare2", &font::spare2 },
    1273             :   { "sizescale", &font::sizescale },
    1274             :   };
    1275             : 
    1276             : // Return file specification of DESC file for selected output device if
    1277             : // it can be located and is valid, and a null pointer otherwise.
    1278        3877 : const char *font::load_desc()
    1279             : {
    1280        3877 :   int nfonts = 0;
    1281             :   char *path;
    1282        3877 :   FILE *fp = open_file("DESC", &path);
    1283        3877 :   if (0 /* nullptr */ == fp)
    1284           0 :     return 0 /* nullptr */;
    1285        7754 :   text_file t(fp, path);
    1286       38761 :   while (t.next_line()) {
    1287       34884 :     char *p = strtok(t.buf, WS);
    1288       34884 :     assert(p != 0 /* nullptr */);
    1289       34884 :     bool numeric_directive_found = false;
    1290             :     size_t idx;
    1291      267789 :     for (idx = 0;
    1292      267789 :          !numeric_directive_found && (idx < countof(numeric_directive_table));
    1293             :          idx++)
    1294      232905 :       if (strcmp(numeric_directive_table[idx].name, p) == 0)
    1295       15967 :         numeric_directive_found = true;
    1296       34884 :     if (numeric_directive_found) {
    1297       15967 :       char *q = strtok(0 /* nullptr */, WS);
    1298       15967 :       if (0 /* nullptr */ == q) {
    1299           0 :         t.error("missing value for directive '%1'", p);
    1300           0 :         return 0 /* nullptr */;
    1301             :       }
    1302             :       int val;
    1303       15967 :       if (sscanf(q, "%d", &val) != 1) {
    1304           0 :         t.error("'%1' directive given invalid number '%2'", p, q);
    1305           0 :         return 0 /* nullptr */;
    1306             :       }
    1307       15967 :       if ((strcmp(p, "res") == 0
    1308       12090 :            || strcmp(p, "hor") == 0
    1309        8238 :            || strcmp(p, "vert") == 0
    1310        4386 :            || strcmp(p, "unitwidth") == 0
    1311         509 :            || strcmp(p, "paperwidth") == 0
    1312         509 :            || strcmp(p, "paperlength") == 0
    1313         509 :            ||  strcmp(p, "sizescale") == 0)
    1314       15967 :           && val < 1) {
    1315           0 :         t.error("expected argument to '%1' directive to be a"
    1316           0 :                 " positive number, got '%2'", p, val);
    1317           0 :         return 0 /* nullptr */;
    1318             :       }
    1319       15967 :       *(numeric_directive_table[idx - 1].ptr) = val;
    1320             :     }
    1321       18917 :     else if (strcmp("family", p) == 0) {
    1322         534 :       p = strtok(0 /* nullptr */, WS);
    1323         534 :       if (0 /* nullptr */ == p) {
    1324           0 :         t.error("'family' directive requires an argument");
    1325           0 :         return 0 /* nullptr */;
    1326             :       }
    1327         534 :       char *tem = new char[strlen(p)+1];
    1328         534 :       strcpy(tem, p);
    1329         534 :       family = tem;
    1330             :     }
    1331       18383 :     else if (strcmp("fonts", p) == 0) {
    1332        3877 :       p = strtok(0 /* nullptr */, WS);
    1333        3877 :       if (0 /* nullptr */ == p) {
    1334           0 :         t.error("'fonts' directive requires arguments");
    1335           0 :         return 0 /* nullptr */;
    1336             :       }
    1337        3877 :       if (sscanf(p, "%d", &nfonts) != 1 || nfonts <= 0) {
    1338           0 :         t.error("expected first argument to 'fonts' directive to be a"
    1339           0 :                 " non-negative number, got '%1'", p);
    1340           0 :         return 0 /* nullptr */;
    1341             :       }
    1342        3877 :       font_name_table = (const char **)new char *[nfonts+1];
    1343       25764 :       for (int i = 0; i < nfonts; i++) {
    1344       21887 :         p = strtok(0 /* nullptr */, WS);
    1345       21887 :         while (0 /* nullptr */ == p) {
    1346           0 :           if (!t.next_line()) {
    1347           0 :             t.error("unexpected end of file while reading font list");
    1348           0 :             return 0 /* nullptr */;
    1349             :           }
    1350           0 :           p = strtok(t.buf, WS);
    1351             :         }
    1352       21887 :         char *temp = new char[strlen(p)+1];
    1353       21887 :         strcpy(temp, p);
    1354       21887 :         font_name_table[i] = temp;
    1355             :       }
    1356        3877 :       p = strtok(0 /* nullptr */, WS);
    1357        3877 :       if (p != 0 /* nullptr */) {
    1358           0 :         t.error("font count does not match declared number of fonts"
    1359           0 :                 " ('%1')", nfonts);
    1360           0 :         return 0 /* nullptr */;
    1361             :       }
    1362        3877 :       font_name_table[nfonts] = 0 /* nullptr */;
    1363             :     }
    1364       14506 :     else if (strcmp("papersize", p) == 0) {
    1365         534 :       if (0 /* nullptr */ == res) {
    1366           0 :         t.error("'res' directive must precede 'papersize' in device"
    1367             :                 " description file");
    1368           0 :         return 0 /* nullptr */;
    1369             :       }
    1370         534 :       p = strtok(0 /* nullptr */, WS);
    1371         534 :       if (0 /* nullptr */ == p) {
    1372           0 :         t.error("'papersize' directive requires an argument");
    1373           0 :         return 0 /* nullptr */;
    1374             :       }
    1375         534 :       bool found_paper = false;
    1376         534 :       char *savedp = strdup(p);
    1377         534 :       if (0 /* nullptr */ == savedp)
    1378           0 :         t.fatal("memory allocation failure while processing 'papersize'"
    1379             :                 " directive");
    1380         534 :       while (p) {
    1381             :         double unscaled_paperwidth, unscaled_paperlength;
    1382         534 :         if (scan_papersize(p, &papersize, &unscaled_paperlength,
    1383             :                            &unscaled_paperwidth)) {
    1384         534 :           paperwidth = int(unscaled_paperwidth * res + 0.5);
    1385         534 :           paperlength = int(unscaled_paperlength * res + 0.5);
    1386         534 :           found_paper = true;
    1387         534 :           break;
    1388             :         }
    1389           0 :         p = strtok(0 /* nullptr */, WS);
    1390             :       }
    1391         534 :       assert(savedp != 0 /* nullptr */);
    1392         534 :       if (!found_paper) {
    1393           0 :         t.error("unable to determine a paper format from '%1'", savedp);
    1394           0 :         free(savedp);
    1395           0 :         return 0 /* nullptr */;
    1396             :       }
    1397         534 :       free(savedp);
    1398             :     }
    1399       13972 :     else if (strcmp("unscaled_charwidths", p) == 0)
    1400         127 :       use_unscaled_charwidths = true;
    1401       13845 :     else if (strcmp("pass_filenames", p) == 0)
    1402         127 :       pass_filenames = true;
    1403       13718 :     else if (strcmp("sizes", p) == 0) {
    1404        3877 :       int n = 16;
    1405        3877 :       sizes = new int[n];
    1406        3877 :       int i = 0;
    1407             :       for (;;) {
    1408        8154 :         p = strtok(0 /* nullptr */, WS);
    1409        8154 :         while (0 /* nullptr */ == p) {
    1410           0 :           if (!t.next_line()) {
    1411           0 :             t.error("list of sizes must be terminated by '0'");
    1412           0 :             return 0 /* nullptr */;
    1413             :           }
    1414           0 :           p = strtok(t.buf, WS);
    1415             :         }
    1416             :         int lower, upper;
    1417        8154 :         switch (sscanf(p, "%d-%d", &lower, &upper)) {
    1418        7493 :         case 1:
    1419        7493 :           upper = lower;
    1420             :           // fall through
    1421        8154 :         case 2:
    1422        8154 :           if (lower <= upper && lower >= 0)
    1423        8154 :             break;
    1424             :           // fall through
    1425             :         default:
    1426           0 :           t.error("invalid size range '%1'", p);
    1427           0 :           return 0 /* nullptr */;
    1428             :         }
    1429        8154 :         if (i + 2 > n) {
    1430           0 :           int *old_sizes = sizes;
    1431           0 :           sizes = new int[n*2];
    1432           0 :           memcpy(sizes, old_sizes, n*sizeof(int));
    1433           0 :           n *= 2;
    1434           0 :           delete[] old_sizes;
    1435             :         }
    1436        8154 :         sizes[i++] = lower;
    1437        8154 :         if (0 == lower)
    1438        3877 :           break;
    1439        4277 :         sizes[i++] = upper;
    1440        4277 :       }
    1441        3877 :       if (i == 1) {
    1442           0 :         t.error("must have some sizes");
    1443           0 :         return 0 /* nullptr */;
    1444             :       }
    1445             :     }
    1446        9841 :     else if (strcmp("styles", p) == 0) {
    1447         614 :       int style_table_size = 5;
    1448         614 :       style_table = (const char **)new char *[style_table_size];
    1449             :       int j;
    1450        3684 :       for (j = 0; j < style_table_size; j++)
    1451        3070 :         style_table[j] = 0;
    1452         614 :       int i = 0;
    1453             :       for (;;) {
    1454        3070 :         p = strtok(0 /* nullptr */, WS);
    1455        3070 :         if (0 /* nullptr */ == p)
    1456         614 :           break;
    1457             :         // leave room for terminating 0
    1458        2456 :         if (i + 1 >= style_table_size) {
    1459           0 :           const char **old_style_table = style_table;
    1460           0 :           style_table_size *= 2;
    1461           0 :           style_table = (const char **)new char*[style_table_size];
    1462           0 :           for (j = 0; j < i; j++)
    1463           0 :             style_table[j] = old_style_table[j];
    1464           0 :           for (; j < style_table_size; j++)
    1465           0 :             style_table[j] = 0;
    1466           0 :           delete[] old_style_table;
    1467             :         }
    1468        2456 :         char *tem = new char[strlen(p) + 1];
    1469        2456 :         strcpy(tem, p);
    1470        2456 :         style_table[i++] = tem;
    1471        2456 :       }
    1472             :     }
    1473        9227 :     else if (strcmp("tcommand", p) == 0)
    1474        3797 :       has_tcommand = true;
    1475        5430 :     else if (strcmp("use_charnames_in_special", p) == 0)
    1476         127 :       use_charnames_in_special = true;
    1477        5303 :     else if (strcmp("unicode", p) == 0)
    1478         340 :       is_unicode = true;
    1479        4963 :     else if (strcmp("image_generator", p) == 0) {
    1480         127 :       p = strtok(0 /* nullptr */, WS);
    1481         127 :       if (0 /* nullptr */ == p) {
    1482           0 :         t.error("'image_generator' directive requires an argument");
    1483           0 :         return 0 /* nullptr */;
    1484             :       }
    1485         127 :       image_generator = strsave(p);
    1486             :     }
    1487        4836 :     else if (strcmp("charset", p) == 0)
    1488           0 :       break;
    1489        4836 :     else if (unknown_desc_command_handler) {
    1490        1981 :       char *directive = p;
    1491        1981 :       p = strtok(0 /* nullptr */, "\n");
    1492        1981 :       (*unknown_desc_command_handler)(directive, trim_arg(p), t.path,
    1493             :                                       t.lineno);
    1494             :     }
    1495             :   }
    1496        3877 :   t.lineno = 0;
    1497        3877 :   if (0 /* nullptr */ == res) {
    1498           0 :     t.error("device description file missing 'res' directive");
    1499           0 :     return 0 /* nullptr */;
    1500             :   }
    1501        3877 :   if (0 == unitwidth) {
    1502           0 :     t.error("device description file missing 'unitwidth' directive");
    1503           0 :     return 0 /* nullptr */;
    1504             :   }
    1505        3877 :   if (0 /* nullptr */ == font_name_table) {
    1506           0 :     t.error("device description file missing 'fonts' directive");
    1507           0 :     return 0 /* nullptr */;
    1508             :   }
    1509        3877 :   if (0 /* nullptr */ == sizes) {
    1510           0 :     t.error("device description file missing 'sizes' directive");
    1511           0 :     return 0 /* nullptr */;
    1512             :   }
    1513        3877 :   return path;
    1514             : }
    1515             : 
    1516       16198 : void font::handle_unknown_font_command(const char *, const char *,
    1517             :                                        const char *, int)
    1518             : {
    1519       16198 : }
    1520             : 
    1521             : FONT_COMMAND_HANDLER
    1522        1498 : font::set_unknown_desc_command_handler(FONT_COMMAND_HANDLER func)
    1523             : {
    1524        1498 :   FONT_COMMAND_HANDLER prev = unknown_desc_command_handler;
    1525        1498 :   unknown_desc_command_handler = func;
    1526        1498 :   return prev;
    1527             : }
    1528             : 
    1529             : // Local Variables:
    1530             : // fill-column: 72
    1531             : // mode: C++
    1532             : // End:
    1533             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14