texlive[52692] trunk: sync with luatex local svn repo.

commits+lscarso at tug.org commits+lscarso at tug.org
Fri Nov 8 10:36:31 CET 2019


Revision: 52692
          http://tug.org/svn/texlive?view=revision&revision=52692
Author:   lscarso
Date:     2019-11-08 10:36:30 +0100 (Fri, 08 Nov 2019)
Log Message:
-----------
sync with luatex local svn repo.

Modified Paths:
--------------
    trunk/Build/source/texk/web2c/luatexdir/ChangeLog
    trunk/Build/source/texk/web2c/luatexdir/font/luafont.c
    trunk/Build/source/texk/web2c/luatexdir/font/mapfile.c
    trunk/Build/source/texk/web2c/luatexdir/font/mapfile.h
    trunk/Build/source/texk/web2c/luatexdir/font/texfont.c
    trunk/Build/source/texk/web2c/luatexdir/font/texfont.h
    trunk/Build/source/texk/web2c/luatexdir/font/writefont.c
    trunk/Build/source/texk/web2c/luatexdir/font/writetype0.c
    trunk/Build/source/texk/web2c/luatexdir/font/writetype2.c
    trunk/Build/source/texk/web2c/luatexdir/lua/luatex-api.h
    trunk/Build/source/texk/web2c/luatexdir/luapplib/ppconf.h
    trunk/Build/source/texk/web2c/luatexdir/luatex.c
    trunk/Build/source/texk/web2c/luatexdir/luatex_svnversion.h
    trunk/Build/source/texk/web2c/luatexdir/tex/dumpdata.c
    trunk/Build/source/texk/web2c/luatexdir/tex/linebreak.c
    trunk/Master/texmf-dist/doc/luatex/base/luatex-fonts.tex
    trunk/Master/texmf-dist/doc/luatex/base/luatex-tex.tex
    trunk/Master/texmf-dist/doc/luatex/base/luatex.pdf

Removed Paths:
-------------
    trunk/Build/source/texk/web2c/luatexdir/lua/lpdfscannerlib.cc
    trunk/Build/source/texk/web2c/luatexdir/luapplib/ppload.c.orig

Modified: trunk/Build/source/texk/web2c/luatexdir/ChangeLog
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/ChangeLog	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/ChangeLog	2019-11-08 09:36:30 UTC (rev 52692)
@@ -1,3 +1,17 @@
+2019-11-08  Luigi Scarso <luigi.scarso at gmail.com>
+    * record a few more files (in kpse enabled mode) (HH)
+
+
+2019-11-05  Luigi Scarso <luigi.scarso at gmail.com>
+    * fixed calculation of  if (abs(fit_class - fitness(r)) > 1)
+      in lineabreak.c (thanks to M. Krüger). This produced different
+      results in Windows and Linux.
+
+
+2019-10-31  Luigi Scarso <luigi.scarso at gmail.com>
+    * subfont key in font table (to identify subfint in ttc) plus
+      some cleanup (HH)
+
 2019-10-28 Luigi Scarso <luigi.scarso at gmail.com>
     * New callback lua.getcodepage()  for diagnostic purposes.
 

Modified: trunk/Build/source/texk/web2c/luatexdir/font/luafont.c
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/font/luafont.c	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/font/luafont.c	2019-11-08 09:36:30 UTC (rev 52692)
@@ -478,6 +478,7 @@
     dump_intfield(L,width,font_width(f));
     dump_intfield(L,direction,font_natural_dir(f));
     dump_intfield(L,encodingbytes,font_encodingbytes(f));
+    dump_intfield(L,subfont,font_subfont(f));
     dump_booleanfield(L,oldmath,font_oldmath(f));
     dump_intfield(L,tounicode,font_tounicode(f));
     /*tex The next one is read only: */
@@ -500,7 +501,7 @@
     write_lua_math_parameters(L, f);
     /*tex Characters: */
     lua_push_string_by_name(L,characters);
-    lua_createtable(L, font_tables[f]->charinfo_size, 0);
+    lua_createtable(L, charinfo_size(f), 0);
     if (has_left_boundary(f)) {
         co = get_charinfo(f, left_boundarychar);
         lua_push_string_by_name(L,left_boundary);
@@ -1554,6 +1555,8 @@
     if (font_encodingbytes(f) == 0 && (font_format(f) == opentype_format || font_format(f) == truetype_format)) {
         set_font_encodingbytes(f, 2);
     }
+    i = lua_numeric_field_by_index(L,lua_key_index(subfont), 0);
+    set_font_subfont(f,i);
     /*tex Now fetch the base fonts, if needed. */
     count_hash_items(L, fonts, n);
     if (n > 0) {

Modified: trunk/Build/source/texk/web2c/luatexdir/font/mapfile.c
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/font/mapfile.c	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/font/mapfile.c	2019-11-08 09:36:30 UTC (rev 52692)
@@ -80,6 +80,7 @@
     fm->fd_flags = FD_FLAGS_NOT_SET_IN_MAPLINE;
     fm->ff_name = NULL;
     fm->encname = NULL;
+    fm->subfont = 0;
     fm->type = 0;
     fm->slant = 0;
     fm->extend = 1000;

Modified: trunk/Build/source/texk/web2c/luatexdir/font/mapfile.h
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/font/mapfile.h	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/font/mapfile.h	2019-11-08 09:36:30 UTC (rev 52692)
@@ -81,19 +81,20 @@
 
 typedef struct {
     /* parameters scanned from the map file: */
-    char *tfm_name;             /* TFM file name (1st field in map line) */
-    char *ps_name;              /* PostScript name (optional 2nd field in map line) */
-    int fd_flags;               /* font descriptor /Flags (PDF Ref. section 5.7.1) */
-    int slant;                  /* SlantFont */
-    int extend;                 /* ExtendFont */
-    char *encname;              /* encoding file name */
-    char *ff_name;              /* font file name */
-    unsigned short type;        /* various flags */
+    char *tfm_name;       /* TFM file name (1st field in map line) */
+    char *ps_name;        /* PostScript name (optional 2nd field in map line) */
+    int   fd_flags;       /* font descriptor /Flags (PDF Ref. section 5.7.1) */
+    int   slant;          /* SlantFont */
+    int   extend;         /* ExtendFont */
+    char *encname;        /* encoding file name */
+    int   subfont;        /* specific index in a ttc file */
+    char *ff_name;        /* font file name */
+    unsigned short type;  /* various flags */
 } fm_entry;
 
 typedef struct {
-    char *ff_name;              /* base name of font file */
-    char *ff_path;              /* full path to font file */
+    char *ff_name;        /* base name of font file */
+    char *ff_path;        /* full path to font file */
 } ff_entry;
 
 /**********************************************************************/

Modified: trunk/Build/source/texk/web2c/luatexdir/font/texfont.c
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/font/texfont.c	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/font/texfont.c	2019-11-08 09:36:30 UTC (rev 52692)
@@ -125,6 +125,8 @@
     set_font_bc(id, 1);
     set_font_writingmode(id, 0);
     set_font_identity(id, 0);
+ /* set_font_encoding_bytes(id,1) */ /* ? */
+    set_font_subfont(id, 0);
     set_hyphen_char(id, '-');
     set_skew_char(id, -1);
     /*tex vertical */
@@ -141,25 +143,24 @@
         set_font_param(id, k, 0);
     }
     /*tex character info zero is reserved for |notdef|. The stack size 1, default item value 0. */
-    font_tables[id]->characters = new_sa_tree(1, 1, sa_value);
+    font_tables[id]->_characters = new_sa_tree(1, 1, sa_value);
     ci = xcalloc(1, sizeof(charinfo));
     set_charinfo_name(ci, xstrdup(".notdef"));
-    font_tables[id]->charinfo = ci;
-    font_tables[id]->charinfo_size = 1;
-    font_tables[id]->charinfo_cache = NULL;
+    font_tables[id]->_charinfo = ci;
+    font_tables[id]->_charinfo_size = 1;
     return id;
 }
 
 void font_malloc_charinfo(internal_font_number f, int num)
 {
-    int glyph = font_tables[f]->charinfo_size;
+    int glyph = font_tables[f]->_charinfo_size;
     font_bytes += (int) (num * (int) sizeof(charinfo));
-    do_realloc(font_tables[f]->charinfo, (unsigned) (glyph + num), charinfo);
-    memset(&(font_tables[f]->charinfo[glyph]), 0, (size_t) (num * (int) sizeof(charinfo)));
-    font_tables[f]->charinfo_size += num;
+    do_realloc(font_tables[f]->_charinfo, (unsigned) (glyph + num), charinfo);
+    memset(&(font_tables[f]->_charinfo[glyph]), 0, (size_t) (num * (int) sizeof(charinfo)));
+    font_tables[f]->_charinfo_size += num;
 }
 
-#define find_charinfo_id(f,c) (get_sa_item(font_tables[f]->characters,c).int_value)
+#define find_charinfo_id(f,c) (get_sa_item(font_tables[f]->_characters,c).int_value)
 
 charinfo *get_charinfo(internal_font_number f, int c)
 {
@@ -166,20 +167,20 @@
     int glyph;
     charinfo *ci;
     if (proper_char_index(c)) {
-        glyph = get_sa_item(font_tables[f]->characters, c).int_value;
+        glyph = get_sa_item(font_tables[f]->_characters, c).int_value;
         if (!glyph) {
             sa_tree_item sa_value = { 0 };
-            int tglyph = ++font_tables[f]->charinfo_count;
-            if (tglyph >= font_tables[f]->charinfo_size) {
+            int tglyph = ++font_tables[f]->_charinfo_count;
+            if (tglyph >= font_tables[f]->_charinfo_size) {
                 font_malloc_charinfo(f, 256);
             }
-            font_tables[f]->charinfo[tglyph].ef = 1000;
+            font_tables[f]->_charinfo[tglyph].ef = 1000;
             sa_value.int_value = tglyph;
             /*tex 1 means global */
-            set_sa_item(font_tables[f]->characters, c, sa_value, 1);
+            set_sa_item(font_tables[f]->_characters, c, sa_value, 1);
             glyph = tglyph;
         }
-        return &(font_tables[f]->charinfo[glyph]);
+        return &(font_tables[f]->_charinfo[glyph]);
     } else if (c == left_boundarychar) {
         if (left_boundary(f) == NULL) {
             ci = xcalloc(1, sizeof(charinfo));
@@ -195,7 +196,7 @@
         }
         return right_boundary(f);
     }
-    return &(font_tables[f]->charinfo[0]);
+    return &(font_tables[f]->_charinfo[0]);
 }
 
 static void set_charinfo(internal_font_number f, int c, charinfo * ci)
@@ -202,9 +203,9 @@
 {
     int glyph;
     if (proper_char_index(c)) {
-        glyph = get_sa_item(font_tables[f]->characters, c).int_value;
+        glyph = get_sa_item(font_tables[f]->_characters, c).int_value;
         if (glyph) {
-            font_tables[f]->charinfo[glyph] = *ci;
+            font_tables[f]->_charinfo[glyph] = *ci;
         } else {
             normal_error("font","character insertion failed");
         }
@@ -319,13 +320,13 @@
         return 0;
     if (proper_char_index(c)) {
         register int glyph = (int) find_charinfo_id(f, c);
-        return &(font_tables[f]->charinfo[glyph]);
+        return &(font_tables[f]->_charinfo[glyph]);
     } else if (c == left_boundarychar && left_boundary(f) != NULL) {
         return left_boundary(f);
     } else if (c == right_boundarychar && right_boundary(f) != NULL) {
         return right_boundary(f);
     }
-    return &(font_tables[f]->charinfo[0]);
+    return &(font_tables[f]->_charinfo[0]);
 }
 
 scaled_whd get_charinfo_whd(internal_font_number f, int c)
@@ -1073,15 +1074,15 @@
     charinfo *ci;
     int k = new_font();
     {
-        ci = font_tables[k]->charinfo;
-        ci_cnt = font_tables[k]->charinfo_count;
-        ci_size = font_tables[k]->charinfo_size;
+        ci = font_tables[k]->_charinfo;
+        ci_cnt = font_tables[k]->_charinfo_count;
+        ci_size = font_tables[k]->_charinfo_size;
         memcpy(font_tables[k], font_tables[f], sizeof(texfont));
-        font_tables[k]->charinfo = ci;
-        font_tables[k]->charinfo_count = ci_cnt;
-        font_tables[k]->charinfo_size = ci_size;
+        font_tables[k]->_charinfo = ci;
+        font_tables[k]->_charinfo_count = ci_cnt;
+        font_tables[k]->_charinfo_size = ci_size;
     }
-    font_malloc_charinfo(k, font_tables[f]->charinfo_count);
+    font_malloc_charinfo(k, font_tables[f]->_charinfo_count);
     set_font_cache_id(k, 0);
     set_font_used(k, 0);
     set_font_touched(k, 0);
@@ -1110,6 +1111,8 @@
         set_font_cidregistry(k, xstrdup(font_cidregistry(f)));
     if (font_cidordering(f) != NULL)
         set_font_cidordering(k, xstrdup(font_cidordering(f)));
+    set_font_encodingbytes(k,font_encodingbytes(f));
+    set_font_subfont(k,font_subfont(f));
     i = (int) (sizeof(*param_base(f)) * (unsigned) (font_params(f)+1));
     font_bytes += i;
     param_base(k) = xmalloc((unsigned) (i+1));
@@ -1121,9 +1124,9 @@
         math_param_base(k) = xmalloc((unsigned) i);
         memcpy(math_param_base(k), math_param_base(f), (size_t) i);
     }
-    for (i = 0; i <= font_tables[f]->charinfo_count; i++) {
-        ci = copy_charinfo(&font_tables[f]->charinfo[i]);
-        font_tables[k]->charinfo[i] = *ci;
+    for (i = 0; i <= font_tables[f]->_charinfo_count; i++) {
+        ci = copy_charinfo(&font_tables[f]->_charinfo[i]);
+        font_tables[k]->_charinfo[i] = *ci;
     }
     if (left_boundary(f) != NULL) {
         ci = copy_charinfo(left_boundary(f));
@@ -1134,7 +1137,7 @@
         set_charinfo(k, right_boundarychar, ci);
     }
     /*tex Not updated yet: */
-    font_tables[k]->charinfo_count = font_tables[f]->charinfo_count;
+    font_tables[k]->_charinfo_count = font_tables[f]->_charinfo_count;
     return k;
 }
 
@@ -1167,9 +1170,9 @@
             }
         }
         /*tex free |notdef| */
-        set_charinfo_name(font_tables[f]->charinfo + 0, NULL);
-        free(font_tables[f]->charinfo);
-        destroy_sa_tree(font_tables[f]->characters);
+        set_charinfo_name(font_tables[f]->_charinfo + 0, NULL);
+        free(font_tables[f]->_charinfo);
+        destroy_sa_tree(font_tables[f]->_characters);
         free(param_base(f));
         if (math_param_base(f) != NULL)
             free(math_param_base(f));
@@ -1339,17 +1342,17 @@
 {
     int c;
     charinfo *co;
-    if (font_tables[f]->ligatures_disabled)
+    if (font_tables[f]->_ligatures_disabled)
         return;
     co = char_info(f, left_boundarychar);
     set_charinfo_ligatures(co, NULL);
     co = char_info(f, right_boundarychar);
     set_charinfo_ligatures(co, NULL);
-    for (c = 0; c < font_tables[f]->charinfo_count; c++) {
-        co = font_tables[f]->charinfo + c;
+    for (c = 0; c < font_tables[f]->_charinfo_count; c++) {
+        co = font_tables[f]->_charinfo + c;
         set_charinfo_ligatures(co, NULL);
     }
-    font_tables[f]->ligatures_disabled = 1;
+    font_tables[f]->_ligatures_disabled = 1;
 }
 
 liginfo get_ligature(internal_font_number f, int lc, int rc)
@@ -1496,6 +1499,7 @@
     dump_int(f->_font_touched);
     dump_int(f->_font_cache_id);
     dump_int(f->_font_encodingbytes);
+    dump_int(f->_font_subfont);
     dump_int(f->_font_oldmath);
     dump_int(f->_font_slant);
     dump_int(f->_font_extend);
@@ -1502,8 +1506,8 @@
     dump_int(f->_font_squeeze);
     dump_int(f->_font_mode);
     dump_int(f->_font_width);
-    dump_int(f->font_max_shrink);
-    dump_int(f->font_max_stretch);
+    dump_int(f->_font_max_shrink);
+    dump_int(f->_font_max_stretch);
     dump_int(f->_font_step);
     dump_int(f->_font_tounicode);
     dump_int(f->_font_type);
@@ -1518,7 +1522,7 @@
     dump_int(f->_font_natural_dir);
     dump_int(f->_font_params);
     dump_int(f->_font_math_params);
-    dump_int(f->ligatures_disabled);
+    dump_int(f->_ligatures_disabled);
     dump_int(f->_pdf_font_num);
     dump_int(f->_pdf_font_attr);
 }
@@ -1527,7 +1531,6 @@
 {
     int i, x;
     set_font_used(f, 0);
-    font_tables[f]->charinfo_cache = NULL;
     dump_font_entry(font_tables[f]);
     dump_string(font_name(f));
     dump_string(font_area(f));
@@ -1669,6 +1672,7 @@
     undump_int(x); f->_font_touched = (char)x;
     undump_int(x); f->_font_cache_id = x;
     undump_int(x); f->_font_encodingbytes = (char)x;
+    undump_int(x); f->_font_subfont = x;
     undump_int(x); f->_font_oldmath = x;
     undump_int(x); f->_font_slant = x;
     undump_int(x); f->_font_extend = x;
@@ -1675,8 +1679,8 @@
     undump_int(x); f->_font_squeeze = x;
     undump_int(x); f->_font_mode = x;
     undump_int(x); f->_font_width = x;
-    undump_int(x); f->font_max_shrink = x;
-    undump_int(x); f->font_max_stretch = x;
+    undump_int(x); f->_font_max_shrink = x;
+    undump_int(x); f->_font_max_stretch = x;
     undump_int(x); f->_font_step = x;
     undump_int(x); f->_font_tounicode = (char)x;
     undump_int(x); f->_font_type = x;
@@ -1691,7 +1695,7 @@
     undump_int(x); f->_font_natural_dir = x;
     undump_int(x); f->_font_params = x;
     undump_int(x); f->_font_math_params = x;
-    undump_int(x); f->ligatures_disabled = x;
+    undump_int(x); f->_ligatures_disabled = x;
     undump_int(x); f->_pdf_font_num = x;
     undump_int(x); f->_pdf_font_attr = x;
 }
@@ -1729,10 +1733,10 @@
         undump_things(*math_param_base(f), (font_math_params(f) + 1));
     }
     /*tex stack size 1, default item value 0 */
-    font_tables[f]->characters = new_sa_tree(1, 1, sa_value);
+    font_tables[f]->_characters = new_sa_tree(1, 1, sa_value);
     ci = xcalloc(1, sizeof(charinfo));
     set_charinfo_name(ci, xstrdup(".notdef"));
