libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

dvi_extractor.c (41973B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2002, 2003, 2004, 2012, 2017, 2019 Vidyut Samanta and Christian Grothoff
      4 
      5      libextractor is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      libextractor is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with libextractor; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19  */
     20 /**
     21  * @file plugins/dvi_extractor.c
     22  * @brief plugin to support DVI files (from LaTeX)
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include "extractor.h"
     27 #include "fulltext.h"
     28 #include "pagelayout.h"
     29 #include <math.h>
     30 
     31 
     32 /**
     33  * Pair of a PostScipt prefix and the corresponding LE type.
     34  */
     35 struct Matches
     36 {
     37   /**
     38    * Prefix in the PS map.
     39    */
     40   const char *text;
     41 
     42   /**
     43    * Corresponding LE type.
     44    */
     45   enum EXTRACTOR_MetaType type;
     46 };
     47 
     48 
     49 /**
     50  * Map from PS names to LE types.
     51  */
     52 static struct Matches tmap[] = {
     53   { "/Title (",    EXTRACTOR_METATYPE_TITLE },
     54   { "/Subject (",  EXTRACTOR_METATYPE_SUBJECT },
     55   { "/Author (",   EXTRACTOR_METATYPE_AUTHOR_NAME },
     56   { "/Keywords (", EXTRACTOR_METATYPE_KEYWORDS },
     57   { "/Creator (",  EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE },
     58   { "/Producer (", EXTRACTOR_METATYPE_PRODUCED_BY_SOFTWARE },
     59   { NULL, 0 }
     60 };
     61 
     62 
     63 /**
     64  * Parse a "ZZZ" tag.  Specifically, the data may contain a
     65  * postscript dictionary with metadata.
     66  *
     67  * @param data overall input stream
     68  * @param pos where in data is the zzz data
     69  * @param len how many bytes from 'pos' does the zzz data extend?
     70  * @param proc function to call with meta data found
     71  * @param proc_cls closure for proc
     72  * @return 0 to continue to extract, 1 to stop
     73  */
     74 static int
     75 parseZZZ (const char *data,
     76           size_t pos, size_t len,
     77           EXTRACTOR_MetaDataProcessor proc,
     78           void *proc_cls)
     79 {
     80   size_t slen;
     81   size_t end;
     82   unsigned int i;
     83 
     84   end = pos + len;
     85   slen = strlen ("ps:SDict begin [");
     86   if ( (len <= slen) ||
     87        (0 != strncmp ("ps:SDict begin [ ", &data[pos], slen)) )
     88     return 0;
     89   pos += slen;
     90   while (pos < end)
     91   {
     92     for (i = 0; NULL != tmap[i].text; i++)
     93     {
     94       slen = strlen (tmap[i].text);
     95       if ( (pos + slen > end) ||
     96            (0 != strncmp (&data[pos], tmap[i].text, slen)) )
     97         continue;
     98       pos += slen;
     99       slen = pos;
    100       while ((slen < end) && (data[slen] != ')'))
    101         slen++;
    102       slen = slen - pos;
    103       {
    104         char value[slen + 1];
    105 
    106         value[slen] = '\0';
    107         memcpy (value, &data[pos], slen);
    108         if (0 != proc (proc_cls,
    109                        "dvi",
    110                        tmap[i].type,
    111                        EXTRACTOR_METAFORMAT_C_STRING,
    112                        "text/plain",
    113                        value,
    114                        strlen (value) + 1))
    115           return 1;
    116       }
    117       pos += slen + 1;
    118       break;
    119     }
    120     pos++;
    121   }
    122   return 0;
    123 }
    124 
    125 
    126 /**
    127  * Read 32-bit unsigned integer in big-endian format from 'data'.
    128  *
    129  * @param data pointer to integer (possibly unaligned)
    130  * @return 32-bit integer in host byte order
    131  */
    132 static uint32_t
    133 getIntAt (const void *data)
    134 {
    135   uint32_t p;
    136 
    137   memcpy (&p, data, 4);          /* ensure alignment! */
    138   return ntohl (p);
    139 }
    140 
    141 
    142 /**
    143  * Read 16-bit unsigned integer in big-endian format from 'data'.
    144  *
    145  * @param data pointer to integer (possibly unaligned)
    146  * @return 16-bit integer in host byte order
    147  */
    148 static uint16_t
    149 getShortAt (const void *data)
    150 {
    151   uint16_t p;
    152 
    153   memcpy (&p, data, sizeof (uint16_t));          /* ensure alignment! */
    154   return ntohs (p);
    155 }
    156 
    157 
    158 /* ****************** full text extraction ********************** */
    159 
    160 /**
    161  * Maximum number of fonts we track.
    162  */
    163 #define DVI_MAX_FONTS 1024
    164 
    165 /**
    166  * Size of the font hash table (a power of two, larger than
    167  * #DVI_MAX_FONTS).
    168  */
    169 #define DVI_FONT_HASH 2048
    170 
    171 /**
    172  * Maximum depth of the push/pop stack.
    173  */
    174 #define DVI_MAX_STACK 1024
    175 
    176 /**
    177  * Maximum number of pages we extract text from.
    178  */
    179 #define DVI_MAX_PAGES 50000
    180 
    181 /**
    182  * Maximum number of lines we collect over the whole document.
    183  */
    184 #define DVI_MAX_LINES 500000
    185 
    186 /**
    187  * Maximum number of bytes in one line.
    188  */
    189 #define DVI_MAX_LINE_BYTES (64 * 1024)
    190 
    191 /**
    192  * Font size assumed for undefined fonts (10pt in TeX's scaled points).
    193  */
    194 #define DVI_DEFAULT_SIZE 655360
    195 
    196 /**
    197  * Markers in the encoding tables for glyphs that expand to several
    198  * characters.  Real code points are never below 0x20.
    199  */
    200 #define LIG_FF 1
    201 #define LIG_FI 2
    202 #define LIG_FL 3
    203 #define LIG_FFI 4
    204 #define LIG_FFL 5
    205 #define LIG_SS 6
    206 
    207 
    208 /**
    209  * Font encodings we know how to map to Unicode.
    210  */
    211 enum DviEncoding
    212 {
    213   /**
    214    * TeX text (OT1), e.g. cmr10.
    215    */
    216   ENC_OT1,
    217 
    218   /**
    219    * TeX typewriter text (OT1 variant), e.g. cmtt10.
    220    */
    221   ENC_OT1TT,
    222 
    223   /**
    224    * Cork encoding (T1), e.g. ecrm1000, sfrm1000, ec-lmr10.
    225    */
    226   ENC_T1,
    227 
    228   /**
    229    * ASCII plus ISO-8859-1 upper half (roughly TeXBase1, 8r).
    230    */
    231   ENC_LATIN1,
    232 
    233   /**
    234    * TeX math italic, e.g. cmmi10.
    235    */
    236   ENC_MATHIT,
    237 
    238   /**
    239    * TeX math symbols, e.g. cmsy10.
    240    */
    241   ENC_SYMBOL,
    242 
    243   /**
    244    * Font whose glyphs we do not map (math extension, dingbats, ...).
    245    */
    246   ENC_NONE
    247 };
    248 
    249 
    250 /**
    251  * A font defined in the DVI file.
    252  */
    253 struct DviFont
    254 {
    255   /**
    256    * Font number used in the DVI file.
    257    */
    258   uint32_t k;
    259 
    260   /**
    261    * Scaled size in DVI units.
    262    */
    263   double size;
    264 
    265   /**
    266    * Encoding guessed from the font name.
    267    */
    268   enum DviEncoding enc;
    269 };
    270 
    271 
    272 /**
    273  * Registers of the DVI machine.
    274  */
    275 struct DviRegs
    276 {
    277   int64_t h;
    278   int64_t v;
    279   int64_t w;
    280   int64_t x;
    281   int64_t y;
    282   int64_t z;
    283 };
    284 
    285 
    286 /**
    287  * State of the full-text extraction.
    288  */
    289 struct DviText
    290 {
    291   /**
    292    * Extraction context, for keep-alives.
    293    */
    294   struct EXTRACTOR_ExtractContext *ec;
    295 
    296   /**
    297    * The DVI file.
    298    */
    299   const unsigned char *data;
    300 
    301   /**
    302    * Number of bytes in @e data.
    303    */
    304   size_t size;
    305 
    306   /**
    307    * Defined fonts.
    308    */
    309   struct DviFont fonts[DVI_MAX_FONTS];
    310 
    311   /**
    312    * Number of entries in @e fonts.
    313    */
    314   unsigned int num_fonts;
    315 
    316   /**
    317    * Hash table (open addressing) mapping font numbers to 1 + their
    318    * index in @e fonts; 0 marks an empty slot.
    319    */
    320   uint16_t font_hash[DVI_FONT_HASH];
    321 
    322   /**
    323    * Pages collected so far.
    324    */
    325   struct EXTRACTOR_FullTextPage *pages;
    326 
    327   /**
    328    * Geometry of the lines of each page.
    329    */
    330   struct EXTRACTOR_PageLineGeom **geoms;
    331 
    332   /**
    333    * Number of pages in @e pages / @e geoms.
    334    */
    335   unsigned int num_pages;
    336 
    337   /**
    338    * Allocated length of the geometry array of the current page.
    339    */
    340   unsigned int geom_cap;
    341 
    342   /**
    343    * Text of the current line.
    344    */
    345   char *lbuf;
    346 
    347   /**
    348    * Bytes in @e lbuf.
    349    */
    350   size_t llen;
    351 
    352   /**
    353    * Allocated size of @e lbuf.
    354    */
    355   size_t lcap;
    356 
    357   /**
    358    * 1 if a line is being collected.
    359    */
    360   int in_line;
    361 
    362   /**
    363    * 1 if a space must precede the next character of the line.
    364    */
    365   int pending_space;
    366 
    367   /**
    368    * Accent (combining code point) waiting for its base character, 0
    369    * for none.
    370    */
    371   uint32_t pending_accent;
    372 
    373   /**
    374    * 1 if @e pending_accent was placed over the preceding base character
    375    * (@e last_cp), so that it may belong to it rather than to the next.
    376    */
    377   int accent_post_ok;
    378 
    379   /**
    380    * Last base character appended to the line (for accents placed
    381    * after their base), 0 if none.
    382    */
    383   uint32_t last_cp;
    384 
    385   /**
    386    * Offset of @e last_cp in @e lbuf.
    387    */
    388   size_t last_cp_off;
    389 
    390   /**
    391    * Baseline of the current line.
    392    */
    393   int64_t line_v;
    394 
    395   /**
    396    * Left end of the current line.
    397    */
    398   double line_x0;
    399 
    400   /**
    401    * (Estimated) right end of the current line.
    402    */
    403   double line_x1;
    404 
    405   /**
    406    * Font size at the start of the current line.
    407    */
    408   double line_size;
    409 
    410   /**
    411    * (Estimated) horizontal position after the last glyph.
    412    */
    413   double last_end;
    414 
    415   /**
    416    * Total number of lines collected.
    417    */
    418   unsigned int total_lines;
    419 
    420   /**
    421    * Total number of text bytes collected.
    422    */
    423   size_t total_bytes;
    424 
    425   /**
    426    * Smallest and largest baseline seen.
    427    */
    428   int64_t vmin;
    429   int64_t vmax;
    430 
    431   /**
    432    * 1 if @e vmin and @e vmax are set.
    433    */
    434   int have_v;
    435 
    436   /**
    437    * Remaining number of opcodes we are willing to interpret.
    438    */
    439   uint64_t budget;
    440 
    441   /**
    442    * 1 if we ran out of memory or into a limit: stop collecting.
    443    */
    444   int full;
    445 };
    446 
    447 
    448 /**
    449  * Approximate widths (in 1/1000 em) of the glyphs of cmr10, used to
    450  * estimate where a glyph ends (DVI does not record glyph widths).
    451  */
    452 static const uint16_t cmr_widths[128] = {
    453   /* 0x00 Greek capitals */
    454   625, 833, 778, 694, 667, 750, 722, 778, 722, 778, 722,
    455   /* 0x0B ff fi fl ffi ffl */
    456   583, 556, 556, 833, 833,
    457   /* 0x10 dotless i/j, accents */
    458   278, 306, 500, 500, 500, 500, 500, 750, 444,
    459   /* 0x19 ss ae oe o/ AE OE O/ */
    460   500, 722, 778, 500, 903, 1014, 778,
    461   /* 0x20 */
    462   278, 278, 500, 833, 500, 833, 778, 278, 389, 389, 500, 778, 278, 333,
    463   278, 500,
    464   /* 0x30 digits */
    465   500, 500, 500, 500, 500, 500, 500, 500, 500, 500,
    466   /* 0x3A */
    467   278, 278, 278, 778, 472, 472,
    468   /* 0x40 @ A-O */
    469   778, 750, 708, 722, 764, 681, 653, 785, 750, 361, 514, 778, 625, 917,
    470   750, 778,
    471   /* 0x50 P-Z [ " ] ^ . */
    472   681, 778, 736, 556, 722, 750, 750, 1028, 750, 750, 611, 278, 500, 278,
    473   500, 278,
    474   /* 0x60 ` a-o */
    475   278, 500, 556, 444, 556, 444, 306, 500, 556, 278, 306, 528, 278, 833,
    476   556, 500,
    477   /* 0x70 p-z -- --- accents */
    478   556, 528, 392, 394, 389, 556, 528, 722, 528, 528, 444, 500, 1000, 500,
    479   500, 500
    480 };
    481 
    482 
    483 /**
    484  * Greek capitals at 0x00-0x0A of OT1 and math italic fonts.
    485  */
    486 static const uint16_t greek_caps[11] = {
    487   0x0393, 0x0394, 0x0398, 0x039B, 0x039E, 0x03A0, 0x03A3, 0x03A5,
    488   0x03A6, 0x03A8, 0x03A9
    489 };
    490 
    491 /**
    492  * Glyphs 0x10-0x1F of OT1 fonts.
    493  */
    494 static const uint16_t ot1_10[16] = {
    495   0x0131, 0x0237, 0x0300, 0x0301, 0x030C, 0x0306, 0x0304, 0x030A,
    496   0x0327, 0x00DF, 0x00E6, 0x0153, 0x00F8, 0x00C6, 0x0152, 0x00D8
    497 };
    498 
    499 /**
    500  * Glyphs 0x00-0x1F of T1 fonts.
    501  */
    502 static const uint16_t t1_00[32] = {
    503   0x0300, 0x0301, 0x0302, 0x0303, 0x0308, 0x030B, 0x030A, 0x030C,
    504   0x0306, 0x0304, 0x0307, 0x0327, 0x0328, 0x201A, 0x2039, 0x203A,
    505   0x201C, 0x201D, 0x201E, 0x00AB, 0x00BB, 0x2013, 0x2014, 0,
    506   0, 0x0131, 0x0237, LIG_FF, LIG_FI, LIG_FL, LIG_FFI, LIG_FFL
    507 };
    508 
    509 /**
    510  * Glyphs 0x80-0xBF of T1 fonts.
    511  */
    512 static const uint16_t t1_80[64] = {
    513   0x0102, 0x0104, 0x0106, 0x010C, 0x010E, 0x011A, 0x0118, 0x011E,
    514   0x0139, 0x013D, 0x0141, 0x0143, 0x0147, 0x014A, 0x0150, 0x0154,
    515   0x0158, 0x015A, 0x0160, 0x015E, 0x0164, 0x0162, 0x0170, 0x016E,
    516   0x0178, 0x0179, 0x017D, 0x017B, 0x0132, 0x0130, 0x0111, 0x00A7,
    517   0x0103, 0x0105, 0x0107, 0x010D, 0x010F, 0x011B, 0x0119, 0x011F,
    518   0x013A, 0x013E, 0x0142, 0x0144, 0x0148, 0x014B, 0x0151, 0x0155,
    519   0x0159, 0x015B, 0x0161, 0x015F, 0x0165, 0x0163, 0x0171, 0x016F,
    520   0x00FF, 0x017A, 0x017E, 0x017C, 0x0133, 0x00A1, 0x00BF, 0x00A3
    521 };
    522 
    523 /**
    524  * Glyphs 0x0B-0x27 of math italic fonts (lower-case Greek).
    525  */
    526 static const uint16_t mathit_0b[29] = {
    527   0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03F5, 0x03B6, 0x03B7, 0x03B8,
    528   0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03C0, 0x03C1,
    529   0x03C3, 0x03C4, 0x03C5, 0x03D5, 0x03C7, 0x03C8, 0x03C9, 0x03B5,
    530   0x03D1, 0x03D6, 0x03F1, 0x03C2, 0x03C6
    531 };
    532 
    533 /**
    534  * Glyphs 0x00-0x7F of math symbol fonts (0: not mapped).
    535  */
    536 static const uint16_t symbol_map[128] = {
    537   0x2212, 0x22C5, 0x00D7, 0x2217, 0x00F7, 0x22C4, 0x00B1, 0x2213,
    538   0x2295, 0x2296, 0x2297, 0x2298, 0x2299, 0x25EF, 0x2218, 0x2022,
    539   0x224D, 0x2261, 0x2286, 0x2287, 0x2264, 0x2265, 0x2AAF, 0x2AB0,
    540   0x223C, 0x2248, 0x2282, 0x2283, 0x226A, 0x226B, 0x227A, 0x227B,
    541   0x2190, 0x2192, 0x2191, 0x2193, 0x2194, 0x2197, 0x2198, 0x2243,
    542   0x21D0, 0x21D2, 0x21D1, 0x21D3, 0x21D4, 0x2196, 0x2199, 0x221D,
    543   0x2032, 0x221E, 0x2208, 0x220B, 0x25B3, 0x25BD, 0, 0,
    544   0x2200, 0x2203, 0x00AC, 0x2205, 0x211C, 0x2111, 0x22A4, 0x22A5,
    545   0x2135, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
    546   'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
    547   'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
    548   'X', 'Y', 'Z', 0x222A, 0x2229, 0x228E, 0x2227, 0x2228,
    549   0x22A2, 0x22A3, 0x230A, 0x230B, 0x2308, 0x2309, '{', '}',
    550   0x27E8, 0x27E9, '|', 0x2016, 0x2195, 0x21D5, '\\', 0x2240,
    551   0x221A, 0x2A3F, 0x2207, 0x222B, 0x2294, 0x2293, 0x2291, 0x2292,
    552   0x00A7, 0x2020, 0x2021, 0x00B6, 0x2663, 0x2662, 0x2661, 0x2660
    553 };
    554 
    555 
    556 /**
    557  * Map glyph @a c of a font with encoding @a enc to Unicode.
    558  *
    559  * @param enc font encoding
    560  * @param c glyph code
    561  * @return code point, a LIG_* marker, or 0 if the glyph has no text
    562  */
    563 static uint32_t
    564 dvi_map_glyph (enum DviEncoding enc,
    565                uint32_t c)
    566 {
    567   if (c > 255)
    568     return 0;
    569   switch (enc)
    570   {
    571   case ENC_OT1:
    572   case ENC_OT1TT:
    573     if (c >= 128)
    574       return 0;
    575     if (c < 0x0B)
    576       return greek_caps[c];
    577     if (ENC_OT1TT == enc)
    578     {
    579       switch (c)
    580       {
    581       case 0x0B: return 0x2191;
    582       case 0x0C: return 0x2193;
    583       case 0x0D: return '\'';
    584       case 0x0E: return 0x00A1;
    585       case 0x0F: return 0x00BF;
    586       case 0x7F: return 0x0308;
    587       default:
    588         break;
    589       }
    590       if (c >= 0x20)
    591         return c;
    592     }
    593     if (c < 0x10)
    594       return LIG_FF + (c - 0x0B);
    595     if (c < 0x20)
    596       return ot1_10[c - 0x10];
    597     switch (c)
    598     {
    599     case 0x20: return 0x0337; /* stroke of \l and \L */
    600     case 0x22: return 0x201D;
    601     case 0x27: return 0x2019;
    602     case 0x3C: return 0x00A1;
    603     case 0x3E: return 0x00BF;
    604     case 0x5C: return 0x201C;
    605     case 0x5E: return 0x0302;
    606     case 0x5F: return 0x0307;
    607     case 0x60: return 0x2018;
    608     case 0x7B: return 0x2013;
    609     case 0x7C: return 0x2014;
    610     case 0x7D: return 0x030B;
    611     case 0x7E: return 0x0303;
    612     case 0x7F: return 0x0308;
    613     default:
    614       return c;
    615     }
    616   case ENC_T1:
    617     if (c < 0x20)
    618       return t1_00[c];
    619     if (c == 0x7F)
    620       return '-';
    621     if (c < 0x80)
    622       return c;
    623     if (c < 0xC0)
    624       return t1_80[c - 0x80];
    625     switch (c)
    626     {
    627     case 0xD7: return 0x0152;
    628     case 0xDF: return LIG_SS;
    629     case 0xF7: return 0x0153;
    630     case 0xFF: return 0x00DF;
    631     default:
    632       return c;
    633     }
    634   case ENC_LATIN1:
    635     if ( ( (c >= 0x20) && (c < 0x7F) ) ||
    636          (c >= 0xA0) )
    637       return c;
    638     return 0;
    639   case ENC_MATHIT:
    640     if (c < 0x0B)
    641       return greek_caps[c];
    642     if (c < 0x28)
    643       return mathit_0b[c - 0x0B];
    644     if ( (c >= 0x30) && (c <= 0x3E) && (c != 0x3D) )
    645       return c;
    646     if (0x3D == c)
    647       return '/';
    648     if (0x40 == c)
    649       return 0x2202;
    650     if ( ( (c >= 'A') && (c <= 'Z') ) ||
    651          ( (c >= 'a') && (c <= 'z') ) )
    652       return c;
    653     if (0x60 == c)
    654       return 0x2113;
    655     if (0x7B == c)
    656       return 0x0131;
    657     if (0x7C == c)
    658       return 0x0237;
    659     return 0;
    660   case ENC_SYMBOL:
    661     if (c >= 128)
    662       return 0;
    663     return symbol_map[c];
    664   case ENC_NONE:
    665   default:
    666     return 0;
    667   }
    668 }
    669 
    670 
    671 /**
    672  * Estimate the width of glyph @a c, in 1/1000 em.
    673  *
    674  * @param enc font encoding
    675  * @param c glyph code
    676  * @return estimated width
    677  */
    678 static unsigned int
    679 dvi_glyph_width (enum DviEncoding enc,
    680                  uint32_t c)
    681 {
    682   switch (enc)
    683   {
    684   case ENC_OT1:
    685     return (c < 128) ? cmr_widths[c] : 500;
    686   case ENC_OT1TT:
    687     return 525;
    688   case ENC_T1:
    689   case ENC_LATIN1:
    690     if ( (c >= 0x21) && (c < 0x7F) &&
    691          (c != '"') && (c != '<') && (c != '>') && (c != '\\') &&
    692          (c != '_') && (c < '{') )
    693       return cmr_widths[c];
    694     if (c >= 0x80)
    695       return ( (c & 0x3F) < 0x20) ? 720 : 500;
    696     return 500;
    697   default:
    698     return 600;
    699   }
    700 }
    701 
    702 
    703 /**
    704  * Guess the encoding of a font from its name.
    705  *
    706  * @param name font name (not 0-terminated)
    707  * @param len length of @a name
    708  * @return guessed encoding
    709  */
    710 static enum DviEncoding
    711 dvi_font_encoding (const char *name,
    712                    size_t len)
    713 {
    714   static const char *const none[] = {
    715     "cmex", "lmex", "msam", "msbm", "lasy", "wasy", "line", "lcircle",
    716     "tc", "ts1-", "eufm", "eusm", "eurm", "rsfs", "stmary", "bbm", "dsrom",
    717     "pzd", "psy", "wncy", "manfnt", "logo", "esint", "cmbsy10x",
    718     NULL
    719   };
    720   static const char *const tt[] = {
    721     "cmtt", "cmsltt", "cmitt", "cmtcsc", "lmtt", "rm-lmtt", NULL
    722   };
    723   static const char *const sf[] = {
    724     "rm", "ti", "bx", "sl", "ss", "tt", "cc", "bi", "bl", "si", "sx",
    725     "so", "sq", "xc", "tl", "st", "oc", "ci", "ui", "vi", "bo",
    726     NULL
    727   };
    728   char lc[64];
    729   size_t i;
    730   size_t end;
    731 
    732   if (len >= sizeof (lc))
    733     len = sizeof (lc) - 1;
    734   for (i = 0; i < len; i++)
    735     lc[i] = (char) tolower ((unsigned char) name[i]);
    736   lc[len] = '\0';
    737   for (i = 0; NULL != none[i]; i++)
    738     if (0 == strncmp (lc, none[i], strlen (none[i])))
    739       return ENC_NONE;
    740   if ( (0 == strncmp (lc, "cmmi", 4)) ||
    741        (0 == strncmp (lc, "lmmi", 4)) ||
    742        (0 == strncmp (lc, "rm-lmmi", 7)) )
    743     return ENC_MATHIT;
    744   if ( (0 == strncmp (lc, "cmsy", 4)) ||
    745        (0 == strncmp (lc, "cmbsy", 5)) ||
    746        (0 == strncmp (lc, "lmsy", 4)) ||
    747        (0 == strncmp (lc, "lmbsy", 5)) )
    748     return ENC_SYMBOL;
    749   for (i = 0; NULL != tt[i]; i++)
    750     if (0 == strncmp (lc, tt[i], strlen (tt[i])))
    751       return ENC_OT1TT;
    752   if ( (0 == strncmp (lc, "ec-", 3)) ||
    753        (0 == strncmp (lc, "t1-", 3)) ||
    754        (0 == strncmp (lc, "cs-", 3)) ||
    755        (0 == strncmp (lc, "qx-", 3)) ||
    756        (0 == strncmp (lc, "l7x-", 4)) ||
    757        (0 == strncmp (lc, "t5-", 3)) ||
    758        (0 == strncmp (lc, "texnansi-", 9)) )
    759     return ENC_T1;
    760   if ( (len > 2) &&
    761        (0 == strncmp (lc, "ec", 2)) &&
    762        isalpha ((unsigned char) lc[2]) )
    763     return ENC_T1;
    764   if ( (len > 4) &&
    765        (0 == strncmp (lc, "sf", 2)) )
    766     for (i = 0; NULL != sf[i]; i++)
    767       if (0 == strncmp (&lc[2], sf[i], 2))
    768         return ENC_T1;
    769   /* Karl Berry's font names: <supplier><family><variant><encoding> */
    770   end = len;
    771   while ( (end > 0) &&
    772           isdigit ((unsigned char) lc[end - 1]) )
    773     end--;
    774   for (i = (end > 4) ? end - 4 : 0; i + 1 < end; i++)
    775   {
    776     if ('8' == lc[i])
    777     {
    778       if ('t' == lc[i + 1])
    779         return ENC_T1;
    780       if ('r' == lc[i + 1])
    781         return ENC_LATIN1;
    782       if ( ('c' == lc[i + 1]) ||
    783            ('y' == lc[i + 1]) )
    784         return ENC_NONE;
    785     }
    786   }
    787   return ENC_OT1;
    788 }
    789 
    790 
    791 /**
    792  * Check whether @a cp is a combining accent.
    793  *
    794  * @param cp code point
    795  * @return 1 if combining
    796  */
    797 static int
    798 is_accent (uint32_t cp)
    799 {
    800   return ( (cp >= 0x0300) && (cp < 0x0370) ) ? 1 : 0;
    801 }
    802 
    803 
    804 /**
    805  * Append raw bytes to the current line.
    806  *
    807  * @param t state
    808  * @param s bytes to append
    809  * @param len number of bytes
    810  */
    811 static void
    812 line_bytes (struct DviText *t,
    813             const char *s,
    814             size_t len)
    815 {
    816   if (t->llen + len > DVI_MAX_LINE_BYTES)
    817     return;
    818   if (t->llen + len > t->lcap)
    819   {
    820     size_t ncap = (0 == t->lcap) ? 256 : t->lcap;
    821     char *n;
    822 
    823     while (ncap < t->llen + len)
    824       ncap *= 2;
    825     n = realloc (t->lbuf,
    826                  ncap);
    827     if (NULL == n)
    828     {
    829       t->full = 1;
    830       return;
    831     }
    832     t->lbuf = n;
    833     t->lcap = ncap;
    834   }
    835   memcpy (&t->lbuf[t->llen],
    836           s,
    837           len);
    838   t->llen += len;
    839 }
    840 
    841 
    842 /**
    843  * Append a code point to the current line, preceded by a space if one
    844  * is pending.
    845  *
    846  * @param t state
    847  * @param cp code point (not a LIG_* marker)
    848  */
    849 static void
    850 line_cp (struct DviText *t,
    851          uint32_t cp)
    852 {
    853   char enc[4];
    854   size_t n;
    855 
    856   if ( (t->pending_space) &&
    857        (t->llen > 0) )
    858     line_bytes (t, " ", 1);
    859   t->pending_space = 0;
    860   if (cp < 0x80)
    861   {
    862     enc[0] = (char) cp;
    863     n = 1;
    864   }
    865   else if (cp < 0x800)
    866   {
    867     enc[0] = (char) (0xC0 | (cp >> 6));
    868     enc[1] = (char) (0x80 | (cp & 0x3F));
    869     n = 2;
    870   }
    871   else
    872   {
    873     enc[0] = (char) (0xE0 | (cp >> 12));
    874     enc[1] = (char) (0x80 | ((cp >> 6) & 0x3F));
    875     enc[2] = (char) (0x80 | (cp & 0x3F));
    876     n = 3;
    877   }
    878   line_bytes (t, enc, n);
    879 }
    880 
    881 
    882 static void
    883 resolve_accent (struct DviText *t);
    884 
    885 
    886 /**
    887  * Finish the current line (if any) and add it to the current page.
    888  *
    889  * @param t state
    890  */
    891 static void
    892 flush_line (struct DviText *t)
    893 {
    894   struct EXTRACTOR_FullTextPage *page;
    895   struct EXTRACTOR_PageLineGeom *g;
    896 
    897   if (! t->in_line)
    898     return;
    899   resolve_accent (t);
    900   t->in_line = 0;
    901   t->pending_space = 0;
    902   t->last_cp = 0;
    903   if ( (0 == t->llen) ||
    904        (t->full) ||
    905        (0 == t->num_pages) )
    906   {
    907     t->llen = 0;
    908     return;
    909   }
    910   if ( (t->total_lines >= DVI_MAX_LINES) ||
    911        (t->total_bytes + t->llen >= EXTRACTOR_FULLTEXT_MAX) )
    912   {
    913     t->full = 1;
    914     t->llen = 0;
    915     return;
    916   }
    917   page = &t->pages[t->num_pages - 1];
    918   if (page->num_lines >= t->geom_cap)
    919   {
    920     unsigned int ncap = (0 == t->geom_cap) ? 32 : 2 * t->geom_cap;
    921 
    922     g = realloc (t->geoms[t->num_pages - 1],
    923                  ncap * sizeof (*g));
    924     if (NULL == g)
    925     {
    926       t->full = 1;
    927       t->llen = 0;
    928       return;
    929     }
    930     t->geoms[t->num_pages - 1] = g;
    931     t->geom_cap = ncap;
    932   }
    933   g = &t->geoms[t->num_pages - 1][page->num_lines];
    934   if (0 != EXTRACTOR_fulltext_page_add_ (page,
    935                                          t->lbuf,
    936                                          t->llen,
    937                                          -1.0,
    938                                          0))
    939   {
    940     t->full = 1;
    941     t->llen = 0;
    942     return;
    943   }
    944   g->x0 = t->line_x0;
    945   g->x1 = t->line_x1;
    946   g->y = (double) t->line_v;
    947   g->size = t->line_size;
    948   if ( (! t->have_v) ||
    949        (t->line_v < t->vmin) )
    950     t->vmin = t->line_v;
    951   if ( (! t->have_v) ||
    952        (t->line_v > t->vmax) )
    953     t->vmax = t->line_v;
    954   t->have_v = 1;
    955   t->total_lines++;
    956   t->total_bytes += t->llen;
    957   t->llen = 0;
    958 }
    959 
    960 
    961 /**
    962  * Attach a pending accent to the preceding base character if it was
    963  * placed over it; otherwise drop it.
    964  *
    965  * @param t state
    966  */
    967 static void
    968 resolve_accent (struct DviText *t)
    969 {
    970   uint32_t comp;
    971   int pending_space;
    972 
    973   if (0 == t->pending_accent)
    974     return;
    975   if ( (t->accent_post_ok) &&
    976        (0 != t->last_cp) &&
    977        (t->last_cp_off <= t->llen) )
    978   {
    979     comp = EXTRACTOR_pagelayout_compose_ (t->last_cp,
    980                                           t->pending_accent);
    981     pending_space = t->pending_space;
    982     t->pending_space = 0;
    983     if (0 != comp)
    984     {
    985       t->llen = t->last_cp_off;
    986       line_cp (t, comp);
    987     }
    988     else
    989     {
    990       line_cp (t, t->pending_accent);
    991     }
    992     t->pending_space = pending_space;
    993     t->last_cp = 0;
    994   }
    995   t->pending_accent = 0;
    996   t->accent_post_ok = 0;
    997 }
    998 
    999 
   1000 /**
   1001  * Typeset glyph @a c of @a font at (@a h, @a v).
   1002  *
   1003  * @param t state
   1004  * @param font the font, NULL if undefined
   1005  * @param c glyph code
   1006  * @param h horizontal position
   1007  * @param v vertical position
   1008  * @return estimated width of the glyph
   1009  */
   1010 static double
   1011 put_glyph (struct DviText *t,
   1012            const struct DviFont *font,
   1013            uint32_t c,
   1014            int64_t h,
   1015            int64_t v)
   1016 {
   1017   enum DviEncoding enc = (NULL == font) ? ENC_OT1 : font->enc;
   1018   double size = (NULL == font) ? DVI_DEFAULT_SIZE : font->size;
   1019   double width = size * dvi_glyph_width (enc, c) / 1000.0;
   1020   double hd = (double) h;
   1021   uint32_t cp;
   1022   double gap;
   1023 
   1024   cp = dvi_map_glyph (enc, c);
   1025   if (0 == cp)
   1026     return width;
   1027   if (t->in_line)
   1028   {
   1029     double big = (size > t->line_size) ? size : t->line_size;
   1030     double dv = (double) v - (double) t->line_v;
   1031 
   1032     if (fabs (dv) > 0.7 * big)
   1033       flush_line (t);
   1034   }
   1035   if (! t->in_line)
   1036   {
   1037     t->in_line = 1;
   1038     t->line_v = v;
   1039     t->line_x0 = hd;
   1040     t->line_x1 = hd;
   1041     t->line_size = size;
   1042     t->last_end = hd;
   1043     t->pending_space = 0;
   1044     t->pending_accent = 0;
   1045     t->accent_post_ok = 0;
   1046     t->last_cp = 0;
   1047   }
   1048   gap = hd - t->last_end;
   1049   if (gap > 0.17 * size)
   1050     t->pending_space = 1;
   1051   if (hd + width > t->line_x1)
   1052     t->line_x1 = hd + width;
   1053   t->last_end = hd + width;
   1054   if (is_accent (cp))
   1055   {
   1056     resolve_accent (t);
   1057     t->pending_accent = cp;
   1058     /* the accent overlaps the preceding base character: it belongs to
   1059        that one unless the next glyph is set under it, too */
   1060     t->accent_post_ok = ( (0 != t->last_cp) &&
   1061                           (gap < -0.1 * size) &&
   1062                           (! t->pending_space) ) ? 1 : 0;
   1063     return width;
   1064   }
   1065   if ( (0 != t->pending_accent) &&
   1066        (gap >= 0) )
   1067     resolve_accent (t);
   1068   if (cp < 0x20)
   1069   {
   1070     static const char *const ligs[] = {
   1071       "", "ff", "fi", "fl", "ffi", "ffl", "SS"
   1072     };
   1073     const char *l = ligs[cp];
   1074 
   1075     t->pending_accent = 0;
   1076     t->accent_post_ok = 0;
   1077     while ('\0' != *l)
   1078     {
   1079       line_cp (t, (unsigned char) *l);
   1080       l++;
   1081     }
   1082     t->last_cp = 0;
   1083     return width;
   1084   }
   1085   if (0 != t->pending_accent)
   1086   {
   1087     uint32_t comp = EXTRACTOR_pagelayout_compose_ (cp,
   1088                                                    t->pending_accent);
   1089 
   1090     if (0 != comp)
   1091     {
   1092       line_cp (t, comp);
   1093     }
   1094     else
   1095     {
   1096       line_cp (t, cp);
   1097       line_cp (t, t->pending_accent);
   1098     }
   1099     t->pending_accent = 0;
   1100     t->accent_post_ok = 0;
   1101     t->last_cp = 0;
   1102     return width;
   1103   }
   1104   {
   1105     size_t before = t->llen;
   1106 
   1107     line_cp (t, cp);
   1108     if (t->llen > before)
   1109     {
   1110       t->last_cp_off = t->llen - ( (cp < 0x80) ? 1 : (cp < 0x800) ? 2 : 3);
   1111       t->last_cp = cp;
   1112     }
   1113     else
   1114     {
   1115       t->last_cp = 0;
   1116     }
   1117   }
   1118   return width;
   1119 }
   1120 
   1121 
   1122 /**
   1123  * Read an unsigned big-endian integer of @a n bytes.
   1124  *
   1125  * @param t state
   1126  * @param pos position to read at, updated
   1127  * @param n number of bytes (1-4)
   1128  * @param[out] ret value read
   1129  * @return 0 on success, -1 if out of bounds
   1130  */
   1131 static int
   1132 read_u (const struct DviText *t,
   1133         size_t *pos,
   1134         unsigned int n,
   1135         uint32_t *ret)
   1136 {
   1137   uint32_t r = 0;
   1138   unsigned int i;
   1139 
   1140   if ( (*pos > t->size) ||
   1141        (t->size - *pos < n) )
   1142     return -1;
   1143   for (i = 0; i < n; i++)
   1144     r = (r << 8) | t->data[*pos + i];
   1145   *pos += n;
   1146   *ret = r;
   1147   return 0;
   1148 }
   1149 
   1150 
   1151 /**
   1152  * Read a signed big-endian integer of @a n bytes.
   1153  *
   1154  * @param t state
   1155  * @param pos position to read at, updated
   1156  * @param n number of bytes (1-4)
   1157  * @param[out] ret value read
   1158  * @return 0 on success, -1 if out of bounds
   1159  */
   1160 static int
   1161 read_s (const struct DviText *t,
   1162         size_t *pos,
   1163         unsigned int n,
   1164         int64_t *ret)
   1165 {
   1166   uint32_t u;
   1167 
   1168   if (0 != read_u (t, pos, n, &u))
   1169     return -1;
   1170   if ( (n < 4) &&
   1171        (u & (1U << (8 * n - 1))) )
   1172     *ret = (int64_t) u - ((int64_t) 1 << (8 * n));
   1173   else if (4 == n)
   1174     *ret = (int64_t) (int32_t) u;
   1175   else
   1176     *ret = (int64_t) u;
   1177   return 0;
   1178 }
   1179 
   1180 
   1181 /**
   1182  * Hash a font number.
   1183  *
   1184  * @param k font number
   1185  * @return initial slot in the font hash table
   1186  */
   1187 static unsigned int
   1188 font_slot (uint32_t k)
   1189 {
   1190   return (unsigned int) ((k * 2654435761U) >> 21) & (DVI_FONT_HASH - 1);
   1191 }
   1192 
   1193 
   1194 /**
   1195  * Look up font @a k.
   1196  *
   1197  * @param t state
   1198  * @param k font number
   1199  * @return the font, NULL if undefined
   1200  */
   1201 static const struct DviFont *
   1202 find_font (const struct DviText *t,
   1203            uint32_t k)
   1204 {
   1205   unsigned int h = font_slot (k);
   1206 
   1207   while (0 != t->font_hash[h])
   1208   {
   1209     const struct DviFont *f = &t->fonts[t->font_hash[h] - 1];
   1210 
   1211     if (f->k == k)
   1212       return f;
   1213     h = (h + 1) & (DVI_FONT_HASH - 1);
   1214   }
   1215   return NULL;
   1216 }
   1217 
   1218 
   1219 /**
   1220  * Parse a fnt_def command (opcode already consumed) and register the
   1221  * font.
   1222  *
   1223  * @param t state
   1224  * @param pos position after the opcode, updated
   1225  * @param n number of bytes of the font number (1-4)
   1226  * @return 0 on success, -1 on malformed input
   1227  */
   1228 static int
   1229 parse_fnt_def (struct DviText *t,
   1230                size_t *pos,
   1231                unsigned int n)
   1232 {
   1233   uint32_t k;
   1234   uint32_t csum;
   1235   int64_t s;
   1236   uint32_t d;
   1237   uint32_t a;
   1238   uint32_t l;
   1239   unsigned int i;
   1240 
   1241   if ( (0 != read_u (t, pos, n, &k)) ||
   1242        (0 != read_u (t, pos, 4, &csum)) ||
   1243        (0 != read_s (t, pos, 4, &s)) ||
   1244        (0 != read_u (t, pos, 4, &d)) ||
   1245        (0 != read_u (t, pos, 1, &a)) ||
   1246        (0 != read_u (t, pos, 1, &l)) )
   1247     return -1;
   1248   if (t->size - *pos < a + l)
   1249     return -1;
   1250   if ( (NULL == find_font (t, k)) &&
   1251        (t->num_fonts < DVI_MAX_FONTS) )
   1252   {
   1253     unsigned int h = font_slot (k);
   1254 
   1255     while (0 != t->font_hash[h])
   1256       h = (h + 1) & (DVI_FONT_HASH - 1);
   1257     t->font_hash[h] = (uint16_t) (t->num_fonts + 1);
   1258     struct DviFont *f = &t->fonts[t->num_fonts++];
   1259 
   1260     f->k = k;
   1261     if (s < 0)
   1262       s = -s;
   1263     if ( (0 == s) ||
   1264          (s > (1 << 30)) )
   1265       s = DVI_DEFAULT_SIZE;
   1266     f->size = (double) s;
   1267     f->enc = dvi_font_encoding ((const char *) &t->data[*pos + a],
   1268                                 l);
   1269   }
   1270   *pos += a + l;
   1271   (void) csum;
   1272   (void) d;
   1273   return 0;
   1274 }
   1275 
   1276 
   1277 /**
   1278  * Interpret the page starting with the bop at @a bop and collect its
   1279  * text into a new page.
   1280  *
   1281  * @param t state
   1282  * @param bop offset of the page's bop command
   1283  */
   1284 static void
   1285 interpret_page (struct DviText *t,
   1286                 size_t bop)
   1287 {
   1288   struct DviRegs r;
   1289   struct DviRegs *stack;
   1290   unsigned int depth;
   1291   const struct DviFont *font;
   1292   size_t pos;
   1293   struct EXTRACTOR_FullTextPage *np;
   1294   struct EXTRACTOR_PageLineGeom **ng;
   1295 
   1296   np = realloc (t->pages,
   1297                 (t->num_pages + 1) * sizeof (*np));
   1298   if (NULL == np)
   1299   {
   1300     t->full = 1;
   1301     return;
   1302   }
   1303   t->pages = np;
   1304   ng = realloc (t->geoms,
   1305                 (t->num_pages + 1) * sizeof (*ng));
   1306   if (NULL == ng)
   1307   {
   1308     t->full = 1;
   1309     return;
   1310   }
   1311   t->geoms = ng;
   1312   memset (&t->pages[t->num_pages], 0, sizeof (*np));
   1313   t->geoms[t->num_pages] = NULL;
   1314   t->num_pages++;
   1315   t->geom_cap = 0;
   1316   stack = malloc (DVI_MAX_STACK * sizeof (*stack));
   1317   if (NULL == stack)
   1318   {
   1319     t->full = 1;
   1320     return;
   1321   }
   1322   memset (&r, 0, sizeof (r));
   1323   depth = 0;
   1324   font = NULL;
   1325   pos = bop + 45;
   1326   while ( (pos < t->size) &&
   1327           (! t->full) )
   1328   {
   1329     unsigned char op = t->data[pos++];
   1330     uint32_t u;
   1331     int64_t s;
   1332     int64_t s2;
   1333 
   1334     if (0 == t->budget)
   1335     {
   1336       t->full = 1;
   1337       break;
   1338     }
   1339     t->budget--;
   1340     if (0 == (t->budget & 0xFFFF))
   1341       (void) t->ec->seek (t->ec->cls,
   1342                           0,
   1343                           SEEK_CUR); /* keep the host from giving up */
   1344     if (op < 128)
   1345     {
   1346       r.h += (int64_t) put_glyph (t, font, op, r.h, r.v);
   1347       continue;
   1348     }
   1349     if ( (op >= 171) &&
   1350          (op <= 234) )
   1351     {
   1352       font = find_font (t, op - 171);
   1353       continue;
   1354     }
   1355     switch (op)
   1356     {
   1357     case 128: case 129: case 130: case 131: /* set1..4 */
   1358       if (0 != read_u (t, &pos, op - 127, &u))
   1359         goto DONE;
   1360       r.h += (int64_t) put_glyph (t, font, u, r.h, r.v);
   1361       break;
   1362     case 133: case 134: case 135: case 136: /* put1..4 */
   1363       if (0 != read_u (t, &pos, op - 132, &u))
   1364         goto DONE;
   1365       (void) put_glyph (t, font, u, r.h, r.v);
   1366       break;
   1367     case 132: /* set_rule */
   1368       if ( (0 != read_s (t, &pos, 4, &s)) ||
   1369            (0 != read_s (t, &pos, 4, &s2)) )
   1370         goto DONE;
   1371       r.h += s2;
   1372       break;
   1373     case 137: /* put_rule */
   1374       if ( (0 != read_s (t, &pos, 4, &s)) ||
   1375            (0 != read_s (t, &pos, 4, &s2)) )
   1376         goto DONE;
   1377       break;
   1378     case 138: /* nop */
   1379       break;
   1380     case 141: /* push */
   1381       if (depth >= DVI_MAX_STACK)
   1382         goto DONE;
   1383       stack[depth++] = r;
   1384       break;
   1385     case 142: /* pop */
   1386       if (0 == depth)
   1387         goto DONE;
   1388       r = stack[--depth];
   1389       break;
   1390     case 143: case 144: case 145: case 146: /* right1..4 */
   1391       if (0 != read_s (t, &pos, op - 142, &s))
   1392         goto DONE;
   1393       r.h += s;
   1394       break;
   1395     case 147: /* w0 */
   1396       r.h += r.w;
   1397       break;
   1398     case 148: case 149: case 150: case 151: /* w1..4 */
   1399       if (0 != read_s (t, &pos, op - 147, &s))
   1400         goto DONE;
   1401       r.w = s;
   1402       r.h += s;
   1403       break;
   1404     case 152: /* x0 */
   1405       r.h += r.x;
   1406       break;
   1407     case 153: case 154: case 155: case 156: /* x1..4 */
   1408       if (0 != read_s (t, &pos, op - 152, &s))
   1409         goto DONE;
   1410       r.x = s;
   1411       r.h += s;
   1412       break;
   1413     case 157: case 158: case 159: case 160: /* down1..4 */
   1414       if (0 != read_s (t, &pos, op - 156, &s))
   1415         goto DONE;
   1416       r.v += s;
   1417       break;
   1418     case 161: /* y0 */
   1419       r.v += r.y;
   1420       break;
   1421     case 162: case 163: case 164: case 165: /* y1..4 */
   1422       if (0 != read_s (t, &pos, op - 161, &s))
   1423         goto DONE;
   1424       r.y = s;
   1425       r.v += s;
   1426       break;
   1427     case 166: /* z0 */
   1428       r.v += r.z;
   1429       break;
   1430     case 167: case 168: case 169: case 170: /* z1..4 */
   1431       if (0 != read_s (t, &pos, op - 166, &s))
   1432         goto DONE;
   1433       r.z = s;
   1434       r.v += s;
   1435       break;
   1436     case 235: case 236: case 237: case 238: /* fnt1..4 */
   1437       if (0 != read_u (t, &pos, op - 234, &u))
   1438         goto DONE;
   1439       font = find_font (t, u);
   1440       break;
   1441     case 239: case 240: case 241: case 242: /* xxx1..4 */
   1442       if (0 != read_u (t, &pos, op - 238, &u))
   1443         goto DONE;
   1444       if (t->size - pos < u)
   1445         goto DONE;
   1446       pos += u;
   1447       break;
   1448     case 243: case 244: case 245: case 246: /* fnt_def1..4 */
   1449       if (0 != parse_fnt_def (t, &pos, op - 242))
   1450         goto DONE;
   1451       break;
   1452     case 140: /* eop */
   1453     default: /* bop, pre, post, post_post, undefined */
   1454       goto DONE;
   1455     }
   1456     /* keep the registers far away from overflowing */
   1457     if ( (r.h > INT64_C (1) << 50) || (r.h < -(INT64_C (1) << 50)) ||
   1458          (r.v > INT64_C (1) << 50) || (r.v < -(INT64_C (1) << 50)) )
   1459       goto DONE;
   1460   }
   1461 DONE:
   1462   flush_line (t);
   1463   free (stack);
   1464 }
   1465 
   1466 
   1467 /**
   1468  * Extract the full text of the DVI file and emit it.
   1469  *
   1470  * @param ec extraction context
   1471  * @param data the DVI file
   1472  * @param size number of bytes in @a data
   1473  * @param post offset of the postamble
   1474  * @param page_count number of pages
   1475  * @return 0 to continue, 1 if the callback asked us to stop
   1476  */
   1477 static int
   1478 dvi_fulltext (struct EXTRACTOR_ExtractContext *ec,
   1479               const unsigned char *data,
   1480               size_t size,
   1481               size_t post,
   1482               unsigned int page_count)
   1483 {
   1484   struct DviText *t;
   1485   size_t *bops;
   1486   size_t pos;
   1487   unsigned int n;
   1488   unsigned int i;
   1489   int64_t l = 0;
   1490   double top;
   1491   double bottom;
   1492   int ret = 0;
   1493 
   1494   if ( (0 == page_count) ||
   1495        (post > size) ||
   1496        (size - post < 29) )
   1497     return 0;
   1498   t = calloc (1, sizeof (*t));
   1499   if (NULL == t)
   1500     return 0;
   1501   t->ec = ec;
   1502   t->data = data;
   1503   t->size = size;
   1504   t->budget = 4 * (uint64_t) size + 1000000;
   1505   /* fonts from the postamble */
   1506   pos = post + 29;
   1507   while (pos < size)
   1508   {
   1509     unsigned char op = data[pos++];
   1510 
   1511     if (138 == op)
   1512       continue;
   1513     if ( (op < 243) ||
   1514          (op > 246) ||
   1515          (0 != parse_fnt_def (t, &pos, op - 242)) )
   1516       break;
   1517   }
   1518   pos = post + 17;
   1519   (void) read_s (t, &pos, 4, &l);
   1520   /* list of pages, in order */
   1521   if (page_count > DVI_MAX_PAGES)
   1522     page_count = DVI_MAX_PAGES;
   1523   bops = malloc (page_count * sizeof (size_t));
   1524   if (NULL == bops)
   1525   {
   1526     free (t);
   1527     return 0;
   1528   }
   1529   {
   1530     uint32_t p;
   1531     uint32_t prev;
   1532     unsigned int total;
   1533 
   1534     total = 0;
   1535     p = getIntAt (&data[post + 1]);
   1536     prev = UINT32_MAX;
   1537     while ( (UINT32_MAX != p) &&
   1538             ((size_t) p + 45 <= size) &&
   1539             (p < prev) &&
   1540             (139 == data[p]) )
   1541     {
   1542       total++;
   1543       prev = p;
   1544       p = getIntAt (&data[p + 41]);
   1545     }
   1546     n = (total < page_count) ? total : page_count;
   1547     p = getIntAt (&data[post + 1]);
   1548     prev = UINT32_MAX;
   1549     while ( (total > 0) &&
   1550             (UINT32_MAX != p) &&
   1551             ((size_t) p + 45 <= size) &&
   1552             (p < prev) &&
   1553             (139 == data[p]) )
   1554     {
   1555       total--;
   1556       if (total < n)
   1557         bops[total] = p;
   1558       prev = p;
   1559       p = getIntAt (&data[p + 41]);
   1560     }
   1561   }
   1562   for (i = 0; (i < n) && (! t->full); i++)
   1563     interpret_page (t, bops[i]);
   1564   free (bops);
   1565   /* relative vertical positions */
   1566   top = 0;
   1567   bottom = (l > 0) ? (double) l : 0;
   1568   if (t->have_v)
   1569   {
   1570     if ((double) t->vmin < top)
   1571       top = (double) t->vmin;
   1572     if ((double) t->vmax > bottom)
   1573       bottom = (double) t->vmax;
   1574   }
   1575   for (i = 0; i < t->num_pages; i++)
   1576   {
   1577     unsigned int j;
   1578 
   1579     for (j = 0; j < t->pages[i].num_lines; j++)
   1580     {
   1581       double y = -1;
   1582 
   1583       if (bottom > top)
   1584         y = (t->geoms[i][j].y - top) / (bottom - top);
   1585       if (y > 1)
   1586         y = 1;
   1587       t->pages[i].lines[j].y = y;
   1588     }
   1589   }
   1590   if (t->num_pages > 0)
   1591   {
   1592     struct EXTRACTOR_FullText ft;
   1593 
   1594     EXTRACTOR_fulltext_filter_margins_ (t->pages,
   1595                                         t->num_pages);
   1596     EXTRACTOR_pagelayout_paragraphs_ (t->pages,
   1597                                       t->geoms,
   1598                                       t->num_pages,
   1599                                       0.06);
   1600     EXTRACTOR_fulltext_init_ (&ft);
   1601     EXTRACTOR_fulltext_append_pages_ (&ft,
   1602                                       t->pages,
   1603                                       t->num_pages);
   1604     ret = EXTRACTOR_fulltext_emit_ (&ft,
   1605                                     ec,
   1606                                     "dvi");
   1607     EXTRACTOR_fulltext_clear_ (&ft);
   1608   }
   1609   for (i = 0; i < t->num_pages; i++)
   1610   {
   1611     EXTRACTOR_fulltext_page_clear_ (&t->pages[i]);
   1612     free (t->geoms[i]);
   1613   }
   1614   free (t->pages);
   1615   free (t->geoms);
   1616   free (t->lbuf);
   1617   free (t);
   1618   return (0 != ret) ? 1 : 0;
   1619 }
   1620 
   1621 
   1622 /**
   1623  * Main entry method for the 'application/x-dvi' extraction plugin.
   1624  *
   1625  * @param ec extraction context provided to the plugin
   1626  */
   1627 void
   1628 EXTRACTOR_dvi_extract_method (struct EXTRACTOR_ExtractContext *ec);
   1629 
   1630 void
   1631 EXTRACTOR_dvi_extract_method (struct EXTRACTOR_ExtractContext *ec)
   1632 {
   1633   unsigned int klen;
   1634   uint32_t pos;
   1635   uint32_t opos;
   1636   unsigned int len;
   1637   unsigned int pageCount;
   1638   char pages[16];
   1639   void *buf;
   1640   unsigned char *data;
   1641   uint64_t size;
   1642   uint64_t off;
   1643   ssize_t iret;
   1644   uint32_t post;
   1645 
   1646   if (40 >= (iret = ec->read (ec->cls, &buf, 1024)))
   1647     return;
   1648   data = buf;
   1649   if ( (data[0] != 247) ||
   1650        (data[1] != 2) )
   1651     return;                /* cannot be DVI or unsupported version */
   1652   klen = data[14];
   1653   size = ec->get_size (ec->cls);
   1654   if (size > 16 * 1024 * 1024)
   1655     return; /* too large */
   1656   if (klen + 15 > size)
   1657     return; /* malformed klen */
   1658   if (NULL == (data = malloc ((size_t) size)))
   1659     return; /* out of memory */
   1660   memcpy (data, buf, iret);
   1661   off = iret;
   1662   while (off < size)
   1663   {
   1664     if (0 >= (iret = ec->read (ec->cls, &buf, 16 * 1024)))
   1665     {
   1666       free (data);
   1667       return;
   1668     }
   1669     memcpy (&data[off], buf, iret);
   1670     off += iret;
   1671   }
   1672   pos = size - 1;
   1673   while ( (223 == data[pos]) &&
   1674           (pos > 0) )
   1675     pos--;
   1676   if ( (2 != data[pos]) ||
   1677        (pos < 40) )
   1678     goto CLEANUP;
   1679   pos--;
   1680   pos -= 4;
   1681   /* assert pos at 'post_post tag' */
   1682   if (data[pos] != 249)
   1683     goto CLEANUP;
   1684   opos = pos;
   1685   pos = getIntAt (&data[opos + 1]);
   1686   if ( (pos + 25 > size) ||
   1687        (pos + 25 < pos) )
   1688     goto CLEANUP;
   1689   /* assert pos at 'post' command */
   1690   if (data[pos] != 248)
   1691     goto CLEANUP;
   1692   post = pos;
   1693   pageCount = 0;
   1694   opos = pos;
   1695   pos = getIntAt (&data[opos + 1]);
   1696   while (1)
   1697   {
   1698     if (UINT32_MAX == pos)
   1699       break;
   1700     if ( (pos + 45 > size) ||
   1701          (pos + 45 < pos) )
   1702       goto CLEANUP;
   1703     if (data[pos] != 139)       /* expect 'bop' */
   1704       goto CLEANUP;
   1705     pageCount++;
   1706     opos = pos;
   1707     pos = getIntAt (&data[opos + 41]);
   1708     if (UINT32_MAX == pos)
   1709       break;
   1710     if (pos >= opos)
   1711       goto CLEANUP;       /* invalid! */
   1712   }
   1713   /* ok, now we believe it's a dvi... */
   1714   snprintf (pages,
   1715             sizeof (pages),
   1716             "%u",
   1717             pageCount);
   1718   if (0 != ec->proc (ec->cls,
   1719                      "dvi",
   1720                      EXTRACTOR_METATYPE_PAGE_COUNT,
   1721                      EXTRACTOR_METAFORMAT_UTF8,
   1722                      "text/plain",
   1723                      pages,
   1724                      strlen (pages) + 1))
   1725     goto CLEANUP;
   1726   if (0 != ec->proc (ec->cls,
   1727                      "dvi",
   1728                      EXTRACTOR_METATYPE_MIMETYPE,
   1729                      EXTRACTOR_METAFORMAT_UTF8,
   1730                      "text/plain",
   1731                      "application/x-dvi",
   1732                      strlen ("application/x-dvi") + 1))
   1733     goto CLEANUP;
   1734   {
   1735     char comment[klen + 1];
   1736 
   1737     comment[klen] = '\0';
   1738     memcpy (comment, &data[15], klen);
   1739     if (0 != ec->proc (ec->cls,
   1740                        "dvi",
   1741                        EXTRACTOR_METATYPE_COMMENT,
   1742                        EXTRACTOR_METAFORMAT_C_STRING,
   1743                        "text/plain",
   1744                        comment,
   1745                        strlen (comment) + 1))
   1746       goto CLEANUP;
   1747   }
   1748   /* try to find PDF/ps special */
   1749   pos = opos;
   1750   while ( (size >= 100) &&
   1751           (pos < size - 100) )
   1752   {
   1753     switch (data[pos])
   1754     {
   1755     case 139:                  /* begin page 'bop', we typically have to skip that one to
   1756                                    find the zzz's */
   1757       pos += 45;                /* skip bop */
   1758       break;
   1759     case 239:                  /* zzz1 */
   1760       len = data[pos + 1];
   1761       if ( (pos + 2 + len < size) &&
   1762            (0 != parseZZZ ((const char *) data, pos + 2, len, ec->proc,
   1763                            ec->cls)) )
   1764         goto CLEANUP;
   1765       pos += len + 2;
   1766       break;
   1767     case 240:                  /* zzz2 */
   1768       len = getShortAt (&data[pos + 1]);
   1769       if ( (pos + 3 + len < size) &&
   1770            (0 != parseZZZ ((const char *) data, pos + 3, len, ec->proc,
   1771                            ec->cls)) )
   1772         goto CLEANUP;
   1773       pos += len + 3;
   1774       break;
   1775     case 241:                  /* zzz3, who uses that? */
   1776       len = (getShortAt (&data[pos + 1])) + 65536 * data[pos + 3];
   1777       if ( (pos + 4 + len < size) &&
   1778            (0 != parseZZZ ((const char *) data, pos + 4, len, ec->proc,
   1779                            ec->cls)) )
   1780         goto CLEANUP;
   1781       pos += len + 4;
   1782       break;
   1783     case 242:                  /* zzz4, hurray! */
   1784       len = getIntAt (&data[pos + 1]);
   1785       if ( (pos + 5 + len < size) &&
   1786            (0 != parseZZZ ((const char *) data, pos + 5, len, ec->proc,
   1787                            ec->cls)) )
   1788         goto CLEANUP;
   1789       pos += len + 5;
   1790       break;
   1791     default:                   /* unsupported opcode, abort scan */
   1792       goto FULLTEXT;
   1793     }
   1794   }
   1795 FULLTEXT:
   1796   if (EXTRACTOR_fulltext_requested_ (ec->config))
   1797     (void) dvi_fulltext (ec,
   1798                          data,
   1799                          (size_t) size,
   1800                          post,
   1801                          pageCount);
   1802 CLEANUP:
   1803   free (data);
   1804 }
   1805 
   1806 
   1807 /* end of dvi_extractor.c */