-    font_tables[f]->charinfo = ci;
+    font_tables[f]->_charinfo = ci;
     undump_int(x);
     if (x) {
         /*tex left boundary */

Modified: trunk/Build/source/texk/web2c/luatexdir/font/texfont.h
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/font/texfont.h	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/font/texfont.h	2019-11-08 09:36:30 UTC (rev 52692)
@@ -129,67 +129,60 @@
 extern scaled_whd get_charinfo_whd(internal_font_number f, int c);
 
 typedef struct texfont {
-    int _font_size;
-    int _font_dsize;
-    int _font_units_per_em;
-    char *_font_name;
-    char *_font_area;
-    char *_font_filename;
-    char *_font_fullname;
-    char *_font_psname;
-    char *_font_encodingname;
-    char *_font_cidregistry;
-    char *_font_cidordering;
-    int _font_cidversion;
-    int _font_cidsupplement;
-
-    int _font_ec;
-    unsigned _font_checksum;    /* internal information */
-    char _font_used;            /* internal information */
-    char _font_touched;         /* internal information */
-    int _font_cache_id;         /* internal information */
-    char _font_encodingbytes;   /* 1 or 2 bytes */
-    boolean _font_oldmath;      /* default to false when MathConstants seen */
-    int _font_slant;            /* a slant in ppt */
-    int _font_extend;           /* an extension in ppt, or 1000 */
-    int _font_squeeze;          /* an extension in ppt, or 1000 */
-    int _font_width;
-    int _font_mode;
-    int font_max_shrink;
-    int font_max_stretch;
-    int _font_step;             /* amount of one step of expansion */
-
-    char _font_tounicode;       /* 1 if info is present */
-    fm_entry *_font_map;
-    int _font_type;
-    int _font_format;
-    int _font_writingmode;
-    int _font_identity;
-    int _font_embedding;
-    int _font_streamprovider;
-    int _font_bc;
-    int _hyphen_char;
-    int _skew_char;
-    int _font_natural_dir;
-
-    charinfo *_left_boundary;
-    charinfo *_right_boundary;
-
-    int _font_params;
-    scaled *_param_base;
-
-    int _font_math_params;
-    scaled *_math_param_base;
-
-    sa_tree characters;
-    int charinfo_count;
-    int charinfo_size;
-    charinfo *charinfo;
-    int *charinfo_cache;
-    int ligatures_disabled;
-
-    int _pdf_font_num;          /* maps to a PDF resource ID */
-    str_number _pdf_font_attr;  /* pointer to additional attributes */
+    int         _font_size;
+    int         _font_dsize;
+    int         _font_units_per_em;
+    char       *_font_name;
+    char       *_font_area;
+    char       *_font_filename;
+    char       *_font_fullname;
+    char       *_font_psname;
+    char       *_font_encodingname;
+    char       *_font_cidregistry;
+    char       *_font_cidordering;
+    int         _font_cidversion;
+    int         _font_cidsupplement;
+    int         _font_ec;
+    unsigned    _font_checksum;       /* internal information */
+    char        _font_used;           /* internal information */
+    char        _font_touched;        /* internal information */
+    int         _font_cache_id;       /* internal information */
+    char        _font_encodingbytes;  /* 1 or 2 bytes */
+    int         _font_subfont;
+    boolean     _font_oldmath;        /* default to false when MathConstants seen */
+    int         _font_slant;          /* a slant in ppt */
+    int         _font_extend;         /* an extension in ppt, or 1000 */
+    int         _font_squeeze;        /* an extension in ppt, or 1000 */
+    int         _font_width;
+    int         _font_mode;
+    int         _font_max_shrink;
+    int         _font_max_stretch;
+    int         _font_step;           /* amount of one step of expansion */
+    char        _font_tounicode;      /* 1 if info is present */
+    fm_entry   *_font_map;
+    int         _font_type;
+    int         _font_format;
+    int         _font_writingmode;
+    int         _font_identity;
+    int         _font_embedding;
+    int         _font_streamprovider;
+    int         _font_bc;
+    int         _hyphen_char;
+    int         _skew_char;
+    int         _font_natural_dir;
+    charinfo   *_left_boundary;
+    charinfo   *_right_boundary;
+    int         _font_params;
+    scaled     *_param_base;
+    int         _font_math_params;
+    scaled     *_math_param_base;
+    sa_tree     _characters;
+    int         _charinfo_count;
+    int         _charinfo_size;
+    charinfo   *_charinfo;
+    int         _ligatures_disabled;
+    int         _pdf_font_num;        /* maps to a PDF resource ID */
+    str_number  _pdf_font_attr;       /* pointer to additional attributes */
 } texfont;
 
 typedef enum {
@@ -330,6 +323,9 @@
 #  define font_encodingbytes(a)          font_tables[a]->_font_encodingbytes
 #  define set_font_encodingbytes(a,b)    font_encodingbytes(a) = b
 
+#  define font_subfont(a)                font_tables[a]->_font_subfont
+#  define set_font_subfont(a,b)          font_subfont(a) = b
+
 #  define font_streamprovider(a)         font_tables[a]->_font_streamprovider
 #  define set_font_streamprovider(a,b)   font_streamprovider(a) = b
 
@@ -358,10 +354,10 @@
 #  define font_stretch(a)                font_tables[a]->_font_stretch
 #  define set_font_stretch(a,b)          font_stretch(a) = b
 
-#  define font_max_shrink(a)             font_tables[a]->font_max_shrink
+#  define font_max_shrink(a)             font_tables[a]->_font_max_shrink
 #  define set_font_max_shrink(a,b)       font_max_shrink(a) = b
 
-#  define font_max_stretch(a)            font_tables[a]->font_max_stretch
+#  define font_max_stretch(a)            font_tables[a]->_font_max_stretch
 #  define set_font_max_stretch(a,b)      font_max_stretch(a) = b
 
 #  define font_step(a)                   font_tables[a]->_font_step
@@ -385,6 +381,8 @@
 #  define pdf_font_attr(a)               font_tables[a]->_pdf_font_attr
 #  define set_pdf_font_attr(a,b)         pdf_font_attr(a) = b
 
+#  define charinfo_size(a)               font_tables[a]->_charinfo_size
+
 #  define left_boundarychar  -1
 #  define right_boundarychar -2
 #  define non_boundarychar   -3
@@ -467,7 +465,7 @@
     glyph id, not one of the two special boundary objects.
 */
 
-#  define quick_char_exists(f,c) (get_sa_item(font_tables[f]->characters,c).int_value)
+#  define quick_char_exists(f,c) (get_sa_item(font_tables[f]->_characters,c).int_value)
 
 extern void set_charinfo_width(charinfo * ci, scaled val);
 extern void set_charinfo_height(charinfo * ci, scaled val);

Modified: trunk/Build/source/texk/web2c/luatexdir/font/writefont.c
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/font/writefont.c	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/font/writefont.c	2019-11-08 09:36:30 UTC (rev 52692)
@@ -814,6 +814,7 @@
         }
         /*tex Needed for the CIDSystemInfo: */
         fm->encname = font_encodingname(f);
+        fm->subfont = font_subfont(f);
         fm->slant = font_slant(f);
         set_slantset(fm);
         fm->extend = font_extend(f);

Modified: trunk/Build/source/texk/web2c/luatexdir/font/writetype0.c
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/font/writetype0.c	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/font/writetype0.c	2019-11-08 09:36:30 UTC (rev 52692)
@@ -48,8 +48,7 @@
     assert(is_included(fd_cur->fm));
     ttf_curbyte = 0;
     ttf_size = 0;
-    cur_file_name =
-        luatex_find_file(fd_cur->fm->ff_name, find_opentype_file_callback);
+    cur_file_name = luatex_find_file(fd_cur->fm->ff_name, find_opentype_file_callback);
     if (cur_file_name == NULL) {
         cur_file_name =
             luatex_find_file(fd_cur->fm->ff_name, find_truetype_file_callback);
@@ -75,18 +74,15 @@
     fd_cur->ff_found = true;
     sfont = sfnt_open(ttf_buffer, ttf_size);
     if (sfont->type == SFNT_TYPE_TTC)
-        i = ff_get_ttc_index(fd->fm->ff_name, fd->fm->ps_name);
-
+        i = fd->fm->subfont > 0 ? (fd->fm->subfont - 1): ff_get_ttc_index(fd->fm->ff_name, fd->fm->ps_name);
     if (is_subsetted(fd_cur->fm)) {
         report_start_file(filetype_subset, cur_file_name);
     } else {
         report_start_file(filetype_font, cur_file_name);
     }
-
     if (sfont->type == SFNT_TYPE_TTC) otc_read_tabdir(i);
     else ttf_read_tabdir();
     sfnt_close(sfont);
-
     /*tex Read font parameters: */
     if (ttf_name_lookup("head", false) != NULL)
         ttf_read_head();

Modified: trunk/Build/source/texk/web2c/luatexdir/font/writetype2.c
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/font/writetype2.c	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/font/writetype2.c	2019-11-08 09:36:30 UTC (rev 52692)
@@ -237,7 +237,7 @@
     cidtogidmap = NULL;
     sfont = sfnt_open(buff, buflen);
     if (sfont->type == SFNT_TYPE_TTC) {
-        i = ff_get_ttc_index(fd->fm->ff_name, fd->fm->ps_name);
+        i = fd->fm->subfont > 0 ? (fd->fm->subfont - 1) : ff_get_ttc_index(fd->fm->ff_name, fd->fm->ps_name);
         error = sfnt_read_table_directory(sfont, ttc_read_offset(sfont, (int) i, fd));
     } else {
         error = sfnt_read_table_directory(sfont, 0);
@@ -306,6 +306,7 @@
     fontfile = sfnt_create_FontFile_stream(sfont);
     /*tex The |cidgidmap|: */
     if (cidtogidmap != NULL) {
+        /* never seen when ndef NO_GHOSTSCRIPT_BUG */
         cidtogid_obj = (unsigned long) pdf_create_obj(pdf, obj_type_others, 0);
         pdf_begin_obj(pdf, (int) cidtogid_obj, OBJSTM_NEVER);
         pdf_begin_dict(pdf);

Deleted: trunk/Build/source/texk/web2c/luatexdir/lua/lpdfscannerlib.cc
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/lua/lpdfscannerlib.cc	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/lua/lpdfscannerlib.cc	2019-11-08 09:36:30 UTC (rev 52692)
@@ -1,1041 +0,0 @@
-/* lpdfscannerlib.c
-
-   Copyright 2013 Taco Hoekwater <taco at luatex.org>
-
-   This file is part of LuaTeX.
-
-   LuaTeX is free software; you can redistribute it and/or modify it under
-   the terms of the GNU General Public License as published by the Free
-   Software Foundation; either version 2 of the License, or (at your
-   option) any later version.
-
-   LuaTeX is distributed in the hope that it will be useful, but WITHOUT
-   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
-   License for more details.
-
-   You should have received a copy of the GNU General Public License along
-   with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
-
-/* converted example in manual:
-
-local operatortable = { }
-
-operatortable.Do = function(scanner,info)
-    local resources = info.resources
-    if resources then
-        local val     = scanner:pop()
-        local name    = val[2]
-        local xobject = pdfe.dictionarytotable(resources.XObject[2])
-        print(info.space .. "Uses XObject " .. name)
-        local kind,
-              entry  = unpack(xobject[name])
-        local dict
-        if kind == 10 then -- reference
-            kind,
-            entry,
-            dict = pdfe.getfromreference(entry)
-        end
-        if kind == 9 then -- stream
-            dict = pdfe.dictionarytotable(dict)
-            local resources = dict.Resources
-            if resources then
-                local newinfo =  {
-                    space     = info.space .. "  " ,
-                    resources = Resources[2],
-                }
-                pdfscanner.scan(entry, operatortable, newinfo)
-            end
-        end
-    end
-end
-
-local function Analyze(filename)
-    local doc = pdfe.open(filename)
-    if doc then
-        local pagenum  = 1
-        local n        = 1 -- pdfe.getnofpages(doc)
-        for i=1,n do
-            local page = pdfe.getpage(doc,i)
-            local kind,
-                  data = pdfe.getfromdictionarybyname(page,"Resources")
-            if kind == 10 then -- reference
-                kind, data = pdfe.getfromreference(data)
-            end
-            data = pdfe.dictionarytotable(data)
-            local info = {
-              space     = "  " ,
-              resources = data,
-            }
-            print("Page " .. i)
-            kind, data = pdfe.getfromdictionarybyname(page,"Contents")
-            if kind == 10 then -- reference
-                kind, data = pdfe.getfromreference(data)
-            end
-            pdfscanner.scan(data,operatortable,info)
-        end
-    end
-end
-
-Analyze("e:/tmp/oeps.pdf")
-
-*/
-
-#  include <stdlib.h>
-#  include <stdio.h>
-#  include <stdarg.h>
-#  include <string.h>
-#  include <assert.h>
-#  include <math.h>
-
-extern "C" {
-#  include <lua.h>
-#  include <lauxlib.h>
-#  include <lualib.h>
-
-#  include "luapplib/pplib.h"
-}
-
-#  include <lua/luatex-api.h>
-
-
-#define SCANNER "pdfscanner"
-
-#define MAXOPERANDS 1000
-
-typedef enum {
-    pdf_integer = 1,
-    pdf_real,
-    pdf_boolean,
-    pdf_name,
-    pdf_operator,
-    pdf_string,
-    pdf_startarray,
-    pdf_stoparray,
-    pdf_startdict,
-    pdf_stopdict,
-} pdf_token_type ;
-
-typedef struct Token {
-    pdf_token_type type;
-    double value;
-    char *string;
-} Token;
-
-typedef struct ObjectList {
-    struct ObjectList *next;
-    ppstream *stream;
-} ObjectList;
-
-typedef struct scannerdata {
-    int _ininlineimage;
-    int _nextoperand;
-    Token ** _operandstack;
-    ppstream * _stream;
-    ObjectList * _streams;
-    const char *buffer;
-    size_t position;
-    size_t size;
-} scannerdata;
-
-#define PDFE_METATABLE_ARRAY  "luatex.pdfe.array"
-#define PDFE_METATABLE_STREAM "luatex.pdfe.stream"
-
-typedef struct {
-    void *d;
-    void *pd;            // reference to PdfDocument, or NULL
-    unsigned long pc;    // counter to detect PDFDoc change
-} udstruct;
-
-static void clear_operand_stack (scannerdata *self, int from);
-static Token *_parseToken (scannerdata *self, int c);
-static void push_token (lua_State *L, scannerdata *self);
-
-static void *priv_xmalloc (size_t size)
-{
-    void *new_mem = (void *)malloc(size);
-    if (new_mem == NULL) {
-        fprintf(stderr, "fatal: memory exhausted (priv_xmalloc of %lu bytes).\n", (unsigned long)size);
-        exit(1);
-    }
-    return new_mem;
-}
-
-static void *priv_xrealloc (void *old_ptr, size_t size)
-{
-    void *new_mem = (void *)realloc(old_ptr, size);
-    if (new_mem == NULL) {
-        fprintf(stderr,"fatal: memory exhausted (realloc of %lu bytes).\n", (unsigned long)size);
-        exit(EXIT_FAILURE);
-    }
-    return new_mem;
-}
-
-#define xreallocarray(ptr,type,size) ((type*)priv_xrealloc(ptr,(size+1)*sizeof(type)))
-
-#define INITBUFSIZE 64
-
-#define define_buffer(a) \
-  char *a = (char *)priv_xmalloc (INITBUFSIZE); \
-  int a##_size = INITBUFSIZE; \
-  int a##index = 0; \
-  memset (a,0,INITBUFSIZE)
-
-#define check_overflow(a, wsize) do { \
-    if (wsize >= a##_size) { \
-        int nsize = a##_size + a##_size / 4; \
-        a = (char *) xreallocarray(a, char, (unsigned) nsize); \
-        memset (a+a##_size, 0, a##_size / 4); \
-        a##_size = nsize; \
-    } \
-} while (0)
-
-
-static scannerdata * scanner_push(lua_State * L)
-{
-    scannerdata *a = (scannerdata *)lua_newuserdata(L, sizeof(scannerdata));
-    luaL_getmetatable(L, SCANNER);
-    lua_setmetatable(L, -2);
-    return a;
-}
-
-static scannerdata *scanner_check (lua_State *L, int index)
-{
-    scannerdata *bar;
-    luaL_checktype(L, index, LUA_TUSERDATA);
-    bar = (scannerdata *)luaL_checkudata(L, index, SCANNER);
-    if (bar == NULL)
-        luaL_argerror(L, index, SCANNER " expected");
-    return bar;
-}
-
-static void free_token (Token *token)
-{
-    if (token->string) {
-        free(token->string);
-    }
-    free(token);
-}
-
-static void clear_operand_stack (scannerdata *self, int from)
-{
-    int i = self->_nextoperand-1;
-        while (i>=from) {
-            if (self->_operandstack[i]) {
-            free_token(self->_operandstack[i]);
-            self->_operandstack[i] = NULL;
-        }
-        i--;
-    }
-    self->_nextoperand = from;
-}
-
-static void push_operand (scannerdata *self, Token *token)
-{
-    if (self->_nextoperand+1> MAXOPERANDS) {
-        fprintf(stderr, "out of operand stack space");
-        exit(1);
-    }
-    self->_operandstack[self->_nextoperand++] = token;
-}
-
-static Token * new_operand (pdf_token_type c)
-{
-    Token *token = (Token *)priv_xmalloc(sizeof(Token));
-    memset (token, 0, sizeof(Token));
-    token->type = c;
-    return token;
-}
-
-static void _nextStream (scannerdata *self)
-{
-    if (self->buffer != NULL) {
-        ppstream_done(self->_stream);
-    }
-    ObjectList *rover = self->_streams;
-    self->_stream = rover->stream;
-    self->buffer = (const char*) ppstream_all(self->_stream,&self->size,1);
-    self->position = 0;
-    self->_streams = rover->next;
-    free(rover);
-}
-
-static int streamGetChar (scannerdata *self)
-{
-    int i = EOF ;
-    if (self->position < self->size) {
-        const char c = self->buffer[self->position];
-        ++self->position;
-        i = (int) c;
-    }
-    if (i<0 && self->_streams) {
-        _nextStream(self);
-        i = streamGetChar(self);
-    }
-    return i;
-}
-
-static int streamLookChar (scannerdata *self)
-{
-    int i = EOF ;
-    if (self->position < self->size) {
-        const char c = self->buffer[self->position];
-     /* ++self->position; */
-        i = (int) c;
-    }
-    if (i<0 && self->_streams) {
-        _nextStream(self);
-        i = streamGetChar(self);
-    }
-    return i;
-}
-
-static void streamReset (scannerdata *self)
-{
-    self->buffer = (const char*) ppstream_all(self->_stream,&self->size,1);
-    self->position = 0;
-}
-
-static void streamClose (scannerdata *self) {
-    ppstream_done(self->_stream);
-    self->buffer = NULL;
-    self->_stream = NULL;
-}
-
-/* end of stream interface */
-
-static Token * _parseSpace (scannerdata *self)
-{
-    return _parseToken (self,streamGetChar(self));
-}
-
-static Token * _parseString (scannerdata *self, int c)
-{
-    define_buffer(found);
-    int level = 1;
-    while (1) {
-        c = streamGetChar(self);
-        if (c == '(') {
-            level = level + 1 ;
-        } else if (c == ')') {
-            level = level - 1 ;
-            if (level < 1) break;
-        } else if (c == '\\') {
-            int next = streamGetChar(self);
-            if (next == '(' || next == ')' || next == '\\') {
-                c = next;
-            } else if (next == '\n' || next == '\r') {
-                c = '\0';
-            } else if (next == 'n') {
-                c = '\n';
-            } else if (next == 'r') {
-                c = '\r';
-            } else if (next == 't') {
-                c = '\t';
-            } else if (next == 'b') {
-                c = '\b';
-            } else if (next == 'f') {
-                c = '\f';
-            } else if (next >= '0' && next <= '7') {
-                next = next - '0';
-                int next2 = streamLookChar(self);
-                if (next2 >= '0' && next2 <= '7') {
-                    next2 = streamGetChar(self);
-                    next2 = next2 - '0';
-                    int next3 = streamLookChar(self);
-                    if (next3 >= '0' && next3 <= '7') {
-                        next3 = streamGetChar(self);
-                        next3 = next3 - '0';
-                        c = (next*64+next2*8+next3);
-                    } else {
-                        c = (next*8+next2);
-                    }
-                } else {
-                    c = next;
-                }
-            } else {
-                c = next;
-            }
-        }
-        check_overflow(found,foundindex);
-        if (c>=0) {
-            found[foundindex++] = c;
-        }
-    }
-    Token *token = new_operand(pdf_string);
-    token->value = foundindex;
-    token->string = found;
-    return token;
-}
-
-static Token * _parseNumber (scannerdata *self, int c)
-{
-    double value = 0;
-    pdf_token_type type = pdf_integer;
-    int isfraction = 0;
-    int isnegative = 0;
-    int i = 0;
-    if (c == '-') {
-        isnegative = 1;
-        c = streamGetChar(self);
-    }
-    if (c == '.') {
-        type = pdf_real;
-        isfraction = 1;
-    } else  {
-        value = c - '0';
-    }
-    c = streamLookChar(self);
-    if ((c>= '0'&&  c<= '9') || c == '.') {
-        c = streamGetChar(self);
-        while (1) {
-            if (c == '.') {
-                type = pdf_real;
-                isfraction = 1;
-            } else {
-                i = c - '0';
-                if (isfraction>0) {
-                    value = value + (i/(pow(10.0,isfraction)));
-                    isfraction = isfraction + 1;
-                } else {
-                    value = (value * 10) + i;
-                }
-            }
-            c = streamLookChar(self);
-            if (! ((c>= '0' && c<= '9') ||  c == '.'))
-                break ;
-            c = streamGetChar(self);
-        }
-    }
-    if (isnegative) {
-        value = -value;
-    }
-    Token *token = new_operand(type);
-    token->value = value;
-    return token;
-}
-
-static Token *_parseName (scannerdata *self, int c)
-{
-    define_buffer(found);
-    c = streamGetChar(self);
-    while (1) {
-        check_overflow(found,foundindex);
-        found[foundindex++] = c;
-        c = streamLookChar(self);
-        if (c == ' ' || c == '\n' || c == '\r' || c == '\t' ||
-            c == '/' || c == '[' || c == '(' || c == '<') break ;
-        c = streamGetChar(self);
-    }
-    Token *token = new_operand(pdf_name);
-    token->string = found;
-    token->value = strlen(found);
-    return token;
-}
-
-#define hexdigit(c)	\
-  (c>= '0' && c<= '9') ? (c - '0') : ((c>= 'A' && c<= 'F') ? (c - 'A' + 10) : (c - 'a' + 10))
-
-static Token *_parseHexstring (scannerdata *self, int c)
-{
-    int isodd = 1;
-    int hexval = 0;
-    define_buffer(found);
-    while (c != '>') {
-        if ((c>= '0' && c<= '9') || (c>= 'A' && c<= 'F') || (c>= 'a' && c<= 'f')) {
-            if (isodd==1) {
-                int v = hexdigit(c);
-                hexval = 16 * v;
-            } else {
-                hexval += hexdigit(c);
-                check_overflow(found,foundindex);
-                found[foundindex++] = hexval;
-            }
-            isodd = (isodd==1 ? 0 : 1);
-        }
-        c = streamGetChar(self);
-    }
-    Token *token = new_operand(pdf_string);
-    token->value = foundindex;
-    token->string = found;
-    return token;
-}
-
-#define pdf_isspace(a) (a == '\0' || a == ' ' || a == '\n' || a == '\r' || a == '\t' || a == '\v')
-
-// -- this is rather horrible
-
-static Token *_parseInlineImage (scannerdata *self, int c)
-{
-    define_buffer(found);
-    if (c == ' ') { // first space can be ignored
-        c = streamGetChar(self);
-    }
-    check_overflow(found, foundindex);
-    found[foundindex++] = c;
-    while (1) {
-        c = streamLookChar(self);
-        if (c == 'E' && (found[foundindex-1] == '\n' || found[foundindex-1] == '\r')) {
-            c = streamGetChar(self);
-            check_overflow(found, foundindex);
-            found[foundindex++] = c;
-            c = streamLookChar(self);
-            if (c == 'I') {
-                c = streamGetChar(self);
-                check_overflow(found, foundindex);
-                found[foundindex++] = c;
-                c = streamLookChar(self);
-                if (pdf_isspace(c)) {
-                    found[--foundindex] = '\0'; /* I */
-                    found[--foundindex] = '\0'; /* E */
-                    /* remove end-of-line before EI */
-                    if (found[foundindex-1] == '\n') {
-                        found[--foundindex] = '\0';
-                    }
-                    if (found[foundindex-1] == '\r') {
-                        found[--foundindex] = '\0';
-                    }
-                    break;
-                } else {
-                    c = streamGetChar(self);
-                    check_overflow(found, foundindex);
-                    found[foundindex++] = c;
-                }
-            } else {
-                c = streamGetChar(self);
-                check_overflow(found, foundindex);
-                found[foundindex++] = c;
-            }
-        } else {
-            c = streamGetChar(self);
-            check_overflow(found, foundindex);
-            found[foundindex++] = c;
-        }
-    }
-    Token *token = new_operand(pdf_string);
-    token->value = foundindex;
-    token->string = found;
-    return token;
-}
-
-static Token *_parseOperator (scannerdata *self, int c)
-{
-    define_buffer(found);
-    while (1) {
-        check_overflow(found, foundindex);
-        found[foundindex++] = c;
-        c = streamLookChar(self);
-        if ((c<0) || (c == ' ' || c == '\n' || c == '\r' || c == '\t' ||
-                c == '/' || c == '[' || c == '(' || c == '<'))
-            break ;
-        c = streamGetChar(self);
-    }
-    // print (found)
-    if (strcmp(found, "ID") == 0) {
-        self->_ininlineimage = 1;
-    }
-    if (strcmp(found,"false") == 0) {
-        Token *token = new_operand(pdf_boolean);
-        token->value = 0;
-        free(found);
-        return token;
-    } else if (strcmp(found,"true") == 0) {
-        Token *token = new_operand(pdf_boolean);
-        token->value = 1.0;
-        free(found);
-        return token;
-    } else {
-        Token *token = new_operand(pdf_operator);
-        token->string = found;
-        return token;
-    }
-}
-
-
-static Token * _parseComment  (scannerdata *self, int c)
-{
-    do {
-        c = streamGetChar(self);
-    } while (c != '\n' && c != '\r' && c != -1);
-    return _parseToken(self,streamGetChar(self));
-}
-
-static Token *_parseLt (scannerdata *self, int c)
-{
-    c = streamGetChar(self);
-    if (c == '<') {
-        return new_operand(pdf_startdict);
-    } else {
-        return _parseHexstring(self,c);
-    }
-}
-
-static Token * _parseGt (scannerdata *self, int c)
-{
-    c = streamGetChar(self);
-    if (c== '>') {
-        return new_operand(pdf_stopdict);
-    } else {
-        fprintf(stderr,"stray > in stream");
-        return NULL;
-    }
-}
-
-static Token *_parseError (int c)
-{
-    fprintf(stderr, "stray %c [%d] in stream", c, c);
-    return NULL;
-}
-
-static Token *_parseStartarray ()
-{
-    return new_operand (pdf_startarray);
-}
-
-static Token *_parseStoparray ()
-{
-    return new_operand (pdf_stoparray);
-}
-
-
-static Token *_parseToken (scannerdata *self, int c)
-{
-    if (self->_ininlineimage==1) {
-        self->_ininlineimage = 2;
-        return _parseInlineImage(self,c);
-    } else if (self->_ininlineimage==2) {
-        self->_ininlineimage = 0;
-        Token *token = new_operand(pdf_operator);
-        token->string = strdup("EI");
-        return token;
-    }
-    if (c<0)
-        return NULL ;
-    switch (c) {
-        case '(':
-            return _parseString(self,c);
-            break;
-        case ')':
-            return _parseError(c);
-            break;
-        case '[':
-            return _parseStartarray();
-            break;
-        case ']':
-            return _parseStoparray();
-            break;
-        case '/':
-            return _parseName(self,c);
-            break;
-        case '<':
-            return _parseLt(self,c);
-            break;
-        case '>':
-            return _parseGt(self,c);
-            break;
-        case '%':
-            return _parseComment(self,c);
-            break;
-        case ' ':
-        case '\r':
-        case '\n':
-        case '\t':
-            return _parseSpace(self);
-        break;
-        case '0':
-        case '1':
-        case '2':
-        case '3':
-        case '4':
-        case '5':
-        case '6':
-        case '7':
-        case '8':
-        case '9':
-        case '-':
-        case '.':
-            return _parseNumber(self,c);
-        break;
-        default:
-            if (c<=127) {
-                return _parseOperator(self,c);
-            } else {
-                return _parseError(c);
-            }
-    }
-}
-
-static int scanner_scan(lua_State * L)
-{
-    Token *token;
-    scannerdata *self;
-    if (lua_gettop(L) != 3) {
-        return 0;
-    }
-    luaL_checktype(L, 2, LUA_TTABLE);
-    luaL_checktype(L, 3, LUA_TTABLE);
-    self = scanner_push(L);
-    memset(self,0,sizeof(scannerdata));
-    self->_operandstack = (Token **)priv_xmalloc (MAXOPERANDS * sizeof (Token));
-    memset (self->_operandstack,0,(MAXOPERANDS * sizeof (Token)));
-    // 4 = self
-    if (lua_type(L,1)== LUA_TTABLE) {
-        udstruct *uin;
-        void *ud;
-        int i = 1;
-        while (1) {
-            lua_rawgeti(L,1,i);
-            if (lua_type(L,-1)== LUA_TUSERDATA) {
-                ud = luaL_checkudata(L, -1, PDFE_METATABLE_STREAM);
-                if (ud != NULL) {
-                    uin = (udstruct *) ud;
-                    ObjectList *rover = self->_streams;
-                    ObjectList *item = (ObjectList *)priv_xmalloc (sizeof(ObjectList));
-                    item->stream = ((ppstream *) uin->d);
-                    item->next = NULL;
-                    if (!rover) {
-                        rover = item;
-                        self->_streams = rover;
-                    } else {
-                        while (rover->next)
-                            rover = rover->next;
-                        rover->next = item;
-                    }
-                }
-            } else {
-                ObjectList *rover = self->_streams;
-                self->_stream = rover->stream;
-                self->_streams = rover->next;
-                free(rover);
-                lua_pop(L,1);
-                break;
-            }
-            lua_pop(L,1);
-            i++;
-        }
-    } else {
-        udstruct *uin;
-        void *ud;
-        luaL_checktype(L, 1, LUA_TUSERDATA);
-        ud = luaL_checkudata(L, 1, PDFE_METATABLE_STREAM);
-        if (ud != NULL) {
-            uin = (udstruct *) ud;
-            self->_stream = ((ppstream *) uin->d);
-        } else {
-            ud = luaL_checkudata(L, 1, PDFE_METATABLE_ARRAY);
-            if (ud != NULL) {
-                uin = (udstruct *) ud;
-                pparray * array = (pparray *) uin->d;
-                int count = array->size;
-                int i;
-                for (i=0;i<count;i++) {
-                    ppobj *obj = pparray_at(array,i);
-                    if (obj->type == PPSTREAM) {
-                        ObjectList *rover = self->_streams;
-                        ObjectList *item = (ObjectList *)priv_xmalloc (sizeof(ObjectList));
-                        item->stream = obj->stream;
-                        item->next = NULL;
-                        if (!rover) {
-                            rover = item;
-                            self->_streams = rover;
-                        } else {
-                            while (rover->next)
-                            rover = rover->next;
-                            rover->next = item;
-                        }
-                    }
-                }
-                ObjectList *rover = self->_streams;
-                self->_stream = rover->stream;
-                self->_streams = rover->next;
-            }
-        }
-    }
-    streamReset(self);
-    token = _parseToken(self,streamGetChar(self));
-    while (token) {
-        if (token->type == pdf_operator) {
-            lua_pushstring(L, token->string);
-            free_token(token);
-            lua_rawget(L,2); // operator table
-            if (lua_isfunction(L,-1)) {
-                lua_pushvalue(L,4);
-                lua_pushvalue(L,3);
-                (void)lua_call(L,2,0);
-            } else {
-                lua_pop(L,1); // nil
-            }
-            clear_operand_stack(self,0);
-        } else {
-            push_operand(self, token);
-        }
-        if (!self->_stream) {
-            break;
-        }
-        token = _parseToken(self,streamGetChar(self));
-    }
-    /* wrap up */
-    if (self->_stream) {
-        streamClose(self);
-    }
-    clear_operand_stack(self,0);
-    free(self->_operandstack);
-    return 0;
-}
-
-static int scanner_done(lua_State * L)
-{
-    int c;
-    scannerdata *self = scanner_check(L,1);
-    while ((c=streamGetChar(self))>=0) ;
-    return 0;
-}
-
-// here are the stack popping functions, and their helpers
-
-static void operandstack_backup (scannerdata *self) {
-    int i = self->_nextoperand-1;
-    int balance = 0;
-    int backupstart = 0;
-    int backupstop = self->_operandstack[i]->type;
-    if (backupstop == pdf_stopdict) {
-        backupstart = pdf_startdict;
-    } else if (backupstop == pdf_stoparray) {
-        backupstart = pdf_startarray;
-    } else {
-        return;
-    }
-    for (;i>=0;i--) {
-        if (self->_operandstack[i]->type == backupstop) {
-            balance++;
-        } else if (self->_operandstack[i]->type == backupstart) {
-            balance--;
-        }
-        if (balance==0) {
-            break;
-        }
-    }
-    self->_nextoperand = i+1;
-}
-
-static void push_array (lua_State *L, scannerdata *self)
-{
-    int balance = 1; // nesting tracking
-    int index = 1;   // lua array index
-    Token *token =  self->_operandstack[self->_nextoperand++];
-    lua_newtable(L);
-    while (token) {
-        if (token->type == pdf_stoparray)
-            balance --;
-        if (token->type == pdf_startarray)
-            balance ++;
-        if (!balance) {
-            break;
-        } else {
-            push_token(L,self);
-            lua_rawseti(L,-2, index++);
-        }
-        token =  self->_operandstack[self->_nextoperand++];
-    }
-}
-
-
-static void push_dict (lua_State *L, scannerdata *self)
-{
-    int balance = 1; // nesting tracking
-    int needskey = 1; // toggle between lua value and lua key
-    Token *token =  self->_operandstack[self->_nextoperand++];
-    lua_newtable(L);
-    while (token) {
-        if (token->type == pdf_stopdict)
-            balance --;
-        if (token->type == pdf_startdict)
-            balance ++;
-        if (!balance) {
-            break;
-        } else if (needskey) {
-            lua_pushlstring(L, token->string, token->value);
-            needskey = 0;
-        } else {
-            push_token(L,self);
-            needskey = 1;
-            lua_rawset(L,-3);
-        }
-        token =  self->_operandstack[self->_nextoperand++];
-    }
-}
-
-const char *typenames[pdf_stopdict+1] = {
-    "unknown", "integer", "real", "boolean", "name", "operator",
-    "string", "array", "array", "dict", "dict"
-};
-
-static void push_token (lua_State *L, scannerdata *self)
-{
-    Token *token =  self->_operandstack[self->_nextoperand-1];
-    lua_createtable(L,2,0);
-    lua_pushstring (L, typenames[token->type]);
-    lua_rawseti(L,-2,1);
-    if (token->type == pdf_string || token->type == pdf_name) {
-        lua_pushlstring(L, token->string, token->value);
-    } else if (token->type == pdf_real || token->type == pdf_integer) {
-        lua_pushnumber(L, token->value); /* integer or float */
-    } else if (token->type == pdf_boolean) {
-        lua_pushboolean(L, (int)token->value);
-    } else if (token->type == pdf_startarray) {
-        push_array(L, self);
-    } else if (token->type == pdf_startdict) {
-        push_dict(L, self);
-    } else {
-        lua_pushnil(L);
-    }
-    lua_rawseti(L,-2, 2);
-}
-
-static int scanner_popsingular (lua_State * L, int token_type) {
-    int clear = 0; // how much of the operand stack needs deleting
-    scannerdata *self = scanner_check(L,1);
-    if (self->_nextoperand==0) {
-        return 0;
-    }
-    clear = self->_nextoperand-1;
-    Token *token = self->_operandstack[self->_nextoperand-1];
-    if (token ==NULL || (token->type != token_type )) {
-        return 0;
-    }
-    // the simple cases can be written out directly, but dicts and
-    // arrays are better done via the recursive function
-    if (token_type == pdf_stoparray || token_type == pdf_stopdict) {
-        operandstack_backup(self);
-        clear = self->_nextoperand-1;
-        push_token(L, self);
-        lua_rawgeti(L,-1,2);
-    } else if (token_type == pdf_real || token_type == pdf_integer) {
-        lua_pushnumber(L, token->value); /* integer or float */
-    } else if (token_type == pdf_boolean) {
-        lua_pushboolean(L,(int)token->value);
-    } else if (token_type == pdf_name || token_type == pdf_string) {
-        lua_pushlstring(L, token->string, token->value);
-    } else {
-        return 0;
-    }
-    clear_operand_stack(self,clear);
-    return 1;
-}
-
-static int scanner_popanything (lua_State * L) {
-    int clear = 0; // how much of the operand stack needs deleting
-    scannerdata *self = scanner_check(L,1);
-    if (self->_nextoperand==0) {
-        return 0;
-    }
-    clear = self->_nextoperand-1;
-    Token *token = self->_operandstack[self->_nextoperand-1];
-    if (token ==NULL) {
-        return 0;
-    }
-    int token_type = token->type;
-    // the simple cases can be written out directly, but dicts and
-    // arrays are better done via the recursive function
-    if (token_type == pdf_stoparray || token_type == pdf_stopdict) {
-        operandstack_backup(self);
-        clear = self->_nextoperand-1;
-        push_token(L, self);
-    } else {
-        push_token(L, self);
-    }
-    clear_operand_stack(self,clear);
-    return 1;
-}
-
-
-static int scanner_popnumber(lua_State * L)
-{
-    if(scanner_popsingular(L,pdf_real))
-        return 1;
-    if (scanner_popsingular(L,pdf_integer))
-        return 1;
-    lua_pushnil(L);
-  return 1;
-}
-
-static int scanner_popboolean(lua_State * L)
-{
-    if(scanner_popsingular(L,pdf_boolean))
-        return 1;
-    lua_pushnil(L);
-    return 1;
-}
-
-static int scanner_popstring(lua_State * L)
-{
-    if (scanner_popsingular(L,pdf_string))
-        return 1;
-    lua_pushnil(L);
-    return 1;
-}
-
-static int scanner_popname(lua_State * L)
-{
-    if (scanner_popsingular(L,pdf_name))
-        return 1;
-    lua_pushnil(L);
-    return 1;
-}
-
-static int scanner_poparray(lua_State * L)
-{
-    if (scanner_popsingular(L,pdf_stoparray))
-        return 1;
-    lua_pushnil(L);
-    return 1;
-}
-
-static int scanner_popdictionary(lua_State * L)
-{
-    if (scanner_popsingular(L,pdf_stopdict))
-        return 1;
-    lua_pushnil(L);
-    return 1;
-}
-
-static int scanner_popany(lua_State * L)
-{
-    if (scanner_popanything(L))
-        return 1;
-    lua_pushnil(L);
-    return 1;
-}
-
-static const luaL_Reg scannerlib_meta[] = {
-    {0, 0}
-};
-
-static const struct luaL_Reg scannerlib_m[] = {
-    {"done",        scanner_done},
-    {"popNumber",   scanner_popnumber},
-    {"popName",     scanner_popname},
-    {"popString",   scanner_popstring},
-    {"popArray",    scanner_poparray},
-    {"popDict",     scanner_popdictionary},
-    {"popBool",     scanner_popboolean},
-    {"pop",         scanner_popany},
-    {NULL, NULL}    /* sentinel */
-};
-
-static const luaL_Reg scannerlib[] = {
-    {"scan", scanner_scan},
-    {NULL, NULL}
-};
-
-LUALIB_API int luaopen_pdfscanner(lua_State * L)
-{
-    luaL_newmetatable(L, SCANNER);
-    luaL_openlib(L, 0, scannerlib_meta, 0);
-    lua_pushvalue(L, -1);
-    lua_setfield(L, -2, "__index");
-    luaL_openlib(L, NULL, scannerlib_m, 0);
-    luaL_openlib(L, "pdfscanner", scannerlib, 0);
-    return 1;
-}
-

Modified: trunk/Build/source/texk/web2c/luatexdir/lua/luatex-api.h
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/lua/luatex-api.h	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/lua/luatex-api.h	2019-11-08 09:36:30 UTC (rev 52692)
@@ -50,9 +50,9 @@
 #  include "lualib.h"
 #ifdef LuajitTeX
 #  include "luajit.h"
-#endif 
+#endif
 
-/* Names */ 
+/* Names */
 #ifndef LUATEX_HARFBUZZ_ENABLED
 #ifdef LuajitTeX
 #  define MyName "LuajitTeX"
@@ -1139,6 +1139,7 @@
 make_lua_key(string);\
 make_lua_key(style);\
 make_lua_key(sub);\
+make_lua_key(subfont);\
 make_lua_key(sub_box);\
 make_lua_key(sub_mark);\
 make_lua_key(sub_mlist);\
@@ -1832,6 +1833,7 @@
 init_lua_key(string);\
 init_lua_key(style);\
 init_lua_key(sub);\
+init_lua_key(subfont);\
 init_lua_key(sub_box);\
 init_lua_key(sub_mark);\
 init_lua_key(sub_mlist);\
@@ -2600,6 +2602,7 @@
 use_lua_key(string);
 use_lua_key(style);
 use_lua_key(sub);
+use_lua_key(subfont);
 use_lua_key(sub_box);
 use_lua_key(sub_mark);
 use_lua_key(sub_mlist);

Modified: trunk/Build/source/texk/web2c/luatexdir/luapplib/ppconf.h
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/luapplib/ppconf.h	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/luapplib/ppconf.h	2019-11-08 09:36:30 UTC (rev 52692)
@@ -9,6 +9,9 @@
 #  define ARM_COMPLIANT 0
 #endif
 
+/* we impose this */
+/*#define ARM_COMPLIANT 1*/
+
 /*
 Aux flags:
   PPDLL -- indicates a part of a shared library

Deleted: trunk/Build/source/texk/web2c/luatexdir/luapplib/ppload.c.orig
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/luapplib/ppload.c.orig	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/luapplib/ppload.c.orig	2019-11-08 09:36:30 UTC (rev 52692)
@@ -1,2555 +0,0 @@
-
-#include <utilbasexx.h>
-
-#include "pplib.h"
-
-const char * ppobj_kind[] = { "none", "null", "bool", "integer", "number", "name", "string", "array", "dict", "stream", "ref" };
-
-#define ignored_char(c) (c == 0x20 || c == 0x0A || c == 0x0D || c == 0x09 || c == 0x00)
-#define newline_char(c) (c == 0x0A || c == 0x0D)
-#define IGNORED_CHAR_CASE 0x20: case 0x0A: case 0x0D: case 0x09: case 0x00
-#define NEWLINE_CHAR_CASE 0x0A: case 0x0D
-#define DIGIT_CHAR_CASE '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9'
-#define OCTAL_CHAR_CASE '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7'
-
-#define MAX_INT_DIGITS 32
-
-#define PP_LENGTH_UNKNOWN ((size_t)-1)
-
-static const char * ppref_str (ppuint refnumber, ppuint refversion)
-{
-	static char buffer[MAX_INT_DIGITS + 1 + MAX_INT_DIGITS + 1 + 1 + 1];
-#if defined(MSVC64)|| defined(MINGW64)
-	sprintf(buffer, PPUINTF " " PPUINTF " R", refnumber, refversion);
-#else
-	sprintf(buffer, PPUINTF " " PPUINTF " R", (unsigned long)(refnumber), (unsigned long)(refversion));
-#endif
-	return buffer;
-}
-
-/* name */
-
-// pdf name delimiters: 0..32, ()<>[]{}/%
-// # treated specially
-// .+- are valid part of name; keep in mind names such as -| | |- .notdef ABCDEF+Font etc.
-const char ppname_byte_lookup[] = {
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 1, 1, '#', 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
-};
-
-#define PPNAME_INIT (8+1)
-
-#define ppname_flush(O, ghost, siz, flgs) \
-  (iof_put(O, '\0'), \
-   ghost = (_ppname *)ppheap_flush(O, &siz), \
-   ghost->flags = flgs, \
-   ghost->size = siz - sizeof(_ppname) - 1, \
-  (ppname)(ghost + 1))
-
-#define ppname_flush_with_ego(O, ghost, siz, flgs) \
-  (iof_put(O, '\0'), \
-   iof_ensure(O, sizeof(ppname *)), \
-   O->pos += sizeof(ppname *), \
-   ghost = (_ppname *)ppheap_flush(O, &siz), \
-   ghost->flags = flgs, \
-   ghost->size = siz - sizeof(_ppname) - 1 - sizeof(ppname *), \
-  (ppname)(ghost + 1))
-
-#define ppname_set_alter_ego(name, ghost, ego) (*((ppname *)(name + (ghost)->size + 1)) = ego)
-#define ppname_get_alter_ego(name) (*((ppname *)(name + ppname_size(name) + 1)))
-
-static ppname ppscan_name (iof *I, ppheap **pheap)
-{
-  int c, decode;
-  iof *O;
-  _ppname *ghost1, *ghost2;
-  ppname encoded, decoded;
-  size_t size;
-  const char *p, *e;
-  uint8_t v1, v2;
-  O = ppheap_buffer(pheap, sizeof(_ppname), PPNAME_INIT);
-  for (decode = 0, c = iof_char(I); c >= 0 && ppname_byte_lookup[c]; c = iof_next(I))
-  {
-    if (c == '#')
-      decode = 1;
-    iof_put(O, c);
-  }
-  if (!decode)
-    return ppname_flush(O, ghost1, size, 0);
-  encoded = ppname_flush_with_ego(O, ghost1, size, 0|PPNAME_ENCODED);
-  O = ppheap_buffer(pheap, sizeof(_ppname), PPNAME_INIT);
-  for (p = encoded, e = encoded + ghost1->size; p < e; ++p)
-  {
-    if (*p == '#' && p + 2 < e ){
-      v1 = base16_value(p[1]);
-      v2 = base16_value(p[2]);
-      iof_put(O, ((v1<<4)+v2));
-    }else
-      iof_put(O, *p);
-  }
-  decoded = ppname_flush_with_ego(O, ghost2, size, 0|PPNAME_DECODED);
-  ppname_set_alter_ego(encoded, ghost1, decoded);
-  ppname_set_alter_ego(decoded, ghost2, encoded);
-  return encoded;
-}
-
-static ppname ppscan_exec (iof *I, ppheap **pheap, int first)
-{
-  int c, decode;
-  iof *O;
-  _ppname *ghost1, *ghost2;
-  ppname encoded, decoded;
-  size_t size;
-  const char *p, *e;
-  uint8_t v1, v2;
-
-  O = ppheap_buffer(pheap, sizeof(_ppname), PPNAME_INIT);
-  iof_put(O, first);
-  for (decode = 0, c = iof_char(I); c >= 0 && ppname_byte_lookup[c]; c = iof_next(I))
-  {
-    if (c == '#')
-      decode = 1;
-    iof_put(O, c);
-  }
-  if (!decode)
-    return ppname_flush(O, ghost1, size, PPNAME_EXEC);
-  encoded = ppname_flush_with_ego(O, ghost1, size, PPNAME_EXEC|PPNAME_ENCODED);
-  O = ppheap_buffer(pheap, sizeof(_ppname), PPNAME_INIT);
-  for (p = encoded, e = encoded + ghost1->size; p < e; ++p)
-  {
-    if (*p == '#' && p + 2 < e ){
-      v1 = base16_value(p[1]);
-      v2 = base16_value(p[2]);
-      iof_put(O, ((v1<<4)+v2));
-    }else
-      iof_put(O, *p);
-  }
-  decoded = ppname_flush_with_ego(O, ghost2, size, PPNAME_EXEC|PPNAME_DECODED);
-  ppname_set_alter_ego(encoded, ghost1, decoded);
-  ppname_set_alter_ego(decoded, ghost2, encoded);
-  return encoded;
-}
-
-static ppname ppexec_internal (const void *data, size_t size, ppheap **pheap)
-{ // used only for artificial 'EI' operator name
-  iof *O;
-  _ppname *ghost1;
-
-  O = ppheap_buffer(pheap, sizeof(_ppname), size);
-  iof_write(O, data, size);
-  return ppname_flush(O, ghost1, size, PPNAME_EXEC);
-}
-
-ppname ppname_decoded (ppname name)
-{
-  const _ppname *ghost;
-  ghost = _ppname_ghost(name);
-  return (ghost->flags & PPNAME_ENCODED) ? ppname_get_alter_ego(name) : name;
-}
-
-ppname ppname_encoded (ppname name)
-{
-  const _ppname *ghost;
-  ghost = _ppname_ghost(name);
-  return (ghost->flags & PPNAME_DECODED) ? ppname_get_alter_ego(name) : name;
-}
-
-/* string */
-
-#define PPSTRING_INIT (16+1)
-
-#define ppstring_flush(O, ghost, siz, flgs) \
-  (iof_put(O, '\0'), \
-   ghost = (_ppstring *)ppheap_flush(O, &siz), \
-   ghost->flags = flgs, \
-   ghost->size = siz - sizeof(_ppstring) - 1, \
-  (ppstring)(ghost + 1))
-
-#define ppstring_flush_with_ego(O, ghost, siz, flgs) \
-  (iof_put(O, '\0'), \
-   iof_ensure(O, sizeof(ppstring *)), \
-   O->pos += sizeof(ppstring *), \
-   ghost = (_ppstring *)ppheap_flush(O, &siz), \
-   ghost->flags = flgs, \
-   ghost->size = siz - sizeof(_ppstring) - 1 - sizeof(ppstring *), \
-  (ppstring)(ghost + 1))
-
-#define ppstring_utf16be_bom(decoded) (decoded[0] == ((char)0xFE) && decoded[1] == ((char)0xFF) )
-#define ppstring_utf16le_bom(decoded) (decoded[0] == ((char)0xFF) && decoded[1] == ((char)0xFE))
-
-#define ppstring_check_bom(decoded, ghost) ((void)\
-  (ghost->size >= 2 ? (ppstring_utf16be_bom(decoded) ? (ghost->flags |= PPSTRING_UTF16BE) : \
-                      (ppstring_utf16le_bom(decoded) ? (ghost->flags |= PPSTRING_UTF16LE) : 0)) : 0))
-
-#define ppstring_check_bom2(decoded, ghost1, ghost2) ((void)\
-  (ghost2->size >= 2 ? (ppstring_utf16be_bom(decoded) ? ((ghost1->flags |= PPSTRING_UTF16BE), (ghost2->flags |= PPSTRING_UTF16BE)) : \
-                       (ppstring_utf16le_bom(decoded) ? ((ghost1->flags |= PPSTRING_UTF16LE), (ghost2->flags |= PPSTRING_UTF16LE)) : 0)) : 0))
-
-#define ppstring_set_alter_ego(string, ghost, ego) (*((ppstring *)(string + (ghost)->size + 1)) = ego)
-#define ppstring_get_alter_ego(string) (*((ppstring *)(string + ppstring_size(string) + 1)))
-
-static ppstring ppscan_string (iof *I, ppheap **pheap)
-{
-  int c, decode, balance;
-  iof *O;
-  _ppstring *ghost1, *ghost2;
-  uint8_t *p, *e;
-  ppstring encoded, decoded;
-  size_t size;
-  O = ppheap_buffer(pheap, sizeof(_ppstring), PPSTRING_INIT);
-  for (decode = 0, balance = 0, c = iof_char(I); c >= 0; )
-  {
-    switch (c)
-    {
-      case '\\':
-        decode = 1; // unescaping later
-        iof_put(O, '\\');
-        if ((c = iof_next(I)) >= 0)
-        {
-          iof_put(O, c);
-          c = iof_next(I);
-        }
-        break;
-      case '(': // may be unescaped if balanced
-        ++balance;
-        iof_put(O, '(');
-        c = iof_next(I);
-        break;
-      case ')':
-        if (balance == 0)
-        {
-          c = IOFEOF;
-          ++I->pos;
-          break;
-        }
-        --balance;
-        iof_put(O, ')');
-        c = iof_next(I);
-        break;
-      default:
-        iof_put(O, c);
-        c = iof_next(I);
-    }
-  }
-  if (!decode)
-  {
-    encoded = ppstring_flush(O, ghost1, size, 0);
-    ppstring_check_bom(encoded, ghost1); // any bytes can be there
-    return encoded;
-  }
-  encoded = ppstring_flush_with_ego(O, ghost1, size, 0|PPSTRING_ENCODED);
-  O = ppheap_buffer(pheap, sizeof(_ppstring), PPSTRING_INIT);
-  for (p = (uint8_t *)encoded, e = (uint8_t *)encoded + ghost1->size; p < e; ++p)
-  {
-    if (*p == '\\')
-    {
-      if (++p >= e)
-        break;
-      switch (*p)
-      {
-        case OCTAL_CHAR_CASE:
-          c = *p - '0';
-          if (++p < e && *p >= '0' && *p <= '7')
-          {
-            c = (c << 3) + *p - '0';
-            if (++p < e && *p >= '0' && *p <= '7')
-              c = (c << 3) + *p - '0';
-          }
-          iof_put(O, c);
-          break;
-        case 'n':
-          iof_put(O, '\n');
-          break;
-        case 'r':
-          iof_put(O, '\r');
-          break;
-        case 't':
-          iof_put(O, '\t');
-          break;
-        case 'b':
-          iof_put(O, '\b');
-          break;
-        case 'f':
-          iof_put(O, '\f');
-          break;
-        case NEWLINE_CHAR_CASE: // not a part of the string, ignore (pdf spec page 55)
-          break;
-        case '(': case ')': case '\\':
-        default: // for enything else backslash is ignored (pdf spec page 54)
-          iof_put(O, *p);
-          break;
-      }
-    }
-    else
-      iof_put(O, *p);
-  }
-  decoded = ppstring_flush_with_ego(O, ghost2, size, 0|PPSTRING_DECODED);
-  ppstring_check_bom2(decoded, ghost1, ghost2);
-  ppstring_set_alter_ego(encoded, ghost1, decoded);
-  ppstring_set_alter_ego(decoded, ghost2, encoded);
-  return encoded;
-}
-
-/* Hex string may contain white characters. If odd number of digits, the last assumed to be '0' */
-
-static ppstring ppscan_base16 (iof *I, ppheap **pheap)
-{
-  int c, v1, v2;
-  iof *O;
-  _ppstring *ghost1, *ghost2;
-  size_t size;
-  ppstring encoded, decoded;
-  uint8_t *p, *e;
-
-  O = ppheap_buffer(pheap, sizeof(_ppstring), PPSTRING_INIT);
-  for (c = iof_char(I); c >= 0 && (base16_digit(c) || ignored_char(c)); c = iof_next(I))
-    iof_put(O, c);
-  if (c == '>')
-    ++I->pos;
-  encoded = ppstring_flush_with_ego(O, ghost1, size, PPSTRING_BASE16|PPSTRING_ENCODED);
-  O = ppheap_buffer(pheap, sizeof(_ppstring), (ghost1->size >> 1) + 1);
-  for (p = (uint8_t *)encoded, e = (uint8_t *)encoded + ghost1->size; p < e; ++p)
-  {
-    if ((v1 = base16_value(*p)) < 0) // ignored
-      continue;
-    for (v2 = 0, ++p; p < e && (v2 = base16_value(*p)) < 0; ++p);
-    iof_put(O, (v1<<4)|v2);
-  }
-  decoded = ppstring_flush_with_ego(O, ghost2, size, 0|PPSTRING_DECODED);
-  ppstring_check_bom2(decoded, ghost1, ghost2);
-  ppstring_set_alter_ego(encoded, ghost1, decoded);
-  ppstring_set_alter_ego(decoded, ghost2, encoded);
-  return encoded;
-}
-
-/* internal use only; binary string */
-
-static ppstring ppstring_buffer (iof *O, ppheap **pheap)
-{
-   _ppstring *ghost1, *ghost2;
-   ppstring encoded, decoded;
-   uint8_t *p, *e;
-   size_t size;
-
-   decoded = ppstring_flush_with_ego(O, ghost2, size, 0|PPSTRING_DECODED);
-   O = ppheap_buffer(pheap, sizeof(_ppstring), (ghost2->size << 1) + 1);
-   for (p = (uint8_t *)decoded, e = (uint8_t *)decoded + ghost2->size; p < e; ++p)
-     iof_set2(O, base16_uc_alphabet[(*p)>>4], base16_uc_alphabet[(*p)&15]);
-   encoded = ppstring_flush_with_ego(O, ghost1, size, PPSTRING_BASE16|PPSTRING_ENCODED);
-   ppstring_set_alter_ego(encoded, ghost1, decoded);
-   ppstring_set_alter_ego(decoded, ghost2, encoded);
-   return encoded;
-}
-
-ppstring ppstring_internal (const void *data, size_t size, ppheap **pheap)
-{ // so far used only for crypt key
-  iof *O;
-
-  O = ppheap_buffer(pheap, sizeof(_ppstring), size);
-  iof_write(O, data, size);
-  return ppstring_buffer(O, pheap);
-}
-
-/* PDF spec says nothing about base85 strings, but streams might be (afair no leading '<~' but present trailing '~>') */
-
-static ppstring ppscan_base85 (iof *I, ppheap **pheap)
-{ // bawse85 alphabet is 33.117, adobe also hires 'z' and 'y' for compression
-  int c;
-  iof *O, B;
-  _ppstring *ghost1, *ghost2;
-  size_t size;
-  ppstring encoded, decoded;
-  O = ppheap_buffer(pheap, sizeof(_ppstring), PPSTRING_INIT);
-  for (c = iof_char(I); (c >= '!' && c <= 'u') || c == 'z' || c == 'y'; c = iof_next(I))
-    iof_put(O, c);
-  if (c == '~')
-    if ((c = iof_next(I)) == '>')
-      ++I->pos;
-  encoded = ppstring_flush_with_ego(O, ghost1, size, PPSTRING_BASE85|PPSTRING_ENCODED);
-  iof_string_reader(&B, encoded, ghost1->size);
-  O = ppheap_buffer(pheap, sizeof(_ppstring), (ghost1->size * 5 / 4) + 1);
-  base85_decode(&B, O);
-  decoded = ppstring_flush_with_ego(O, ghost2, size, 0|PPSTRING_DECODED);
-  ppstring_check_bom2(decoded, ghost1, ghost2);
-  ppstring_set_alter_ego(encoded, ghost1, decoded);
-  ppstring_set_alter_ego(decoded, ghost2, encoded);
-  return encoded;
-}
-
-/*
-Encrypted strings. In case of encrypted strings, we first need to decode the string (saving original form hardly makes sense),
-then decrypt the string, and encode it again.
-*/
-
-const char ppstring_byte_escape[] = { /* -1 escaped with octal, >0 escaped with \\, 0 left intact*/
- -1,-1,-1,-1,-1,-1,-1,-1,'b','t','n',-1,'f','r',-1,-1,
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-  0, 0, 0, 0, 0, 0, 0, 0,'(',')', 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-};
-
-
-static ppstring ppscan_crypt_string (iof *I, ppcrypt *crypt, ppheap **pheap)
-{
-  int c, b, balance, encode;
-  iof *O;
-  _ppstring *ghost1, *ghost2;
-  ppstring encoded, decoded;
-  uint8_t *p, *e;
-  size_t size;
-
-  O = ppheap_buffer(pheap, sizeof(_ppstring), PPSTRING_INIT);
-  for (balance = 0, encode = 0, c = iof_char(I); c >= 0; )
-  {
-    switch (c)
-    {
-      case '\\':
-        if ((c = iof_next(I)) < 0)
-          break;
-        encode = 1;
-        switch (c)
-        {
-          case OCTAL_CHAR_CASE:
-            b = c - '0';
-            if ((c = iof_next(I)) >= 0 && c >= '0' && c <= '7')
-            {
-              b = (b << 3) + c - '0';
-              if ((c = iof_next(I)) >= 0 && c >= '0' && c <= '7')
-              {
-                b = (b << 3) + c - '0';
-                c = iof_next(I);
-              }
-            }
-            iof_put(O, b);
-            // c is set to the next char
-            break;
-          case 'n':
-            iof_put(O, '\n');
-            c = iof_next(I);
-            break;
-          case 'r':
-            iof_put(O, '\r');
-            c = iof_next(I);
-            break;
-          case 't':
-            iof_put(O, '\t');
-            c = iof_next(I);
-            break;
-          case 'b':
-            iof_put(O, '\b');
-            c = iof_next(I);
-            break;
-          case 'f':
-            iof_put(O, '\f');
-            c = iof_next(I);
-            break;
-          case NEWLINE_CHAR_CASE: // not a part of the string, ignore (pdf spec page 55)
-            c = iof_next(I);
-            break;
-          case '(': case ')': case '\\':
-          default: // for enything else backslash is ignored (pdf spec page 54)
-            iof_put(O, c);
-            c = iof_next(I);
-            break;
-        }
-        break;
-      case '(':
-        ++balance;
-        encode = 1;
-        iof_put(O, '(');
-        c = iof_next(I);
-        break;
-      case ')':
-        if (balance == 0)
-        {
-          c = IOFEOF;
-          ++I->pos;
-        }
-        else
-        {
-          --balance;
-          //encode = 1;
-          iof_put(O, ')');
-          c = iof_next(I);
-        }
-        break;
-      default:
-        if (ppstring_byte_escape[c] != 0)
-          encode = 1;
-        iof_put(O, c);
-        c = iof_next(I);
-    }
-  }
-  /* decrypt the buffer in place, update size */
-  if (ppstring_decrypt(crypt, O->buf, iof_size(O), O->buf, &size))
-    O->pos = O->buf + size;
-  /* make encoded counterpart */
-  if (!encode)
-  {
-    decoded = ppstring_flush(O, ghost2, size, 0);
-    ppstring_check_bom(decoded, ghost2);
-    return decoded;
-  }
-  decoded = ppstring_flush_with_ego(O, ghost2, size, PPSTRING_DECODED);
-  O = ppheap_buffer(pheap, sizeof(_ppstring), ghost2->size);
-  for (p = (uint8_t *)decoded, e = (uint8_t *)decoded + ghost2->size; p < e; ++p)
-  {
-    switch ((b = ppstring_byte_escape[*p]))
-    {
-      case 0:
-        iof_put(O, *p);
-        break;
-      case -1:
-        iof_put4(O, '\\', (c >> 6) + '0', ((c >> 3) & 7) + '0', (c & 7) + '0');
-        break;
-      default:
-        iof_put2(O, '\\', b);
-        break;
-    }
-  }
-  encoded = ppstring_flush_with_ego(O, ghost1, size, PPSTRING_ENCODED);
-  ppstring_check_bom2(decoded, ghost1, ghost2);
-  ppstring_set_alter_ego(encoded, ghost1, decoded);
-  ppstring_set_alter_ego(decoded, ghost2, encoded);
-  return encoded;
-}
-
-
-static ppstring ppscan_crypt_base16 (iof *I, ppcrypt *crypt, ppheap **pheap)
-{
-  int c, v1, v2;
-  iof *O;
-  _ppstring *ghost1, *ghost2;
-  ppstring encoded, decoded;
-  uint8_t *p, *e;
-  size_t size;
-
-  O = ppheap_buffer(pheap, sizeof(_ppstring), PPSTRING_INIT);
-  // base16_decode(I, O); // no info about the last char..
-  for (c = iof_char(I); c != '>' && c >= 0; )
-  {
-    if ((v1 = base16_value(c)) < 0)
-    {
-      if (ignored_char(c))
-      {
-        c = iof_next(I);
-        continue;
-      }
-      break;
-    }
-    do {
-      c = iof_next(I);
-      if ((v2 = base16_value(c)) >= 0)
-      {
-        c = iof_next(I);
-        break;
-      }
-      if (!ignored_char(c)) // c == '>' || c < 0 or some crap
-      {
-        v2 = 0;
-        break;
-      }
-    } while (1);
-    iof_put(O, (v1 << 4)|v2);
-  }
-  if (c == '>')
-    ++I->pos;
-  /* decrypt the buffer in place, update size */
-  if (ppstring_decrypt(crypt, O->buf, iof_size(O), O->buf, &size))
-    O->pos = O->buf + size;
-  decoded = ppstring_flush_with_ego(O, ghost2, size, PPSTRING_DECODED);
-  /* recreate an encoded form */
-  O = ppheap_buffer(pheap, sizeof(_ppstring), (ghost2->size << 1) + 1);
-  for (p = (uint8_t *)decoded, e = (uint8_t *)decoded + ghost2->size; p < e; ++p)
-    iof_set2(O, base16_uc_alphabet[(*p)>>4], base16_uc_alphabet[(*p)&15]);
-  encoded = ppstring_flush_with_ego(O, ghost1, size, PPSTRING_BASE16|PPSTRING_ENCODED);
-  ppstring_check_bom2(decoded, ghost1, ghost2);
-  ppstring_set_alter_ego(encoded, ghost1, decoded);
-  ppstring_set_alter_ego(decoded, ghost2, encoded);
-  return encoded;
-}
-
-/* ppstring alter ego switcher */
-
-ppstring ppstring_decoded (ppstring string)
-{
-  const _ppstring *ghost;
-  ghost = _ppstring_ghost(string);
-  return (ghost->flags & PPSTRING_ENCODED) ? ppstring_get_alter_ego(string) : string;
-}
-
-ppstring ppstring_encoded (ppstring string)
-{
-  const _ppstring *ghost;
-  ghost = _ppstring_ghost(string);
-  return (ghost->flags & PPSTRING_DECODED) ? ppstring_get_alter_ego(string) : string;
-}
-
-/* scanner stack */
-
-#define PPSTACK_BUFFER 512
-
-static void ppstack_init (ppstack *stack, ppheap **pheap)
-{
-  stack->buf = stack->pos = (ppobj *)pp_malloc(PPSTACK_BUFFER * sizeof(ppobj));
-  stack->size = 0;
-  stack->space = PPSTACK_BUFFER;
-  stack->pheap = pheap;
-}
-
-#define ppstack_free_buffer(stack) (pp_free((stack)->buf))
-
-static void ppstack_resize (ppstack *stack)
-{
-  ppobj *newbuffer;
-  stack->space <<= 1;
-  newbuffer = (ppobj *)pp_malloc(stack->space * sizeof(ppobj));
-  memcpy(newbuffer, stack->buf, stack->size * sizeof(ppobj));
-  ppstack_free_buffer(stack);
-  stack->buf = newbuffer;
-  stack->pos = newbuffer + stack->size;
-}
-
-#define ppstack_push(stack) ((void)((stack)->size < (stack)->space || (ppstack_resize(stack), 0)), ++(stack)->size, (stack)->pos++)
-#define ppstack_pop(stack, n) ((stack)->size -= (n), (stack)->pos -= (n))
-#define ppstack_at(stack, i) ((stack)->buf + i)
-#define ppstack_clear(stack) ((stack)->pos = (stack)->buf, (stack)->size = 0)
-
-/* scanner commons */
-
-#define ppscan_uint(I, u) iof_get_uintlw(I, u)
-#define ppread_uint(s, u) string_to_uintlw((const char *)(s), u)
-
-static ppobj * ppscan_numobj (iof *I, ppobj *obj, int negative)
-{
-  ppint integer;
-  ppnum number;
-  int exponent;
-  int c;
-  c = iof_char(I);
-  iof_scan_integer(I, c, integer);
-  switch(c)
-  {
-    case '.':
-    {
-      number = (ppnum)integer;
-      c = iof_next(I);
-      iof_scan_fraction(I, c, number, exponent);
-      double_negative_exp10(number, exponent);
-      obj->type = PPNUM, obj->number = negative ? -number : number;
-      break;
-    }
-    default:
-      obj->type = PPINT, obj->integer = negative ? -integer : integer;
-      break;
-  }
-  return obj;
-}
-
-static ppobj * ppscan_numobj_frac (iof *I, ppobj *obj, int negative)
-{
-  ppnum number;
-  int c, exponent;
-
-  number = 0.0;
-  c = iof_next(I);
-  iof_scan_fraction(I, c, number, exponent);
-  double_negative_exp10(number, exponent);
-  obj->type = PPNUM, obj->number = negative ? -number : number;
-  return obj;
-}
-
-static int ppscan_find (iof *I)
-{ // skips whitechars and comments
-  int c;
-  for (c = iof_char(I); ; c = iof_next(I))
-  {
-    switch (c)
-    {
-      case IGNORED_CHAR_CASE:
-        break;
-      case '%': {
-        do {
-          if ((c = iof_next(I)) < 0)
-            return c;
-        } while (!newline_char(c));
-        break;
-      }
-      default:
-        return c;
-    }
-  }
-  return c; // never reached
-}
-
-static int ppscan_keyword (iof *I, const char *keyword, size_t size)
-{
-  size_t i;
-  int c;
-  if (iof_left(I) >= size)
-  {
-    if (memcmp(I->pos, keyword, size) != 0)
-      return 0;
-    I->pos += size;
-    return 1;
-  }
-  // sticky case, we can't go back
-  for (i = 0, c = iof_char(I); i < size; ++i, ++keyword, c = iof_next(I))
-    if (i != c)
-      return 0;
-  return 1;
-}
-
-#define ppscan_key(I, literal) ppscan_keyword(I, "" literal, sizeof(literal) - 1)
-
-/* objects parser */
-
-static ppref * ppref_unresolved (ppheap **pheap, ppuint refnumber, ppuint refversion)
-{
-  ppref *ref = (ppref *)ppheap_take(pheap, sizeof(ppref));
-  memset(ref, 0, sizeof(ppref));
-  ref->object.type = PPNONE;
-  ref->number = refnumber;
-  ref->version = refversion;
-  return ref;
-}
-
-#define PPMARK PPNONE
-
-static ppobj * ppscan_obj (iof *I, ppdoc *pdf, ppxref *xref)
-{
-  int c;
-  ppobj *obj;
-  size_t mark, size;
-  ppuint refnumber, refversion;
-  ppref *ref;
-  ppstack *stack;
-  ppcrypt *crypt;
-
-  stack = &pdf->stack;
-  c = iof_char(I);
-  switch (c)
-  {
-    case DIGIT_CHAR_CASE:
-      return ppscan_numobj(I, ppstack_push(stack), 0);
-    case '.':
-      return ppscan_numobj_frac(I, ppstack_push(stack), 0);
-    case '+':
-      ++I->pos;
-      return ppscan_numobj(I, ppstack_push(stack), 0);
-    case '-':
-      ++I->pos;
-      return ppscan_numobj(I, ppstack_push(stack), 1);
-    case '/':
-      ++I->pos;
-      obj = ppstack_push(stack);
-      obj->type = PPNAME;
-      obj->name = ppscan_name(I, &pdf->heap);
-      return obj;
-    case '(':
-      ++I->pos;
-      obj = ppstack_push(stack);
-      obj->type = PPSTRING;
-      if (ppcrypt_ref(pdf, crypt))
-        obj->string = ppscan_crypt_string(I, crypt, &pdf->heap);
-      else
-        obj->string = ppscan_string(I, &pdf->heap);
-      return obj;
-    case '[':
-      mark = stack->size;
-      obj = ppstack_push(stack);
-      obj->type = PPMARK; // ppscan_obj() checks types backward for 'R', so set the type immediatelly (reserved for PPARRAY)
-      obj->any = NULL;
-      ++I->pos;
-      for (c = ppscan_find(I); c != ']'; c = ppscan_find(I))
-      {
-        if (ppscan_obj(I, pdf, xref) == NULL)
-        { // callers assume that NULL returns means nothing pushed
-          size = stack->size - mark; // pop items AND the obj reserved for array
-          ppstack_pop(stack, size);
-          return NULL;
-        }
-      }
-      ++I->pos;
-      size = stack->size - mark - 1;
-      obj = ppstack_at(stack, mark); // stack might have been realocated
-      obj->type = PPARRAY;
-      obj->array = pparray_create(ppstack_at(stack, mark + 1), size, &pdf->heap);
-      ppstack_pop(stack, size); // pop array items, leave the array on top
-      return obj;
-    case '<':
-      if ((c = iof_next(I)) == '<')
-      {
-        mark = stack->size;
-        obj = ppstack_push(stack);
-        obj->type = PPMARK;
-        obj->any = NULL;
-        ++I->pos;
-        for (c = ppscan_find(I); c != '>'; c = ppscan_find(I))
-        {
-          if (ppscan_obj(I, pdf, xref) == NULL)
-          {
-            size = stack->size - mark;
-            ppstack_pop(stack, size);
-            return NULL;
-          }
-        }
-        if (iof_next(I) == '>')
-          ++I->pos;
-        size = stack->size - mark - 1;
-        obj = ppstack_at(stack, mark);
-        obj->type = PPDICT;
-        obj->dict = ppdict_create(ppstack_at(stack, mark + 1), size, &pdf->heap);
-        ppstack_pop(stack, size);
-        return obj;
-      }
-      obj = ppstack_push(stack);
-      obj->type = PPSTRING;
-      if (ppcrypt_ref(pdf, crypt))
-        obj->string = ppscan_crypt_base16(I, crypt, &pdf->heap);
-      else
-        obj->string = ppscan_base16(I, &pdf->heap);
-      return obj;
-    case 'R':
-      if (stack->size >= 2 && stack->pos[-1].type == PPINT && stack->pos[-2].type == PPINT)
-      {
-        ++I->pos;
-        obj = &stack->pos[-2];
-        refnumber = (ppuint)obj->integer;
-        ppstack_pop(stack, 1); // pop version number, retype obj to a reference
-        if (xref == NULL || (ref = ppxref_find(xref, refnumber)) == NULL)
-        { /* pdf spec page 64: unresolvable reference is not an error, should just be treated as a reference to null.
-             we also need this to read trailer, where refs can't be resolved yet */
-          refversion = (obj + 1)->integer;
-          //if (xref != NULL)
-          //  loggerf("unresolved reference %s", ppref_str(refnumber, refversion));
-          ref = ppref_unresolved(stack->pheap, refnumber, refversion);
-        }
-        obj->type = PPREF;
-        obj->ref = ref;
-        return obj;
-      }
-      break;
-    case 't':
-      if (iof_next(I) == 'r' && iof_next(I) == 'u' && iof_next(I) == 'e')
-      {
-        ++I->pos;
-        obj = ppstack_push(stack);
-        obj->type = PPBOOL;
-        obj->integer = 1;
-        return obj;
-      }
-      break;
-    case 'f':
-      if (iof_next(I) == 'a' && iof_next(I) == 'l' && iof_next(I) == 's' && iof_next(I) == 'e')
-      {
-        ++I->pos;
-        obj = ppstack_push(stack);
-        obj->type = PPBOOL;
-        obj->integer = 0;
-        return obj;
-      }
-      break;
-    case 'n':
-      if (iof_next(I) == 'u' && iof_next(I) == 'l' && iof_next(I) == 'l')
-      {
-        ++I->pos;
-        obj = ppstack_push(stack);
-        obj->type = PPNULL;
-        obj->any = NULL;
-        return obj;
-      }
-      break;
-  }
-  return NULL;
-}
-
-/*
-A variant for contents streams (aka postscript); wise of operators, blind to references.
-We are still PDF, so we don't care about postscript specific stuff such as radix numbers
-and scientific numbers notation. It takes ppstack * as context (no ppdoc *) to be able
-to run contents parser beyond the scope of ppdoc heap.
-*/
-
-static ppstring ppstring_inline (iof *I, ppdict *imagedict, ppheap **pheap);
-
-static ppobj * ppscan_psobj (iof *I, ppstack *stack)
-{
-  int c;
-  ppobj *obj, *op;
-  size_t size, mark;
-  ppname exec;
-
-  c = iof_char(I);
-  switch (c)
-  {
-    case DIGIT_CHAR_CASE:
-      return ppscan_numobj(I, ppstack_push(stack), 0);
-    case '.':
-      return ppscan_numobj_frac(I, ppstack_push(stack), 0);
-    case '+':
-      c = iof_next(I);
-      if (base10_digit(c)) // '+.abc' is probably an executable name, but we are not in postscript
-        return ppscan_numobj(I, ppstack_push(stack), 0);
-      else if (c == '.')
-        return ppscan_numobj_frac(I, ppstack_push(stack), 0);
-      obj = ppstack_push(stack);
-      obj->type = PPNAME;
-      obj->name = ppscan_exec(I, stack->pheap, '+');
-      return obj;
-    case '-':
-      c = iof_next(I);
-      if (base10_digit(c)) // ditto, we would handle type1 '-|' '|-' operators though
-        return ppscan_numobj(I, ppstack_push(stack), 1);
-      else if (c == '.')
-        return ppscan_numobj_frac(I, ppstack_push(stack), 1);
-      obj = ppstack_push(stack);
-      obj->type = PPNAME;
-      obj->name = ppscan_exec(I, stack->pheap, '-');
-      return obj;
-    case '/':
-      ++I->pos;
-      obj = ppstack_push(stack);
-      obj->type = PPNAME;
-      obj->name = ppscan_name(I, stack->pheap);
-      return obj;
-    case '(':
-      ++I->pos;
-      obj = ppstack_push(stack);
-      obj->type = PPSTRING;
-      obj->string = ppscan_string(I, stack->pheap);
-      return obj;
-    case '[':
-      mark = stack->size;
-      obj = ppstack_push(stack);
-      obj->type = PPMARK;
-      obj->any = NULL;
-      ++I->pos;
-      for (c = ppscan_find(I); c != ']'; c = ppscan_find(I))
-      {
-        if (ppscan_psobj(I, stack) == NULL)
-        {
-          size = stack->size - mark;
-          ppstack_pop(stack, size);
-          return NULL;
-        }
-      }
-      ++I->pos;
-      size = stack->size - mark - 1;
-      obj = ppstack_at(stack, mark);
-      obj->type = PPARRAY;
-      obj->array = pparray_create(ppstack_at(stack, mark + 1), size, stack->pheap);
-      ppstack_pop(stack, size);
-      return obj;
-    case '<':
-      if ((c = iof_next(I)) == '<')
-      {
-        mark = stack->size;
-        obj = ppstack_push(stack);
-        obj->type = PPMARK;
-        obj->any = NULL;
-        ++I->pos;
-        for (c = ppscan_find(I); c != '>'; c = ppscan_find(I))
-        {
-          if (ppscan_psobj(I, stack) == NULL)
-          {
-            size = stack->size - mark;
-            ppstack_pop(stack, size);
-            return NULL;
-          }
-        }
-        if (iof_next(I) == '>')
-          ++I->pos;
-        size = stack->size - mark - 1;
-        obj = ppstack_at(stack, mark);
-        obj->type = PPDICT;
-        obj->dict = ppdict_create(ppstack_at(stack, mark + 1), size, stack->pheap);
-        ppstack_pop(stack, size);
-        return obj;
-      }
-      obj = ppstack_push(stack);
-      obj->type = PPSTRING;
-      if (c == '~')
-        ++I->pos, obj->string = ppscan_base85(I, stack->pheap);
-      else
-        obj->string = ppscan_base16(I, stack->pheap);
-      return obj;
-    default:
-      if (c < 0 || !ppname_byte_lookup[c])
-        break; // forbid empty names; dead loop otherwise
-      ++I->pos;
-      /* true false null practically don't occur in streams so it makes sense to assume that we get an operator name here.
-         If it happen to be a keyword we could give back those several bytes to the heap but.. heap buffer is tricky enough. */
-      exec = ppscan_exec(I, stack->pheap, c);
-      obj = ppstack_push(stack);
-      switch (exec[0])
-      {
-        case 't':
-          if (exec[1] == 'r' && exec[2] == 'u' && exec[3] == 'e' && exec[4] == '\0')
-          {
-            obj->type = PPBOOL;
-            obj->integer = 1;
-            // todo: drop exec
-            return obj;
-          }
-          break;
-        case 'f':
-          if (exec[1] == 'a' && exec[2] == 'l' && exec[3] == 's' && exec[4] == 'e' && exec[5] == '\0')
-          {
-            obj->type = PPBOOL;
-            obj->integer = 0;
-            // todo: drop exec
-            return obj;
-          }
-          break;
-        case 'n':
-          if (exec[1] == 'u' && exec[2] == 'l' && exec[3] == 'l' && exec[4] == '\0')
-          {
-            obj->type = PPNULL;
-            obj->any = NULL;
-            // todo: drop exec
-            return obj;
-          }
-          break;
-        case 'B':
-           /*
-           Inline images break rules of operand/operator syntax, so 'BI/ID' operators need to be treated as special syntactic keywords.
-
-             BI <keyval pairs> ID<whitechar?><imagedata><whitechar?>EI
-
-           We treat the image as a single syntactic token; BI starts collecting a dict, ID is the beginning of the data. Effectively EI
-           operator obtains two operands - dict and string. It is ok to put three items onto the stack, callers dont't assume there is just one.
-           */
-          if (exec[1] == 'I' && exec[2] == '\0')
-          {
-            ppdict *imagedict;
-            /* key val pairs -> dict */
-            mark = stack->size - 1;
-            obj->type = PPMARK;
-            obj->any = NULL;
-            for (c = ppscan_find(I); ; c = ppscan_find(I))
-            {
-              if ((op = ppscan_psobj(I, stack)) == NULL)
-              {
-                size = stack->size - mark;
-                ppstack_pop(stack, size);
-                return NULL;
-              }
-              if (op->type == PPNAME && ppname_exec(op->name))
-              {
-                if (!ppname_is(op->name, "ID"))
-                { // weird
-                  size = stack->size - mark;
-                  ppstack_pop(stack, size);
-                  return NULL;
-                }
-                break;
-              }
-            }
-            size = stack->size - mark - 1;
-            obj = ppstack_at(stack, mark);
-            obj->type = PPDICT;
-            obj->dict = imagedict = ppdict_create(ppstack_at(stack, mark + 1), size, stack->pheap);
-            ppstack_pop(stack, size);
-            /* put image data string */
-            obj = ppstack_push(stack);
-            obj->type = PPSTRING;
-            obj->string = ppstring_inline(I, imagedict, stack->pheap);;
-            /* put EI operator name */
-            obj = ppstack_push(stack);
-            obj->type = PPNAME;
-            obj->name = ppexec_internal("EI", 2, stack->pheap);
-            return obj;
-          }
-      }
-      obj->type = PPNAME;
-      obj->name = exec;
-      return obj;
-  }
-  return NULL;
-}
-
-/*
-We try to get the exact inline image length from its dict params. If cannot predict the length, we have to scan the input until 'EI'.
-I've checked on may examples that it gives the same results but one can never be sure, as 'EI' might happen to be a part of the data.
-Stripping white char is also very heuristic; \0 is a white char in PDF and very likely to be a data byte.. weak method.
-*/
-
-static size_t inline_image_length (ppdict *dict)
-{
-  ppuint w, h, bpc, colors;
-  ppname cs;
-
-  if (ppdict_get_uint(dict, "W", &w) && ppdict_get_uint(dict, "H", &h) && ppdict_get_uint(dict, "BPC", &bpc) && (cs = ppdict_get_name(dict, "CS")) != NULL)
-  {
-    if (ppname_is(cs, "DeviceGray"))
-      colors = 1;
-    else if (ppname_is(cs, "DeviceRGB"))
-      colors = 3;
-    else if (ppname_is(cs, "DeviceCMYK"))
-      colors = 4;
-    else
-      return PP_LENGTH_UNKNOWN;
-    return (w * h * bpc * colors + 7) >> 3;
-  }
-  return PP_LENGTH_UNKNOWN;
-}
-
-static ppstring ppstring_inline (iof *I, ppdict *imagedict, ppheap **pheap)
-{
-  iof *O;
-  int c, d, e;
-  size_t length, leftin, leftout, bytes;
-
-  O = ppheap_buffer(pheap, sizeof(_ppstring), PPSTRING_INIT);
-  c = iof_char(I);
-  if (ignored_char(c))
-    c = iof_next(I);
-
-  length = inline_image_length(imagedict);
-  if (length != PP_LENGTH_UNKNOWN)
-  {
-    while (length > 0 && iof_readable(I) && iof_writable(O))
-    {
-      leftin = iof_left(I);
-      leftout = iof_left(O);
-      bytes = length;
-      if (bytes > leftin) bytes = leftin;
-      if (bytes > leftout) bytes = leftout;
-      memcpy(O->pos, I->pos, bytes);
-      I->pos += bytes;
-      O->pos += bytes;
-      length -= bytes;
-    }
-    // gobble EI
-    if (ppscan_find(I) == 'E')
-      if (iof_next(I) == 'I')
-        ++I->pos;
-  }
-  else
-  {
-    while (c >= 0)
-    {
-      if (c == 'E')
-      {
-        d = iof_next(I);
-        if (d == 'I')
-        {
-          e = iof_next(I);
-          if (!ppname_byte_lookup[e])
-          { /* strip one newline from the end and stop */
-            if (O->pos - 2 >= O->buf) // sanity
-            {
-              c = *(O->pos - 1);
-              if (ignored_char(c))
-              {
-                if (c == 0x0A && *(O->pos - 2) == 0x0D)
-                  O->pos -= 2;
-                else
-                  O->pos -= 1;
-              }
-            }
-            break;
-          }
-          iof_put2(O, c, d);
-          c = e;
-        }
-        else
-        {
-          iof_put(O, c);
-          c = d;
-        }
-      }
-      else
-      {
-        iof_put(O, c);
-        c = iof_next(I);
-      }
-    }
-  }
-  return ppstring_buffer(O, pheap);
-}
-
-/* input reader */
-
-/*
-PDF input is a pseudo file that either keeps FILE * or data. Reader iof * is a proxy to input
-that provides byte-by-byte interface. Our iof structure is capable to link iof_file *input,
-but t avoid redundant checks on IOF_DATA flag, here we link iof *I directly to FILE * or mem buffer.
-When reading from file we need an internal buffer, which should be kept rather small, as it is
-only used to parse xrefs and objects (no streams). We allocate the buffer from a private heap
-(not static) to avoid conflicts when processing >1 pdfs at once. Besides, the input buffer may be
-needed after loading the document, eg. to access references raw data.
-*/
-
-#define PPDOC_BUFFER 0xFFF // keep that small, it is only used to parse body objects
-
-static void ppdoc_reader_init (ppdoc *pdf, iof_file *input)
-{
-  iof *I;
-  pdf->input = *input;
-  input = &pdf->input;
-  input->refcount = 1;
-  I = &pdf->reader;
-  if (input->flags & IOF_DATA)
-  {
-    pdf->buffer = NULL;            // input iof_file is the buffer
-    iof_string_reader(I, NULL, 0); // gets IOF_DATA flag
-  }
-  else
-  {
-    pdf->buffer = (uint8_t *)ppheap_take(&pdf->heap, PPDOC_BUFFER);
-    iof_setup_file_handle_reader(I, NULL, 0, iof_file_get_fh(input)); // gets IOF_FILE_HANDLE flag and FILE *
-    I->space = PPDOC_BUFFER; // used on refill
-  }
-}
-
-/*
-Whenever we need to read the input file, we fseek the to the given offset and fread to to the private buffer.
-The length we need is not always predictable, in which case PPDOC_BUFFER bytes are read (keep it small).
-I->buf = I->pos is set to the beginning, I->end set to the end (end is the first byte one shouldn't read).
-*/
-
-static iof * ppdoc_reader (ppdoc *pdf, size_t offset, size_t length)
-{
-  iof_file *input;
-  iof *I;
-  input = &pdf->input;
-  I = &pdf->reader;
-  if (iof_file_seek(input, offset, SEEK_SET) != 0)
-    return NULL;
-  I->flags &= ~IOF_STOPPED;
-  if (input->flags & IOF_DATA)
-  {
-    I->buf = I->pos = input->pos;
-    I->end = (length == PP_LENGTH_UNKNOWN || I->pos + length >= input->end) ? input->end : (I->pos + length);
-  }
-  else
-  {
-    I->buf = I->pos = pdf->buffer; // ->buf is actually permanently equal pdf->buffer but we might need some tricks
-    if (length == PP_LENGTH_UNKNOWN || length > PPDOC_BUFFER)
-      length = PPDOC_BUFFER;
-    length = fread(I->buf, 1, length, I->file);
-    I->end = I->buf + length;
-  }
-  return I;
-}
-
-/* The position from the beginning of input
-- for data buffer: (pdf->input.pos - pdf->input.buf) + (I->pos - I->buf)
-  I->buf == pdf->input.pos, so this resolves to (I->pos - pdf->input.buf), independent from I->buf
-- for file buffer: ftell(pdf->input.file) - (I->end - I->pos)
-*/
-
-#define ppdoc_reader_tell(pdf, I) ((size_t)(((pdf)->input.flags & IOF_DATA) ? ((I)->pos - (pdf)->input.buf) : (ftell(iof_file_get_fh(&(pdf)->input)) - ((I)->end - (I)->pos))))
-
-/* pdf */
-
-#define PPDOC_HEADER 10 // "%PDF-?.??\n"
-
-static int ppdoc_header (ppdoc *pdf, uint8_t header[PPDOC_HEADER])
-{
-  size_t i;
-  if (memcmp(header, "%PDF-", 5) != 0)
-    return 0;
-  for (i = 5; i < PPDOC_HEADER - 1 && !ignored_char(header[i]); ++i)
-    pdf->version[i - 5] = header[i];
-  pdf->version[i - 5] = '\0';
-  return 1;
-}
-
-static int ppdoc_tail (ppdoc *pdf, iof_file *input, size_t *pxrefoffset)
-{
-  int c;
-  uint8_t tail[4*10], *p, back, tailbytes;
-
-  if (iof_file_seek(input, 0, SEEK_END) != 0)
-    return 0;
-  pdf->filesize = (size_t)iof_file_tell(input);
-  // simple heuristic to avoif fgetc() / fseek(-2) hiccup: keep seeking back by len(startxref) + 1 == 10
-  // until a letter found (assuming liberal white characters and tail length)
-  for (back = 1, tailbytes = 0; ; ++back)
-  {
-    if (iof_file_seek(input, -10, SEEK_CUR) != 0)
-      return 0;
-    tailbytes += 10;
-    c = iof_file_getc(input);
-    tailbytes -= 1;
-    switch (c)
-    {
-      case IGNORED_CHAR_CASE:
-      case DIGIT_CHAR_CASE:
-      case '%': case 'E': case 'O': case 'F':
-        if (back > 4) // 2 should be enough
-          return 0;
-        continue;
-      case 's': case 't': case 'a': case 'r': case 'x': case 'e': case 'f':
-        if (iof_file_read(tail, 1, tailbytes, input) != tailbytes)
-          return 0;
-        tail[tailbytes] = '\0';
-        for (p = &tail[0]; ; ++p)
-        {
-          if (*p == '\0')
-            return 0;
-          if ((c = base10_value(*p)) >= 0)
-            break;
-        }
-        ppread_uint(p, pxrefoffset);
-        return 1;
-      default:
-        return 0;
-    }
-  }
-  return 0;
-}
-
-/* xref/body */
-
-static int ppscan_start_entry (iof *I, ppref *ref)
-{
-  ppuint u;
-  ppscan_find(I); if (!ppscan_uint(I, &u) || u != ref->number) return 0;
-  ppscan_find(I); if (!ppscan_uint(I, &u) || u != ref->version) return 0;
-  ppscan_find(I); if (!ppscan_key(I, "obj")) return 0;
-  ppscan_find(I);
-  return 1;
-}
-
-static int ppscan_skip_entry (iof *I)
-{
-  size_t u;
-  ppscan_find(I); if (!ppscan_uint(I, &u)) return 0;
-  ppscan_find(I); if (!ppscan_uint(I, &u)) return 0;
-  ppscan_find(I); if (!ppscan_key(I, "obj")) return 0;
-  ppscan_find(I);
-  return 1;
-}
-
-static int ppscan_start_stream (iof *I, ppdoc *pdf, size_t *streamoffset)
-{
-  int c;
-  ppscan_find(I);
-  if (ppscan_key(I, "stream"))
-  { // skip 1 or 2 whites (here we shouldn't just gobble all blanks)
-    c = iof_char(I);
-    if (ignored_char(c))
-    {
-      c = iof_next(I);
-      if (ignored_char(c))
-        ++I->pos;
-    }
-    *streamoffset = ppdoc_reader_tell(pdf, I);
-    return 1;
-  }
-  return 0;
-}
-
-static ppxref * ppxref_load (ppdoc *pdf, size_t xrefoffset);
-static ppxref * ppxref_load_chain (ppdoc *pdf, ppxref *xref);
-
-/* Parsing xref table
-
-  1 10               // first ref number and refs count
-  0000000000 00000 n // 10-digits offset, 5 digits version, type identifier
-  0000000000 00000 n // n states for normal I guess
-  0000000000 00000 f // f states for free (not used)
-  ...
-
-Free entries seem to be a relic of ancient times, completelly useless for us. To avoid parsing xref table twice,
-we waste some space on free entries by allocating one plane of refs for each section. Later on we slice sections,
-so that effectively free entries are not involved in map.
-
-Subsequent refs gets number, version and offset. Other fields initialized when parsing PDF body.
-
-Having xref table loaded, we sort sections for future binary search (xref with objects count == 0 is considered invalid).
-
-Then we have to deal with the trailer dict. In general, to load objects and resolve references we need a complete chain
-of xrefs (not only the top). To load the previous xref, we need its offset, which is given in trailer. So we have to
-parse the trailer ignoring references, which might be unresolvable at this point (objects parser makes a dummy check
-for xref != NULL on refs resolving ppscan_obj(), which irritates me but I don't want a separate parser for trailer..).
-The same applies to xref streams, in which we have parse the trailer not having xref map at all. So the procedure is:
-
-  - load xref map, initialize references, make it ready to search
-  - parse trailer ignoring references
-  - get /Prev xref offset and load older xref (linked list via ->prev)
-  - sort all refs in all xrefs by offset
-  - parse refs in order resolving references in contained objects
-  - fix trailer references
-
-First created xref becomes a pdf->xref (top xref). We link that early to control offsets already read (insane loops?).
-*/
-
-// Every xref table item "0000000000 00000 n" is said to be terminated with 2-byte EOL but we don't like relying on whites.
-#define xref_item_length (10+1+5+1+1)
-
-static ppxref * ppxref_load_table (iof *I, ppdoc *pdf, size_t xrefoffset)
-{
-  ppxref *xref;
-  ppxsec *xrefsection;
-  ppref *ref;
-  ppuint first, count, refindex;
-  uint8_t buffer[xref_item_length + 1];
-  const char *p;
-  const ppobj *obj;
-
-  buffer[xref_item_length] = '\0';
-  xref = ppxref_create(pdf, 0, xrefoffset);
-  if (pdf->xref == NULL) pdf->xref = xref;
-  for (ppscan_find(I); ppscan_uint(I, &first); ppscan_find(I))
-  {
-    ppscan_find(I);
-    if (!ppscan_uint(I, &count))
-      return NULL;
-    if (count == 0) // weird
-      continue;
-    xref->count += count;
-    xrefsection = NULL;
-    ref = (ppref *)ppheap_take(&pdf->heap, count * sizeof(ppref));
-    for (refindex = 0; refindex < count; ++refindex, ++ref)
-    {
-      ref->xref = xref;
-      ref->number = first + refindex;
-      ppscan_find(I);
-      iof_read(I, buffer, xref_item_length);
-      switch (buffer[xref_item_length - 1])
-      {
-        case 'n':
-          if (xrefsection == NULL)
-          {
-            xrefsection = ppxref_push_section(xref, &pdf->heap);
-            xrefsection->first = ref->number;
-            xrefsection->refs = ref;
-          }
-          xrefsection->last = ref->number;
-          for (p = (const char *)buffer; *p == '0'; ++p);
-          p = ppread_uint(p, &ref->offset);
-          for ( ; *p == ' ' || *p == '0'; ++p);
-          p = ppread_uint(p, &ref->version);
-          ref->object.type = PPNONE; // init for sanity
-          ref->object.any = NULL;
-          ref->length = 0;
-          break;
-        case 'f':
-        default:
-          --ref;
-          xrefsection = NULL;
-          --xref->count;
-      }
-    }
-  }
-  /* sort section */
-  if (!ppxref_sort(xref))
-    ; // case of xref->size == 0 handled by ppxref_load_chain()
-  /* get trailer ignoring refs */
-  if (!ppscan_key(I, "trailer"))
-    return NULL;
-  ppscan_find(I);
-  if ((obj = ppscan_obj(I, pdf, NULL)) == NULL)
-    return NULL;
-  ppstack_pop(&pdf->stack, 1);
-  if (obj->type != PPDICT)
-    return NULL;
-  xref->trailer = *obj;
-  return ppxref_load_chain(pdf, xref);
-}
-
-/* Parsing xref stream
-First we load the trailer, ignoring references. Dict defines sections and fields lengths:
-
-  /Size                                  % max ref number plus 1
-  /Index [ first count first count ... ] % a pair of numbers for every section, defaults to [0 Size]
-  /W [w1 w2 w3]                          % fields lengths, 0 states for omitted field
-
-xref stream data is a continuous stream of binary number triplets. First number is a type:
-
-  0 - free entry (as 'f' in xref table)
-  1 - normal entry, followed by offset an version (as 'n' in xref table)
-  2 - compressed entry, followed by parent object stream number and entry index
-
-0 and 1 are handled as 'n' and 'f' entries in xref table. For type 2 we normally initialize
-ref->number and ref->version (the later is implicitly 0). ref->offset is set to 0 (invalid offset),
-which is recognized by objects loader.
-*/
-
-#define XREF_STREAM_MAX_FIELD 4
-
-static ppxref * ppxref_load_stream (iof *I, ppdoc *pdf, size_t xrefoffset)
-{
-  ppxref *xref;
-  ppxsec *xrefsection;
-  ppref *ref;
-  ppobj *obj;
-  ppstream *xrefstream;
-  size_t streamoffset;
-  ppuint w1, w2, w3, w, bufferbytes;
-  uint8_t buffer[3 * XREF_STREAM_MAX_FIELD], *b;
-  ppuint first, count, f1, f2, f3;
-  pparray *fieldwidths, *sectionindices;
-  ppobj sectionmock[2], *sectionfirst, *sectioncount;
-  size_t sections, sectionindex, refindex;
-
-  if (!ppscan_skip_entry(I))
-    return NULL;
-  if ((obj = ppscan_obj(I, pdf, NULL)) == NULL)
-    return NULL;
-  ppstack_pop(&pdf->stack, 1);
-  if (obj->type != PPDICT || !ppscan_start_stream(I, pdf, &streamoffset))
-    return NULL;
-  xrefstream = ppstream_create(pdf, obj->dict, streamoffset);
-  /* All normal streams go through ppstream_info(), but it makes no sense for trailer stream (no crypt allowed, no refs yet).
-     So we just record the length and informative flag. Here we have to expect that /Length and /Filter are not indirects. */
-  if (!ppdict_get_uint(obj->dict, "Length", &xrefstream->length))
-    return NULL;
-  if (ppdict_get_obj(obj->dict, "Filter") != NULL)
-    xrefstream->flags |= PPSTREAM_COMPRESSED;
-  if ((fieldwidths = ppdict_get_array(xrefstream->dict, "W")) != NULL)
-  {
-    if (!pparray_get_uint(fieldwidths, 0, &w1)) w1 = 0;
-    if (!pparray_get_uint(fieldwidths, 1, &w2)) w2 = 0;
-    if (!pparray_get_uint(fieldwidths, 2, &w3)) w3 = 0;
-  }
-  else
-    w1 = w2 = w3 = 0;
-  if (w1 > XREF_STREAM_MAX_FIELD || w2 > XREF_STREAM_MAX_FIELD || w3 > XREF_STREAM_MAX_FIELD)
-    return NULL;
-  bufferbytes = w1 + w2 + w3;
-  if ((sectionindices = ppdict_get_array(xrefstream->dict, "Index")) != NULL)
-  {
-    sections = sectionindices->size >> 1;
-    sectionfirst = sectionindices->data;
-  }
-  else
-  {
-    sections = 1;
-    sectionmock[0].type = PPINT;
-    sectionmock[0].integer = 0;
-    sectionmock[1].type = PPINT;
-    if (!ppdict_get_int(xrefstream->dict, "Size", &sectionmock[1].integer))
-      sectionmock[1].integer = 0;
-    sectionfirst = &sectionmock[0];
-  }
-  if ((I = ppstream_read(xrefstream, 1, 0)) == NULL)
-    return NULL; // we fseek() so original I is useless anyway
-  xref = ppxref_create(pdf, sections, xrefoffset);
-  if (pdf->xref == NULL) pdf->xref = xref;
-  xref->trailer.type = PPSTREAM;
-  xref->trailer.stream = xrefstream;
-  for (sectionindex = 0; sectionindex < sections; ++sectionindex, sectionfirst += 2)
-  {
-    sectioncount = sectionfirst + 1;
-    if (!ppobj_get_uint(sectionfirst, first) || !ppobj_get_uint(sectioncount, count))
-      goto xref_stream_error;
-    if (count == 0)
-      continue;
-    xref->count += count;
-    xrefsection = NULL;
-    ref = (ppref *)ppheap_take(&pdf->heap, count * sizeof(ppref));
-    for (refindex = 0; refindex < count; ++refindex, ++ref)
-    {
-      ref->xref = xref;
-      ref->number = first + refindex;
-      if (iof_read(I, buffer, bufferbytes) != bufferbytes)
-        goto xref_stream_error;
-      b = buffer;
-      if (w1 == 0)
-        f1 = 1; // default type is 1
-      else
-        for (f1 = 0, w = 0; w < w1; f1 = (f1 << 8)|(*b), ++w, ++b);
-      for (f2 = 0, w = 0; w < w2; f2 = (f2 << 8)|(*b), ++w, ++b);
-      for (f3 = 0, w = 0; w < w3; f3 = (f3 << 8)|(*b), ++w, ++b);
-      switch (f1)
-      {
-        case 0:
-          //--ref;
-          xrefsection = NULL;
-          --xref->count;
-          break;
-        case 1:
-          if (xrefsection == NULL)
-          {
-            xrefsection = ppxref_push_section(xref, &pdf->heap);
-            xrefsection->first = ref->number;
-            xrefsection->refs = ref;
-          }
-          xrefsection->last = ref->number;
-          ref->offset = f2;
-          ref->version = f3;
-          ref->object.type = PPNONE;
-          ref->object.any = NULL;
-          ref->length = 0;
-          break;
-        case 2:
-          if (xrefsection == NULL)
-          {
-            xrefsection = ppxref_push_section(xref, &pdf->heap);
-            xrefsection->first = ref->number;
-            xrefsection->refs = ref;
-          }
-          xrefsection->last = ref->number;
-          ref->offset = 0; // f2 is parent objstm, f3 is index in parent, both useless
-          ref->version = 0; // compressed objects has implicit version == 0
-          ref->object.type = PPNONE;
-          ref->object.any = NULL;
-          ref->length = 0;
-          break;
-        default:
-          goto xref_stream_error;
-      }
-    }
-  }
-  /* sort sections */
-  if (!ppxref_sort(xref))
-    ; // case of xref->size == 0 handled by ppxref_load_chain()
-  /* close the stream _before_ loading prev xref */
-  ppstream_done(xrefstream);
-  /* load prev and return */
-  return ppxref_load_chain(pdf, xref);
-xref_stream_error:
-  ppstream_done(xrefstream);
-  return NULL;
-}
-
-/*
-The following procedure loads xref /Prev, links xref->prev and typically returns xref.
-Some docs contain empty xref (one section with zero objects) that is actually a proxy
-to xref stream referred as /XRefStm (genuine concept of xrefs old/new style xrefs in
-the same doc). In case of 0-length xref we ignore the proxy and return the target xref
-(otherwise we would need annoying sanity check for xref->size > 0 on every ref search).
-*/
-
-static ppxref * ppxref_load_chain (ppdoc *pdf, ppxref *xref)
-{
-  ppdict *trailer;
-  ppuint xrefoffset;
-  ppxref *prevxref, *nextxref;
-
-  trailer = ppxref_trailer(xref);
-  if (!ppdict_get_uint(trailer, "Prev", &xrefoffset)) // XRefStm is useless
-    return xref; // missing /Prev is obviously ok
-  for (nextxref = pdf->xref; nextxref != NULL; nextxref = nextxref->prev)
-    if (nextxref->offset == xrefoffset) // insane
-      return NULL;
-  if ((prevxref = ppxref_load(pdf, (size_t)xrefoffset)) == NULL)
-    return NULL;
-  if (xref->size > 0)
-  {
-    xref->prev = prevxref;
-    return xref;
-  }
-  if (pdf->xref == xref)
-    pdf->xref = prevxref;
-  return prevxref;
-}
-
-static ppxref * ppxref_load (ppdoc *pdf, size_t xrefoffset)
-{
-  iof *I;
-  if ((I = ppdoc_reader(pdf, xrefoffset, PP_LENGTH_UNKNOWN)) == NULL)
-    return NULL;
-  ppscan_find(I);
-  if (ppscan_key(I, "xref"))
-    return ppxref_load_table(I, pdf, xrefoffset);
-  return ppxref_load_stream(I, pdf, xrefoffset);
-  // iof_close(I) does nothing here
-}
-
-static void ppoffmap_sort (ppref **left, ppref **right)
-{
-  ppref **l, **r, *t;
-  ppuint pivot;
-  l = left, r = right;
-  pivot = (*(l + ((r - l) / 2)))->offset;
-  do
-  { // don't read from pointer!
-    while ((*l)->offset < pivot) ++l;
-    while ((*r)->offset > pivot) --r;
-    if (l <= r)
-    {
-      t = *l;
-      *l = *r;
-      *r = t;
-      ++l, --r;
-    }
-  } while (l <= r);
-  if (left < r)
-    ppoffmap_sort(left, r);
-  if (l < right)
-    ppoffmap_sort(l, right);
-}
-
-
-static void fix_trailer_references (ppdoc *pdf)
-{
-  ppxref *xref;
-  ppdict *trailer;
-  ppname *pkey;
-  ppobj *obj;
-  ppref *ref;
-  for (xref = pdf->xref; xref != NULL; xref = xref->prev)
-  {
-    if ((trailer = ppxref_trailer(xref)) == NULL)
-      continue;
-    for (ppdict_first(trailer, pkey, obj); *pkey != NULL; ppdict_next(pkey, obj))
-    { // no need to go deeper in structs, all items in trailer except info and root must be direct refs
-      if (obj->type != PPREF)
-        continue;
-      ref = obj->ref;
-      if (ref->offset == 0) // unresolved?
-        if ((ref = ppxref_find(xref, ref->number)) != NULL)
-          obj->ref = ref; // at this moment the reference still points nothing, but should be the one with the proper offset
-    }
-  }
-}
-
-/*
-Here comes a procedure that loads all entries from all document bodies. We resolve references while
-parsing objects and to make resolving correct, we need a complete chain of xref maps, and a knowledge
-about possible linearized dict (first offset). So loading refs sorted by offsets makes sense (not sure
-if it matters nowadays but we also avoid fseek() by large offsets).
-
-Here is the proc:
-
-  - create a list of all refs in all bodies
-  - sort the list by offsets
-  - for every ref from the sorted list:
-    - estimate object length to avoid fread-ing more than necessary (not perfect but enough)
-    - fseek() to the proper offset, fread() entry data or its part
-    - parse the object with ppscan_obj(I, pdf, xref), where xref is not necessarily top pdf->xref
-    - save the actual ref->length (not sure if we need that?)
-    - make a stream if a dict is followed by "stream" keyword, also save the stream offset
-  - free the list
-*/
-
-static int ppdoc_load_objstm (ppstream *stream, ppdoc *pdf, ppxref *xref);
-
-static void ppdoc_load_entries (ppdoc *pdf)
-{
-  size_t objects, sectionindex, refnumber, offindex;
-  ppnum linearized;
-  ppref **offmap, **pref, *ref;
-  ppxref *xref;
-  ppxsec *xsec;
-  ppobj *obj;
-  ppname type;
-  int redundant_indirection = 0;
-  ppcrypt *crypt;
-  ppstream *stream;
-
-  if ((objects = (size_t)ppdoc_objects(pdf)) == 0) // can't happen
-    return;
-  pref = offmap = (ppref **)pp_malloc(objects * sizeof(ppref *));
-  objects = 0; // recount refs with offset > 0
-  for (xref = pdf->xref; xref != NULL; xref = xref->prev)
-    for (sectionindex = 0, xsec = xref->sects; sectionindex < xref->size; ++sectionindex, ++xsec)
-      for (refnumber = xsec->first, ref = xsec->refs; refnumber <= xsec->last; ++refnumber, ++ref)
-        if (ref->offset > 0) // 0 means compressed or insane
-          *pref++ = ref, ++objects;
-  ppoffmap_sort(offmap, offmap + objects - 1);
-
-  crypt = pdf->crypt;
-  for (offindex = 0, pref = offmap; offindex < objects; )
-  {
-    ref = *pref;
-    ++pref;
-    ++offindex;
-    if (ref->object.type != PPNONE) // might be preloaded already (/Encrypt dict, stream filter dicts, stream /Length..)
-    	continue;
-    if (offindex < objects)
-      ref->length = (*pref)->offset - ref->offset;
-    else
-      ref->length = pdf->filesize > ref->offset ? pdf->filesize - ref->offset : 0;
-    if (crypt != NULL)
-    {
-      ppcrypt_start_ref(crypt, ref);
-      obj = ppdoc_load_entry(pdf, ref);
-      ppcrypt_end_ref(crypt);
-    }
-    else
-    {
-      obj = ppdoc_load_entry(pdf, ref);
-    }
-    switch (obj->type)
-    {
-      case PPDICT: /* Check if the object at first offset is linearized dict. We need that to resolve all references properly. */
-        if (offindex == 1 && ppdict_get_num(obj->dict, "Linearized", &linearized)) // /Linearized value is a version number, default 1.0
-          pdf->flags |= PPDOC_LINEARIZED;
-        break;
-      case PPREF:
-        redundant_indirection = 1;
-        break;
-      default:
-        break;
-    }
-    // if pdf->crypt crypt->ref = NULL
-  }
-
-  /* refs pointngs refs? cut. */
-  if (redundant_indirection)
-  {
-    for (offindex = 0, pref = offmap; offindex < objects; ++offindex)
-    {
-      ref = *pref++;
-      if (ref->object.type == PPREF)
-        ref->object = ref->object.ref->object; // doing for all effectively cuts all insane chains
-    }
-  }
-
-  /* now handle streams; update stream info (eg. /Length), load pdf 1.5 object streams
-     we could do earlier but then we would need to struggle with indirects */
-  for (offindex = 0, pref = offmap; offindex < objects; ++offindex)
-  {
-    ref = *pref++;
-    obj = &ref->object;
-    if (obj->type != PPSTREAM)
-      continue;
-    stream = obj->stream;
-    if (crypt != NULL)
-    {
-      ppcrypt_start_ref(crypt, ref);
-      ppstream_info(stream, pdf);
-      ppcrypt_end_ref(crypt);
-    }
-    else
-    {
-      ppstream_info(stream, pdf);
-    }
-    if (ref->xref->trailer.type == PPSTREAM && (type = ppdict_get_name(stream->dict, "Type")) != NULL && ppname_is(type, "ObjStm")) // somewhat dummy..
-      if (!ppdoc_load_objstm(stream, pdf, ref->xref))
-        loggerf("invalid objects stream %s at offset " PPSIZEF, ppref_str(ref->number, ref->version), ref->offset);
-  }
-  pp_free(offmap);
-}
-
-ppobj * ppdoc_load_entry (ppdoc *pdf, ppref *ref)
-{
-  iof *I;
-  size_t length;
-  ppxref *xref;
-  ppobj *obj;
-  ppstack *stack;
-  size_t streamoffset;
-  ppref *refref;
-  ppuint refnumber, refversion;
-
-  length = ref->length > 0 ? ref->length : PP_LENGTH_UNKNOWN; // estimated or unknown
-  if ((I = ppdoc_reader(pdf, ref->offset, length)) == NULL || !ppscan_start_entry(I, ref))
-  {
-    loggerf("invalid %s offset " PPSIZEF, ppref_str(ref->number, ref->version), ref->offset);
-    return &ref->object; // PPNONE
-  }
-  stack = &pdf->stack;
-  xref = ref->xref; // to resolve indirects properly
-  if ((obj = ppscan_obj(I, pdf, xref)) == NULL)
-  {
-    loggerf("invalid %s object at offset " PPSIZEF, ppref_str(ref->number, ref->version), ref->offset);
-    return &ref->object; // PPNONE
-  }
-  ref->object = *obj;
-  ppstack_pop(stack, 1);
-  obj = &ref->object;
-  ref->length = ppdoc_reader_tell(pdf, I) - ref->offset;
-  if (obj->type == PPDICT)
-  {
-    if (ppscan_start_stream(I, pdf, &streamoffset))
-    {
-      obj->type = PPSTREAM;
-      obj->stream = ppstream_create(pdf, obj->dict, streamoffset);
-    }
-  }
-  else if (obj->type == PPINT)
-  {
-    ppscan_find(I);
-    if (ppscan_uint(I, &refversion) && ppscan_find(I) == 'R')
-    {
-      refnumber = (ppuint)obj->integer;
-      if ((refref = ppxref_find(xref, refnumber)) != NULL)
-      {
-        obj->type = PPREF;
-        obj->ref = refref;
-      }
-      else
-      {
-        obj->type = PPNONE; // as ppref_unresolved()
-        obj->any = NULL;
-      }
-    }
-  }
-  return obj;
-}
-
-/* Loading entries from object stream
-
-  /N is the number of contained entries
-  /First is the offset of the first item
-
-The stream consists of N pairs of numbers <objnum> <offset> <objnum> <offset> ...
-Offsets are ascending (relative to the first), but ref numbers order is arbitrary.
-PDF spec says there might be some additional data between objects, so we should obey offsets.
-Which means we should basically load the stream at once (may be needed anyway to grab the stream [...]).
-*/
-
-static int ppdoc_load_objstm (ppstream *stream, ppdoc *pdf, ppxref *xref)
-{
-  ppdict *dict; // stream dict, actually still on stack
-  ppref *ref;
-  ppobj *obj;
-  ppuint items, firstoffset, offset, objnum, i, invalid = 0;
-  iof *I;
-  uint8_t *firstdata, *indexdata;
-  ppstack *stack;
-
-  dict = stream->dict;
-  if (!ppdict_rget_uint(dict, "N", &items) || !ppdict_rget_uint(dict, "First", &firstoffset))
-    return 0;
-  if ((I = ppstream_read(stream, 1, 1)) == NULL)
-    return 0;
-  firstdata = I->pos + firstoffset;
-  if (firstdata >= I->end)
-    goto invalid_objstm;
-  stack = &pdf->stack;
-  //if (pdf->crypt != NULL)
-  //  ppcrypt_end_ref(pdf->crypt); // objects are not encrypted, pdf->crypt->ref ensured NULL
-  for (i = 0; i < items; ++i)
-  {
-    ppscan_find(I);
-    if (!ppscan_uint(I, &objnum))
-      goto invalid_objstm;
-    ppscan_find(I);
-    if (!ppscan_uint(I, &offset))
-      goto invalid_objstm;
-    if ((ref = ppxref_find_local(xref, objnum)) == NULL || ref->object.type != PPNONE)
-    {
-      loggerf("invalid compressed object number " PPUINTF " at position " PPUINTF, objnum, i);
-      ++invalid;
-      continue;
-    }
-    if (firstdata + offset >= I->end)
-    {
-      loggerf("invalid compressed object offset " PPUINTF " at position " PPUINTF, offset, i);
-      ++invalid;
-      continue;
-    }
-    indexdata = I->pos; // save position
-    I->pos = firstdata + offset; // go to the object
-    ppscan_find(I);
-    if ((obj = ppscan_obj(I, pdf, xref)) != NULL)
-    {
-      ref->object = *obj;
-      ppstack_pop(stack, 1);
-      // nothing more needed, as obj can never be indirect ref or stream
-    }
-    else
-    {
-      ++invalid;
-      loggerf("invalid compressed object %s at stream offset " PPUINTF, ppref_str(objnum, 0), offset);
-    }
-    I->pos = indexdata; // restore position and read next from index
-  }
-  ppstream_done(stream);
-  return invalid == 0;
-invalid_objstm:
-  ppstream_done(stream);
-  return 0;
-}
-
-/* main PDF loader proc */
-
-ppcrypt_status ppdoc_crypt_pass (ppdoc *pdf, const void *userpass, size_t userpasslength, const void *ownerpass, size_t ownerpasslength)
-{
-  switch (pdf->cryptstatus)
-  {
-    case PPCRYPT_NONE:
-    case PPCRYPT_DONE:
-    case PPCRYPT_FAIL:
-      break;
-    case PPCRYPT_PASS: // initial status or really needs password
-      pdf->cryptstatus = ppdoc_crypt_init(pdf, userpass, userpasslength, ownerpass, ownerpasslength);
-      switch (pdf->cryptstatus)
-      {
-        case PPCRYPT_NONE:
-        case PPCRYPT_DONE:
-          ppdoc_load_entries(pdf);
-          break;
-        case PPCRYPT_PASS: // user needs to check ppdoc_crypt_status() and recall ppdoc_crypt_pass() with the proper password
-        case PPCRYPT_FAIL: // hopeless..
-          break;
-      }
-      break;
-  }
-  return pdf->cryptstatus;
-}
-
-static ppdoc * ppdoc_read (ppdoc *pdf, iof_file *input)
-{
-  uint8_t header[PPDOC_HEADER];
-  size_t xrefoffset;
-
-  input = &pdf->input;
-  if (iof_file_read(header, 1, PPDOC_HEADER, input) != PPDOC_HEADER || !ppdoc_header(pdf, header))
-    return NULL;
-  if (!ppdoc_tail(pdf, input, &xrefoffset))
-    return NULL;
-  if (ppxref_load(pdf, xrefoffset) == NULL)
-    return NULL;
-  fix_trailer_references(pdf); // after loading xrefs but before accessing trailer refs (/Encrypt might be a reference)
-  // check encryption, if any, try empty password
-  switch (ppdoc_crypt_pass(pdf, "", 0, NULL, 0))
-  {
-    case PPCRYPT_NONE: // no encryption
-    case PPCRYPT_DONE: // encryption with an empty password
-    case PPCRYPT_PASS: // the user needs to check ppdoc_crypt_status() and call ppdoc_crypt_pass()
-      break;
-    case PPCRYPT_FAIL: // hopeless
-      //loggerf("decryption failed");
-      //return NULL;
-      break;
-  }
-  return pdf;
-}
-
-static void ppdoc_pages_init (ppdoc *pdf);
-
-static ppdoc * ppdoc_create (iof_file *input)
-{
-  ppdoc *pdf;
-  ppheap *heap;
-
-  heap = ppheap_new();
-  pdf = (ppdoc *)ppheap_take(&heap, sizeof(ppdoc));
-  pdf->flags = 0;
-  pdf->heap = heap;
-  pdf->xref = NULL;
-  pdf->version[0] = '\0';
-  pdf->crypt = NULL;
-  pdf->cryptstatus = PPCRYPT_PASS; // force encryption check on ppdoc_read() -> ppdoc_crypt_pass()
-  ppstack_init(&pdf->stack, &pdf->heap);
-  ppdoc_reader_init(pdf, input);
-  ppdoc_pages_init(pdf);
-  if (ppdoc_read(pdf, &pdf->input) != NULL)
-    return pdf;
-  ppdoc_free(pdf);
-  return NULL;
-}
-
-ppdoc * ppdoc_load (const char *filename)
-{
-  FILE *file;
-  iof_file input;
-  if ((file = fopen(filename, "rb")) == NULL)
-    return NULL;
-  iof_file_init(&input, file);
-  input.flags |= IOF_CLOSE_FILE;
-  return ppdoc_create(&input);
-}
-
-ppdoc * ppdoc_mem (const void *data, size_t size)
-{
-	iof_file input;
-	iof_file_rdata_init(&input, data, size);
-	input.flags |= IOF_BUFFER_ALLOC; // todo: 3 modes: borrow, take over, copy?
-	return ppdoc_create(&input);
-}
-
-void ppdoc_free (ppdoc *pdf)
-{
-  //iof_file_free(&pdf->input);
-  iof_file_decref(&pdf->input);
-  ppstack_free_buffer(&pdf->stack);
-  ppheap_free(pdf->heap); // last!
-}
-
-ppcrypt_status ppdoc_crypt_status (ppdoc *pdf)
-{
-  return pdf->cryptstatus;
-}
-
-ppint ppdoc_permissions (ppdoc *pdf)
-{
-  return pdf->crypt != NULL ? pdf->crypt->permissions : (ppint)0xFFFFFFFFFFFFFFFF;
-}
-
-/* pages access */
-
-static pparray * pppage_node (ppdict *dict, ppuint *count, ppname *type)
-{
-  ppname *pkey, key;
-  ppobj *obj;
-  pparray *kids = NULL;
-  *count = 0;
-  *type = NULL;
-  for (ppdict_first(dict, pkey, obj); (key = *pkey) != NULL; ppdict_next(pkey, obj))
-  {
-    switch (key[0])
-    {
-      case 'T':
-        if (ppname_is(key, "Type"))
-          *type = ppobj_get_name(obj);
-        break;
-      case 'C':
-        if (ppname_is(key, "Count"))
-          ppobj_get_uint(obj, *count);
-        break;
-      case 'K':
-        if (ppname_is(key, "Kids"))
-          kids = ppobj_rget_array(obj);
-        break;
-    }
-  }
-  return kids;
-}
-
-#define ppname_is_page(type) (type != NULL && ppname_is(type, "Page"))
-
-ppuint ppdoc_page_count (ppdoc *pdf)
-{
-  ppref *ref;
-  ppname type;
-  ppuint count;
-  if ((ref = ppxref_pages(pdf->xref)) == NULL)
-    return 0;
-  if (pppage_node(ref->object.dict, &count, &type) == NULL)
-    return ppname_is_page(type) ? 1 : 0; // acrobat and ghostscript accept documents with root /Pages entry being a reference to a sole /Page object
-  return count;
-}
-
-ppref * ppdoc_page (ppdoc *pdf, ppuint index)
-{
-  ppdict *dict;
-  ppuint count;
-  pparray *kids;
-  size_t size, i;
-  ppobj *r, *o;
-  ppref *ref;
-  ppname type;
-
-
-  if ((ref = ppxref_pages(pdf->xref)) == NULL)
-    return NULL;
-  dict = ref->object.dict;
-  if ((kids = pppage_node(dict, &count, &type)) != NULL)
-  {
-    if (index < 1 || index > count)
-      return NULL;
-  }
-  else
-  {
-    return index == 1 && ppname_is_page(type) ? ref : NULL;
-  }
-scan_array:
-  if (index <= count / 2)
-  { // probably shorter way from the beginning
-    for (i = 0, size = kids->size, r = pparray_at(kids, 0); i < size; ++i, ++r)
-    {
-      if (r->type != PPREF)
-        return NULL;
-      o = &r->ref->object;
-      if (o->type != PPDICT)
-        return NULL;
-      dict = o->dict;
-      if ((kids = pppage_node(dict, &count, &type)) != NULL)
-      {
-        if (index <= count)
-          goto scan_array;
-        index -= count;
-        continue;
-      }
-      if (index == 1 && ppname_is_page(type))
-        return r->ref;
-      --index;
-    }
-  }
-  else if ((size = kids->size) > 0) // for safe (size-1)
-  { // probably shorter way from the end
-    index = count - index + 1;
-    for (i = 0, r = pparray_at(kids, size - 1); i < size; ++i, --r)
-    {
-      if (r->type != PPREF)
-        return NULL;
-      o = &r->ref->object;
-      if (o->type != PPDICT)
-        return NULL;
-      dict = o->dict;
-      if ((kids = pppage_node(dict, &count, &type)) != NULL)
-      {
-        if (index <= count)
-          goto scan_array;
-        index -= count;
-        continue;
-      }
-      if (index == 1 && ppname_is_page(type))
-        return r->ref;
-      --index;
-    }
-  }
-  return NULL;
-}
-
-/*
-Through pages iterator. Iterating over pages tree just on the base of /Kids and /Parent keys
-is ineffective, as to get next pageref we need to take parent, find the pageref in /Kids,
-take next (or go upper).. Annoying. We use a dedicated stack for pages iterator. This could
-actually be done with pdf->stack, but some operations may clear it, so safer to keep it independent
-Besides, its depth is constant (set on first use), so no need for allocs.
-*/
-
-static void ppdoc_pages_init (ppdoc *pdf)
-{
-  pppages *pages;
-  pages = &pdf->pages;
-  pages->root = pages->parent = pages->buffer;
-  pages->depth = 0;
-  pages->space = PPPAGES_STACK_DEPTH;
-}
-
-static ppkids * pppages_push (ppdoc *pdf, pparray *kids)
-{
-  ppkids *newroot, *bounds;
-  pppages *pages;
-  pages = &pdf->pages;
-  if (pages->depth == pages->space)
-  {
-    pages->space <<= 1;
-    newroot = (ppkids *)ppheap_take(&pdf->heap, pages->space * sizeof(ppkids));
-    memcpy(newroot, pages->root, pages->depth * sizeof(ppkids));
-    pages->root = newroot;
-  }
-  bounds = pages->parent = &pages->root[pages->depth++];
-  bounds->current = pparray_at(kids, 0);
-  bounds->sentinel = pparray_at(kids, kids->size);
-  return bounds;
-}
-
-#define pppages_pop(pages) (--((pages)->parent), --((pages)->depth))
-
-static ppref * ppdoc_pages_group_first (ppdoc *pdf, ppref *ref)
-{
-  ppdict *dict;
-  pparray *kids;
-  ppuint count;
-  ppname type;
-
-  dict = ref->object.dict; // typecheck made by callers
-  while ((kids = pppage_node(dict, &count, &type)) != NULL)
-  {
-    if ((ref = pparray_get_ref(kids, 0)) == NULL || ref->object.type != PPDICT)
-      return NULL;
-    pppages_push(pdf, kids);
-    dict = ref->object.dict;
-  }
-  return ppname_is_page(type) ? ref : NULL;
-}
-
-ppref * ppdoc_first_page (ppdoc *pdf)
-{
-  ppref *ref;
-  pppages *pages;
-  if ((ref = ppdoc_pages(pdf)) == NULL)
-    return NULL;
-  pages = &pdf->pages;
-  pages->parent = pages->root;
-  pages->depth = 0;
-  return ppdoc_pages_group_first(pdf, ref);
-}
-
-ppref * ppdoc_next_page (ppdoc *pdf)
-{
-  pppages *pages;
-  ppkids *bounds;
-  ppref *ref;
-  ppobj *obj;
-  pages = &pdf->pages;
-  while (pages->depth > 0)
-  {
-    bounds = pages->parent;
-    obj = ++bounds->current;
-    if (obj < bounds->sentinel)
-    {
-      if (obj->type != PPREF)
-        return NULL;
-      ref = obj->ref;
-      if (ref->object.type != PPDICT)
-        return NULL;
-      return ppdoc_pages_group_first(pdf, ref);
-    }
-    else
-    { // no next node, go upper
-      pppages_pop(pages);
-    }
-  }
-  return NULL;
-}
-
-/* context */
-
-ppcontext * ppcontext_new (void)
-{
-  ppheap *heap;
-  ppcontext *context;
-  heap = ppheap_new();
-  context = (ppcontext *)pp_malloc(sizeof(ppcontext)); // not from priv heap, as we delete it on renew
-  context->heap = heap;
-  ppstack_init(&context->stack, &context->heap);
-  return context;
-}
-
-void ppcontext_done (ppcontext *context)
-{
-  ppheap_renew(context->heap);
-  ppstack_clear(&context->stack);
-}
-
-void ppcontext_free (ppcontext *context)
-{
-  ppstack_free_buffer(&context->stack);
-  ppheap_free(context->heap);
-  pp_free(context);
-}
-
-/* page contents streams */
-
-//#define ppcontents_first_stream(array) pparray_rget_stream(array, 0)
-
-static ppstream * ppcontents_first_stream (pparray *array)
-{
-  size_t i;
-  ppobj *obj;
-  ppref *ref;
-  for (pparray_first(array, i, obj); i < array->size; pparray_next(i, obj))
-    if ((ref = ppobj_get_ref(obj)) != NULL && ref->object.type == PPSTREAM)
-      return ref->object.stream;
-  return NULL;
-}
-
-static ppstream * ppcontents_next_stream (pparray *array, ppstream *stream)
-{
-  size_t i;
-  ppobj *obj;
-  ppref *ref;
-  for (pparray_first(array, i, obj); i < array->size; pparray_next(i, obj))
-    if ((ref = ppobj_get_ref(obj)) != NULL && ref->object.type == PPSTREAM && ref->object.stream == stream)
-      if (++i < array->size && (ref = ppobj_get_ref(obj + 1)) != NULL && ref->object.type == PPSTREAM)
-        return ref->object.stream;
-  return NULL;
-}
-
-ppstream * ppcontents_first (ppdict *dict)
-{
-  ppobj *contentsobj;
-  if ((contentsobj = ppdict_rget_obj(dict, "Contents")) == NULL)
-    return NULL;
-  switch (contentsobj->type)
-  {
-    case PPARRAY:
-      return ppcontents_first_stream(contentsobj->array);
-    case PPSTREAM:
-      return contentsobj->stream;
-    default:
-      break;
-  }
-  return NULL;
-}
-
-ppstream * ppcontents_next (ppdict *dict, ppstream *stream)
-{
-  ppobj *contentsobj;
-  if ((contentsobj = ppdict_rget_obj(dict, "Contents")) == NULL)
-    return NULL;
-  switch (contentsobj->type)
-  {
-    case PPARRAY:
-      return ppcontents_next_stream(contentsobj->array, stream);
-    case PPSTREAM:
-      break;
-    default:
-      break;
-  }
-  return NULL;
-}
-
-static ppobj * ppcontents_op (iof *I, ppstack *stack, size_t *psize, ppname *pname)
-{
-  ppobj *obj;
-  ppstack_clear(stack);
-  do {
-    if (ppscan_find(I) < 0)
-      return NULL;
-    if ((obj = ppscan_psobj(I, stack)) == NULL)
-      return NULL;
-  } while (obj->type != PPNAME || !ppname_exec(obj->name));
-  *pname = obj->name;
-  *psize = stack->size - 1;
-  return stack->buf;
-}
-
-ppobj * ppcontents_first_op (ppcontext *context, ppstream *stream, size_t *psize, ppname *pname)
-{
-  iof *I;
-  if ((I = ppstream_read(stream, 1, 0)) == NULL)
-    return NULL;
-  return ppcontents_op(I, &context->stack, psize, pname);
-}
-
-ppobj * ppcontents_next_op (ppcontext *context, ppstream *stream, size_t *psize, ppname *pname)
-{
-  return ppcontents_op(ppstream_iof(stream), &context->stack, psize, pname);
-}
-
-ppobj * ppcontents_parse (ppcontext *context, ppstream *stream, size_t *psize)
-{
-  iof *I;
-  ppstack *stack;
-  ppobj *obj;
-  stack = &context->stack;
-  ppstack_clear(stack);
-  if ((I = ppstream_read(stream, 1, 0)) == NULL)
-    return NULL;
-  while (ppscan_find(I) >= 0)
-    if ((obj = ppscan_psobj(I, stack)) == NULL)
-      goto error;
-  *psize = stack->size;
-  ppstream_done(stream);
-  return stack->buf;
-error:
-  ppstream_done(stream);
-  return NULL;
-}
-
-/* boxes */
-
-pprect * pparray_to_rect (pparray *array, pprect *rect)
-{
-  ppobj *obj;
-  if (array->size != 4)
-    return NULL;
-  obj = pparray_at(array, 0);
-  if (!ppobj_get_num(obj, rect->lx)) return NULL;
-  obj = pparray_at(array, 1);
-  if (!ppobj_get_num(obj, rect->ly)) return NULL;
-  obj = pparray_at(array, 2);
-  if (!ppobj_get_num(obj, rect->rx)) return NULL;
-  obj = pparray_at(array, 3);
-  if (!ppobj_get_num(obj, rect->ry)) return NULL;
-  return rect;
-}
-
-pprect * ppdict_get_rect (ppdict *dict, const char *name, pprect *rect)
-{
-  pparray *array;
-  return (array = ppdict_rget_array(dict, name)) != NULL ? pparray_to_rect(array, rect) : NULL;
-}
-
-pprect * ppdict_get_box (ppdict *dict, const char *name, pprect *rect)
-{
-  do {
-    if (ppdict_get_rect(dict, name, rect) != NULL)
-      return rect;
-    dict = ppdict_rget_dict(dict, "Parent");
-  } while (dict != NULL);
-  return NULL;
-}
-
-ppmatrix * pparray_to_matrix (pparray *array, ppmatrix *matrix)
-{
-  ppobj *obj;
-  if (array->size != 6)
-    return NULL;
-  obj = pparray_at(array, 0);
-  if (!ppobj_get_num(obj, matrix->xx)) return NULL;
-  obj = pparray_at(array, 1);
-  if (!ppobj_get_num(obj, matrix->xy)) return NULL;
-  obj = pparray_at(array, 2);
-  if (!ppobj_get_num(obj, matrix->yx)) return NULL;
-  obj = pparray_at(array, 3);
-  if (!ppobj_get_num(obj, matrix->yy)) return NULL;
-  obj = pparray_at(array, 4);
-  if (!ppobj_get_num(obj, matrix->x)) return NULL;
-  obj = pparray_at(array, 5);
-  if (!ppobj_get_num(obj, matrix->y)) return NULL;
-  return matrix;
-}
-
-ppmatrix * ppdict_get_matrix (ppdict *dict, const char *name, ppmatrix *matrix)
-{
-  pparray *array;
-  return (array = ppdict_rget_array(dict, name)) != NULL ? pparray_to_matrix(array, matrix) : NULL;
-}
-
-/* logger */
-
-void pplog_callback (pplogger_callback logger, void *alien)
-{
-	logger_callback((logger_function)logger, alien);
-}
-
-int pplog_prefix (const char *prefix)
-{
-	return logger_prefix(prefix);
-}
-
-/* version */
-
-const char * ppdoc_version_string (ppdoc *pdf)
-{
-  return pdf->version;
-}
-
-int ppdoc_version_number (ppdoc *pdf, int *minor)
-{
-  *minor = pdf->version[2] - '0';
-  return pdf->version[0] - '0';
-}
-
-/* doc info */
-
-size_t ppdoc_file_size (ppdoc *pdf)
-{
-  return pdf->filesize;
-}
-
-ppuint ppdoc_objects (ppdoc *pdf)
-{
-  ppuint count;
-  ppxref *xref;
-  for (count = 0, xref = pdf->xref; xref != NULL; xref = xref->prev)
-    count += xref->count;
-  return count;
-}
-
-size_t ppdoc_memory (ppdoc *pdf, size_t *waste)
-{
-  size_t used;
-  ppheap *heap;
-  used = 0, *waste = 0;
-  for (heap = pdf->heap; heap != NULL; heap = heap->prev)
-  {
-    used += heap->space;
-    *waste += heap->size;
-  }
-  return used;
-}

Modified: trunk/Build/source/texk/web2c/luatexdir/luatex.c
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/luatex.c	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/luatex.c	2019-11-08 09:36:30 UTC (rev 52692)
@@ -33,8 +33,8 @@
 */
 
 int luatex_version = 111;
-int luatex_revision = '1';
-const char *luatex_version_string = "1.11.1";
+int luatex_revision = '2';
+const char *luatex_version_string = "1.11.2";
 const char *engine_name = my_name;
 
 #include <kpathsea/c-ctype.h>

Modified: trunk/Build/source/texk/web2c/luatexdir/luatex_svnversion.h
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/luatex_svnversion.h	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/luatex_svnversion.h	2019-11-08 09:36:30 UTC (rev 52692)
@@ -1 +1 @@
-#define luatex_svn_revision 7215
+#define luatex_svn_revision 7223

Modified: trunk/Build/source/texk/web2c/luatexdir/tex/dumpdata.c
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/tex/dumpdata.c	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/tex/dumpdata.c	2019-11-08 09:36:30 UTC (rev 52692)
@@ -32,7 +32,7 @@
 
 */
 
-#define FORMAT_ID (907+51)
+#define FORMAT_ID (907+52)
 #if ((FORMAT_ID>=0) && (FORMAT_ID<=256))
 #error Wrong value for FORMAT_ID.
 #endif

Modified: trunk/Build/source/texk/web2c/luatexdir/tex/linebreak.c
===================================================================
--- trunk/Build/source/texk/web2c/luatexdir/tex/linebreak.c	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Build/source/texk/web2c/luatexdir/tex/linebreak.c	2019-11-08 09:36:30 UTC (rev 52692)
@@ -114,9 +114,9 @@
         tail_append(new_penalty(inf_penalty,line_penalty));
     } else {
         halfword t = alink(cur_list.tail_field);
-		flush_node(cur_list.tail_field);
-		cur_list.tail_field = t;
-		tail_append(new_penalty(inf_penalty,line_penalty));
+        flush_node(cur_list.tail_field);
+        cur_list.tail_field = t;
+        tail_append(new_penalty(inf_penalty,line_penalty));
     }
     final_par_glue = new_param_glue(par_fill_skip_code);
     couple_nodes(cur_list.tail_field, final_par_glue);
@@ -1811,8 +1811,16 @@
                 else
                     d += final_hyphen_demerits;
             }
-            if (abs(fit_class - fitness(r)) > 1)
-                d = d + adj_demerits;
+            /*tex 
+
+              Direct calculation of the absolute value of ((|fit_class| - |fitness(r)|) > 1) where 
+              |fitness(r)|  is an unsigned integral type can lead to unexpected results if 
+              |fitness(r)| is not an |int|,  due to the rules of integer promotions.
+              It's better to use the equivalent expanded expression.
+            */
+            if ( (fit_class>(fitness(r)+1)) || (fitness(r)>(fit_class+1)) )
+                  d = d + adj_demerits;
+
         }
         if (tracing_paragraphs > 0) {
             print_feasible_break(cur_p, r, b, pi, d, artificial_demerits);

Modified: trunk/Master/texmf-dist/doc/luatex/base/luatex-fonts.tex
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/base/luatex-fonts.tex	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Master/texmf-dist/doc/luatex/base/luatex-fonts.tex	2019-11-08 09:36:30 UTC (rev 52692)
@@ -43,6 +43,7 @@
                                                                         complete control to the loader. \NC \NR
 \NC \type{fullname}         \NC no  \NC no  \NC yes  \NC string     \NC output font name, used as a fallback in the \PDF\ output
                                                                         if the \type {psname} is not set \NC \NR
+\NC \type{subfont}          \NC no  \NC no  \NC yes  \NC number     \NC default: 0, index in (\type {ttc}) font with multiple fonts \NC \NR
 \NC \type{header}           \NC yes \NC no  \NC no   \NC string     \NC header comments, if any \NC \NR
 \NC \type{hyphenchar}       \NC no  \NC no  \NC yes  \NC number     \NC default: \TEX's \prm {hyphenchar} \NC \NR
 \NC \type{parameters}       \NC no  \NC yes \NC yes  \NC hash       \NC default: 7 parameters, all zero \NC \NR
@@ -105,6 +106,11 @@
 \type {-shrink} and \type {stretch}, with step \type {step}) of a font that was
 automatically generated by the font expansion algorithm.
 
+The \type {subfont} parameter can be used to specify the subfont in a \type {ttc}
+font. When given, it is used instead of the \type {psname} and \type {fullname}
+combination. The first subfont has number~1. A zero value signals using the names
+as lookup.
+
 Because we store the actual state of expansion with each glyph and don't have
 special font instances, we can change some font related parameters before lines
 are constructed, like:

Modified: trunk/Master/texmf-dist/doc/luatex/base/luatex-tex.tex
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/base/luatex-tex.tex	2019-11-08 00:55:40 UTC (rev 52691)
+++ trunk/Master/texmf-dist/doc/luatex/base/luatex-tex.tex	2019-11-08 09:36:30 UTC (rev 52692)
@@ -113,6 +113,18 @@
 
 \stopsubsection
 
+\startsubsection[title={Code page (Windows only)}]
+
+\libindex{getcodepage}
+
+The \type {getcodepage}  returns the value from  \type{GetOEMCP()} 
+(the current original equipment manufacturer (OEM) code page identifier for the operating system)
+and the value from \type{GetACP()} (the current Windows ANSI code page identifier for the operating system).
+
+\stopsubsection
+
+
+
 \stopsection
 
 \startsection[title={The \type {status} library}][library=status]

Modified: trunk/Master/texmf-dist/doc/luatex/base/luatex.pdf
===================================================================
(Binary files differ)



More information about the tex-live-commits mailing list