1% writejbig2.w
2%
3% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
4% Copyright 2006-2013 Taco Hoekwater <taco@@luatex.org>
5% Copyright 2003-2013 Hartmut Henkel <hartmut@@luatex.org>
6%
7% This file is part of LuaTeX.
8%
9% LuaTeX is free software; you can redistribute it and/or modify it under
10% the terms of the GNU General Public License as published by the Free
11% Software Foundation; either version 2 of the License, or (at your
12% option) any later version.
13%
14% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
15% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16% FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
17% License for more details.
18%
19% You should have received a copy of the GNU General Public License along
20% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
21
22@
23
24This is experimental JBIG2 image support to pdfTeX. JBIG2 image decoding
25is part of Adobe PDF-1.4, and requires Acroread 5.0 or later.
26
27References
28==========
29
30* 14492 FCD: Information technology -- coded representation of picture
31and audio information -- lossy/lossless coding of bi-level images /
32JBIG committee, 1999 July 16. This JBIG2 Working Draft is available from
33http://www.jpeg.org/public/fcd14492.pdf. The references in the C-code
34correspond to the sections of this document.
35
36* PDF Reference, 5th edition, version 1.6, 1985--2005 Adobe Systems
37Incorporated. Available online:
38
39http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf
40
41News
42====
43
4431 May 2006: no need to wait for |endoffileflag| in sequential access
45organization.
46
4710 May 2006: |ygetc()| for some catching of broken JBIG2 files; modify to
48accept Example 3.4 from PDFRef 5th ed. with short end-of-file segment.
49
5009 May 2006: |pages_maketree()| and |segments_maketree()| by AVL tree,
51some cleaning up.
52
5306 May 2006: File list replaced by AVL tree; |new_fileinfo()|,
54|new_pageinfo()|.
55
5604 May 2006: Updated for pdftex-1.40-beta-20060213.
57
5808 Jan. 2003: Added |flushjbig2page0objects()| function. Now at the end
59of the pdfTeX run all pending page0 objects are written out.
60
6108 Jan. 2003: Release on private webpage.
62
6304 Jan. 2003: Completely rewritten. Now with some data structures.
64Rudimentary local file and image bookkeeping. Multiple image inclusion
65from one JBIG2 file. Only required page0 segments are marked for
66inclusion.
67
6813 Nov. 2002: pdfcrypting removed.
69
7008 Dec. 2002: bug in page 0 stream writing repaired.
71Strategy for multiple page inclusion from same JBIG2 file: When writing
721st image, create fresh PDF object for page 0, and include any page
730 segments from complete file (even if these segments are not needed
74for image). When writing next image, check by filename comparison if
75PDF object for page 0 of this JBIG2 file has already been written. This
76can only remember the file name for the direct predecessor JBIG2 image
77(but images of other types might come inbetween). If such page 0 PDF
78object exists, reference it. Else create fresh one.
79
8009 Dec. 2002: JBIG2 seg. page numbers > 0 are now set to 1, see PDF Ref.
81
82@ @c
83
84
85#undef DEBUG
86
87#include "ptexlib.h"
88#include <stdlib.h>
89#include <stdio.h>
90#include <assert.h>
91#include "image/image.h"
92
93@ @c
94/* 7.3 Segment types */
95#define M_SymbolDictionary 0
96#define M_IntermediateTextRegion 4
97#define M_ImmediateTextRegion 6
98#define M_ImmediateLosslessTextRegion 7
99#define M_PatternDictionary 16
100#define M_IntermediateHalftoneRegion 20
101#define M_ImmediateHalftoneRegion 22
102#define M_ImmediateLosslessHalftoneRegion 23
103#define M_IntermediateGenericRegion 36
104#define M_ImmediateGenericRegion 38
105#define M_ImmediateLosslessGenericRegion 39
106#define M_IntermediateGenericRefinementRegion 40
107#define M_ImmediateGenericRefinementRegion 42
108#define M_ImmediateLosslessGenericRefinementRegion 43
109#define M_PageInformation 48
110#define M_EndOfPage 49
111#define M_EndOfStripe 50
112#define M_EndOfFile 51
113#define M_Profiles 52
114#define M_Tables 53
115#define M_Extension 62
116
117@ @c
118typedef enum { INITIAL, HAVEINFO, WRITEPDF } PHASE;
119
120typedef struct _LITEM {
121    struct _LITEM *prev;
122    struct _LITEM *next;
123    void *d;                    /* data */
124} LITEM;
125
126typedef struct _LIST {
127    LITEM *first;
128    LITEM *last;
129    struct avl_table *tree;
130} LIST;
131
132typedef struct _SEGINFO {
133    unsigned long segnum;
134    boolean isrefered;
135    boolean refers;
136    unsigned int seghdrflags;   /* set by readseghdr() */
137    boolean pageassocsizeflag;  /* set by readseghdr() */
138    unsigned int reftosegcount; /* set by readseghdr() */
139    unsigned int countofrefered;        /* set by readseghdr() */
140    unsigned int fieldlen;      /* set by readseghdr() */
141    unsigned int segnumwidth;   /* set by readseghdr() */
142    long segpage;               /* set by readseghdr() */
143    unsigned long segdatalen;   /* set by readseghdr() */
144    unsigned long hdrstart;     /* set by readseghdr() */
145    unsigned long hdrend;       /* set by readseghdr() */
146    unsigned long datastart;
147    unsigned long dataend;
148    boolean endofstripeflag;    /* set by checkseghdrflags() */
149    boolean endofpageflag;      /* set by checkseghdrflags() */
150    boolean pageinfoflag;       /* set by checkseghdrflags() */
151    boolean endoffileflag;      /* set by checkseghdrflags() */
152} SEGINFO;
153
154typedef struct _PAGEINFO {
155    LIST segments;              /* segments associated with page */
156    unsigned long pagenum;
157    unsigned int width;
158    unsigned int height;
159    unsigned int xres;
160    unsigned int yres;
161    unsigned int pagesegmentflags;
162    unsigned int stripinginfo;
163    unsigned int stripedheight;
164} PAGEINFO;
165
166typedef struct _FILEINFO {
167    FILE *file;
168    char *filepath;
169    long filesize;
170    LIST pages;                 /* not including page0 */
171    LIST page0;
172    unsigned int filehdrflags;  /* set by readfilehdr() */
173    boolean sequentialaccess;   /* set by readfilehdr() */
174    unsigned long numofpages;   /* set by readfilehdr() */
175    unsigned long streamstart;  /* set by |get_jbig2_info()| */
176    unsigned long pdfpage0objnum;
177    PHASE phase;
178} FILEINFO;
179
180@ @c
181static struct avl_table *file_tree = NULL;
182
183static int comp_file_entry(const void *pa, const void *pb, void *p)
184{
185    (void) p;
186    return strcmp(((const FILEINFO *) pa)->filepath,
187                  ((const FILEINFO *) pb)->filepath);
188}
189
190static int comp_page_entry(const void *pa, const void *pb, void *p)
191{
192    (void) p;
193    return (int) (((const PAGEINFO *) pa)->pagenum -
194                  ((const PAGEINFO *) pb)->pagenum);
195}
196
197static int comp_segment_entry(const void *pa, const void *pb, void *p)
198{
199    (void) p;
200    return (int) (((const SEGINFO *) pa)->segnum -
201                  ((const SEGINFO *) pb)->segnum);
202}
203
204@ @c
205static int ygetc(FILE * stream)
206{
207    int c = getc(stream);
208    if (c < 0) {
209        if (c == EOF)
210            luatex_fail("getc() failed; premature end of JBIG2 image file");
211        else
212            luatex_fail("getc() failed (can't happen)");
213    }
214    return c;
215}
216
217@ @c
218static void initlinkedlist(LIST * lp)
219{
220    lp->first = NULL;
221    lp->last = NULL;
222    lp->tree = NULL;
223}
224
225static LIST *litem_append(LIST * lp)
226{
227    LITEM *ip;
228    ip = xtalloc(1, LITEM);
229    if (lp->first == NULL) {
230        lp->first = ip;
231        ip->prev = NULL;
232    } else {
233        lp->last->next = ip;
234        ip->prev = lp->last;
235    }
236    lp->last = ip;
237    ip->next = NULL;
238    ip->d = NULL;
239    return lp;
240}
241
242@ @c
243static FILEINFO *new_fileinfo(void)
244{
245    FILEINFO *fip;
246    fip = xtalloc(1, FILEINFO);
247    fip->file = NULL;
248    fip->filepath = NULL;
249    fip->filesize = 0;
250    initlinkedlist(&(fip->pages));
251    initlinkedlist(&(fip->page0));
252    fip->filehdrflags = 0;
253    fip->sequentialaccess = false;
254    fip->numofpages = 0;
255    fip->streamstart = 0;
256    fip->pdfpage0objnum = 0;
257    fip->phase = INITIAL;
258    return fip;
259}
260
261@ @c
262static PAGEINFO *new_pageinfo(void)
263{
264    PAGEINFO *pip;
265    pip = xtalloc(1, PAGEINFO);
266    initlinkedlist(&(pip->segments));
267    pip->pagenum = 0;
268    pip->width = 0;
269    pip->height = 0;
270    pip->xres = 0;
271    pip->yres = 0;
272    pip->pagesegmentflags = 0;
273    pip->stripinginfo = 0;
274    pip->stripedheight = 0;
275    return pip;
276}
277
278@ @c
279static void init_seginfo(SEGINFO * sip)
280{
281    sip->segnum = 0;
282    sip->isrefered = false;
283    sip->refers = false;
284    sip->seghdrflags = 0;
285    sip->pageassocsizeflag = false;
286    sip->reftosegcount = 0;
287    sip->countofrefered = 0;
288    sip->fieldlen = 0;
289    sip->segnumwidth = 0;
290    sip->segpage = 0;
291    sip->segdatalen = 0;
292    sip->hdrstart = 0;
293    sip->hdrend = 0;
294    sip->datastart = 0;
295    sip->dataend = 0;
296    sip->endofstripeflag = false;
297    sip->endofpageflag = false;
298    sip->pageinfoflag = false;
299    sip->endoffileflag = false;
300}
301
302@ @c
303static void pages_maketree(LIST * plp)
304{
305    LITEM *ip;
306    void **aa;
307    assert(plp->tree == NULL);
308    plp->tree = avl_create(comp_page_entry, NULL, &avl_xallocator);
309    assert(plp->tree != NULL);
310    for (ip = plp->first; ip != NULL; ip = ip->next) {
311        aa = avl_probe(plp->tree, (PAGEINFO *) ip->d);
312        assert(aa != NULL);
313    }
314}
315
316@ @c
317static void segments_maketree(LIST * slp)
318{
319    LITEM *ip;
320    void **aa;
321    assert(slp->tree == NULL);
322    slp->tree = avl_create(comp_segment_entry, NULL, &avl_xallocator);
323    assert(slp->tree != NULL);
324    for (ip = slp->first; ip != NULL; ip = ip->next) {
325        aa = avl_probe(slp->tree, (SEGINFO *) ip->d);
326        assert(aa != NULL);
327    }
328}
329
330@ @c
331static PAGEINFO *find_pageinfo(LIST * plp, unsigned long pagenum)
332{
333    PAGEINFO tmp;
334    tmp.pagenum = pagenum;
335    assert(plp->tree != NULL);
336    return (PAGEINFO *) avl_find(plp->tree, &tmp);
337}
338
339@ @c
340static SEGINFO *find_seginfo(LIST * slp, unsigned long segnum)
341{
342    SEGINFO tmp;
343    tmp.segnum = segnum;
344    assert(slp->tree != NULL);
345    return (SEGINFO *) avl_find(slp->tree, &tmp);
346}
347
348@ @c
349unsigned int read2bytes(FILE * f)
350{
351    unsigned int c = (unsigned int) ygetc(f);
352    return (c << 8) + (unsigned int) ygetc(f);
353}
354
355@ @c
356unsigned int read4bytes(FILE * f)
357{
358    unsigned int l = read2bytes(f);
359    return (l << 16) + read2bytes(f);
360}
361
362@ @c
363static unsigned long getstreamlen(LITEM * slip, boolean refer)
364{
365    SEGINFO *sip;
366    unsigned long len = 0;
367    for (; slip != NULL; slip = slip->next) {
368        sip = slip->d;
369        if (refer || sip->isrefered)
370            len += sip->hdrend - sip->hdrstart + sip->dataend - sip->datastart;
371    }
372    return len;
373}
374
375@ @c
376static void readfilehdr(FILEINFO * fip)
377{
378    unsigned int i;
379    /* Annex D.4 File header syntax */
380    /* Annex D.4.1 ID string */
381    unsigned char jbig2_id[] = { 0x97, 'J', 'B', '2', 0x0d, 0x0a, 0x1a, 0x0a };
382    xfseek(fip->file, 0, SEEK_SET, fip->filepath);
383    for (i = 0; i < 8; i++)
384        if (ygetc(fip->file) != jbig2_id[i])
385            luatex_fail
386                ("readfilehdr(): reading JBIG2 image file failed: ID string missing");
387    /* Annex D.4.2 File header flags */
388    fip->filehdrflags = (unsigned int) ygetc(fip->file);
389    fip->sequentialaccess = (fip->filehdrflags & 0x01) ? true : false;
390    if (fip->sequentialaccess) {        /* Annex D.1 vs. Annex D.2 */
391        xfseek(fip->file, 0, SEEK_END, fip->filepath);
392        fip->filesize = (long) xftello(fip->file, fip->filepath);
393        xfseek(fip->file, 9, SEEK_SET, fip->filepath);
394    }
395    /* Annex D.4.3 Number of pages */
396    if (!(fip->filehdrflags >> 1) & 0x01)       /* known number of pages */
397        fip->numofpages = read4bytes(fip->file);
398    /* --- at end of file header --- */
399}
400
401@ @c
402static void checkseghdrflags(SEGINFO * sip)
403{
404    sip->endofstripeflag = false;
405    sip->endofpageflag = false;
406    sip->pageinfoflag = false;
407    sip->endoffileflag = false;
408    /* 7.3 Segment types */
409    switch (sip->seghdrflags & 0x3f) {
410    case M_SymbolDictionary:
411    case M_IntermediateTextRegion:
412    case M_ImmediateTextRegion:
413    case M_ImmediateLosslessTextRegion:
414    case M_PatternDictionary:
415    case M_IntermediateHalftoneRegion:
416    case M_ImmediateHalftoneRegion:
417    case M_ImmediateLosslessHalftoneRegion:
418    case M_IntermediateGenericRegion:
419    case M_ImmediateGenericRegion:
420    case M_ImmediateLosslessGenericRegion:
421    case M_IntermediateGenericRefinementRegion:
422    case M_ImmediateGenericRefinementRegion:
423    case M_ImmediateLosslessGenericRefinementRegion:
424        break;
425    case M_PageInformation:
426        sip->pageinfoflag = true;
427        break;
428    case M_EndOfPage:
429        sip->endofpageflag = true;
430        break;
431    case M_EndOfStripe:
432        sip->endofstripeflag = true;
433        break;
434    case M_EndOfFile:
435        sip->endoffileflag = true;
436        break;
437    case M_Profiles:
438    case M_Tables:
439    case M_Extension:
440        break;
441    default:
442        luatex_fail
443            ("checkseghdrflags(): unknown segment type in JBIG2 image file");
444        break;
445    }
446}
447
448@ for first reading of file; return value tells if header been read
449
450@c
451static boolean readseghdr(FILEINFO * fip, SEGINFO * sip)
452{
453    unsigned int i;
454    sip->hdrstart = xftell(fip->file, fip->filepath);
455    if (fip->sequentialaccess && sip->hdrstart == (unsigned) fip->filesize)
456        return false;           /* no endoffileflag is ok for sequentialaccess */
457#ifdef DEBUG
458    printf("\nhdrstart %d\n", sip->hdrstart);
459#endif
460    /* 7.2.2 Segment number */
461    sip->segnum = read4bytes(fip->file);
462#ifdef DEBUG
463    printf("  segnum %d\n", sip->segnum);
464#endif
465    /* 7.2.3 Segment header flags */
466    sip->seghdrflags = (unsigned int) ygetc(fip->file);
467#ifdef DEBUG
468    printf("  hdrflags %d\n", sip->seghdrflags & 0x3f);
469#endif
470    checkseghdrflags(sip);
471    if (fip->sequentialaccess && sip->endoffileflag)    /* accept shorter segment, */
472        return true;            /* makes it compliant with Example 3.4 of PDFRef. 5th ed. */
473    sip->pageassocsizeflag = ((sip->seghdrflags >> 6) & 0x01) ? true : false;
474    /* 7.2.4 Referred-to segment count and retention flags */
475    sip->reftosegcount = (unsigned int) ygetc(fip->file);
476    sip->countofrefered = sip->reftosegcount >> 5;
477    if (sip->countofrefered < 5)
478        sip->fieldlen = 1;
479    else {
480        sip->fieldlen = 5 + sip->countofrefered / 8;
481        xfseek(fip->file, sip->fieldlen - 1, SEEK_CUR, fip->filepath);
482    }
483    /* 7.2.5 Referred-to segment numbers */
484    if (sip->segnum <= 256)
485        sip->segnumwidth = 1;
486    else if (sip->segnum <= 65536)
487        sip->segnumwidth = 2;
488    else
489        sip->segnumwidth = 4;
490    for (i = 0; i < sip->countofrefered; i++) {
491        switch (sip->segnumwidth) {
492        case 1:
493            (void) ygetc(fip->file);
494            break;
495        case 2:
496            (void) read2bytes(fip->file);
497            break;
498        case 4:
499            (void) read4bytes(fip->file);
500            break;
501        }
502    }
503    /* 7.2.6 Segment page association */
504    if (sip->pageassocsizeflag)
505        sip->segpage = read4bytes(fip->file);
506    else
507        sip->segpage = ygetc(fip->file);
508    /* 7.2.7 Segment data length */
509    sip->segdatalen = read4bytes(fip->file);
510    sip->hdrend = (unsigned long) xftello(fip->file, fip->filepath);
511    /* ---- at end of segment header ---- */
512    return true;
513}
514
515@ @c
516static void checkseghdr(FILEINFO * fip, SEGINFO * sip);
517
518static void markpage0seg(FILEINFO * fip, unsigned long referedseg)
519{
520    PAGEINFO *pip;
521    SEGINFO *sip;
522    pip = fip->page0.first->d;
523    sip = find_seginfo(&(pip->segments), referedseg);
524    if (sip != NULL) {
525        if (!sip->refers && sip->countofrefered > 0)
526            checkseghdr(fip, sip);
527        sip->isrefered = true;
528    }
529}
530
531@ for writing, marks refered page0 segments, sets segpage > 0 to 1
532
533@c
534static void writeseghdr(PDF pdf, FILEINFO * fip, SEGINFO * sip)
535{
536    unsigned int i;
537    unsigned long referedseg = 0;
538    /* 7.2.2 Segment number */
539    /* 7.2.3 Segment header flags */
540    /* 7.2.4 Referred-to segment count and retention flags */
541    for (i = 0; i < 5 + sip->fieldlen; i++)
542        pdf_out(pdf, ygetc(fip->file));
543    /* 7.2.5 Referred-to segment numbers */
544    for (i = 0; i < sip->countofrefered; i++) {
545        switch (sip->segnumwidth) {
546        case 1:
547            referedseg = (unsigned long) ygetc(fip->file);
548            pdf_out(pdf, referedseg);
549            break;
550        case 2:
551            referedseg = read2bytes(fip->file);
552            pdf_out(pdf, (referedseg >> 8) & 0xff);
553            pdf_out(pdf, referedseg & 0xff);
554            break;
555        case 4:
556            referedseg = read4bytes(fip->file);
557            pdf_out(pdf, (referedseg >> 24) & 0xff);
558            pdf_out(pdf, (referedseg >> 16) & 0xff);
559            pdf_out(pdf, (referedseg >> 8) & 0xff);
560            pdf_out(pdf, referedseg & 0xff);
561            break;
562        }
563        if (fip->page0.last != NULL && !sip->refers)
564            markpage0seg(fip, referedseg);
565    }
566    if (sip->countofrefered > 0)
567        sip->refers = true;
568    /* 7.2.6 Segment page association */
569    if (sip->pageassocsizeflag)
570        for (i = 0; i < 3; i++) {
571            (void) ygetc(fip->file);
572            pdf_out(pdf, 0);
573        }
574    (void) ygetc(fip->file);
575    pdf_out(pdf, (unsigned char) ((sip->segpage > 0) ? 1 : 0));
576    /* 7.2.7 Segment data length */
577    for (i = 0; i < 4; i++)
578        pdf_out(pdf, ygetc(fip->file));
579    /* ---- at end of segment header ---- */
580}
581
582@ for recursive marking of refered page0 segments
583@c
584static void checkseghdr(FILEINFO * fip, SEGINFO * sip)
585{
586    unsigned int i;
587    unsigned long referedseg = 0;
588    /* 7.2.2 Segment number */
589    /* 7.2.3 Segment header flags */
590    /* 7.2.4 Referred-to segment count and retention flags */
591    xfseek(fip->file, 5 + sip->fieldlen, SEEK_CUR, fip->filepath);
592    /* 7.2.5 Referred-to segment numbers */
593    for (i = 0; i < sip->countofrefered; i++) {
594        switch (sip->segnumwidth) {
595        case 1:
596            referedseg = (unsigned long) ygetc(fip->file);
597            break;
598        case 2:
599            referedseg = read2bytes(fip->file);
600            break;
601        case 4:
602            referedseg = read4bytes(fip->file);
603            break;
604        }
605        if (!sip->refers)
606            markpage0seg(fip, referedseg);
607    }
608    if (sip->countofrefered > 0)
609        sip->refers = true;
610    /* 7.2.6 Segment page association */
611    /* 7.2.7 Segment data length */
612    if (sip->pageassocsizeflag)
613        xfseek(fip->file, 8, SEEK_CUR, fip->filepath);
614    else
615        xfseek(fip->file, 5, SEEK_CUR, fip->filepath);
616    /* ---- at end of segment header ---- */
617}
618
619@ @c
620static unsigned long findstreamstart(FILEINFO * fip)
621{
622    SEGINFO tmp;
623    assert(!fip->sequentialaccess);     /* D.2 Random-access organisation */
624    do                          /* find random-access stream start */
625        (void) readseghdr(fip, &tmp);
626    while (!tmp.endoffileflag);
627    fip->streamstart = tmp.hdrend;
628    readfilehdr(fip);
629    return fip->streamstart;
630}
631
632@ @c
633static void rd_jbig2_info(FILEINFO * fip)
634{
635    unsigned long seekdist = 0; /* for sequential-access only */
636    unsigned long streampos = 0;        /* for random-access only */
637    unsigned long currentpage = 0;
638    boolean sipavail = false;
639    PAGEINFO *pip;
640    SEGINFO *sip = NULL;
641    LIST *plp, *slp;
642    fip->file = xfopen(fip->filepath, FOPEN_RBIN_MODE);
643    readfilehdr(fip);
644    if (!fip->sequentialaccess) /* D.2 Random-access organisation */
645        streampos = findstreamstart(fip);
646    while (true) {              /* loop over segments */
647        if (!sipavail) {
648            sip = xtalloc(1, SEGINFO);
649            sipavail = true;
650        }
651        init_seginfo(sip);
652        if (!readseghdr(fip, sip) || sip->endoffileflag)
653            break;
654        if (sip->segpage > 0) {
655            if (sip->segpage > (int) currentpage) {
656                plp = litem_append(&(fip->pages));
657                plp->last->d = new_pageinfo();
658                currentpage = (unsigned long) sip->segpage;
659            }
660            pip = fip->pages.last->d;
661        } else {
662            if (fip->page0.last == NULL) {
663                plp = litem_append(&(fip->page0));
664                plp->last->d = new_pageinfo();
665            }
666            pip = fip->page0.last->d;
667        }
668        if (!sip->endofpageflag) {
669            slp = litem_append(&(pip->segments));
670            slp->last->d = sip;
671            sipavail = false;
672        }
673        if (!fip->sequentialaccess)
674            sip->datastart = streampos;
675        else
676            sip->datastart = sip->hdrend;
677        sip->dataend = sip->datastart + sip->segdatalen;
678        if (!fip->sequentialaccess
679            && (sip->pageinfoflag || sip->endofstripeflag))
680            xfseeko(fip->file, (off_t) sip->datastart, SEEK_SET, fip->filepath);
681        seekdist = sip->segdatalen;
682        /* 7.4.8 Page information segment syntax */
683        if (sip->pageinfoflag) {
684            pip->pagenum = (unsigned long) sip->segpage;
685            pip->width = read4bytes(fip->file);
686            pip->height = read4bytes(fip->file);
687            pip->xres = read4bytes(fip->file);
688            pip->yres = read4bytes(fip->file);
689            pip->pagesegmentflags = (unsigned) ygetc(fip->file);
690            /* 7.4.8.6 Page striping information */
691            pip->stripinginfo = read2bytes(fip->file);
692            seekdist -= 19;
693        }
694        if (sip->endofstripeflag) {
695            pip->stripedheight = read4bytes(fip->file);
696            seekdist -= 4;
697        }
698        if (!fip->sequentialaccess
699            && (sip->pageinfoflag || sip->endofstripeflag))
700            xfseeko(fip->file, (off_t) sip->hdrend, SEEK_SET, fip->filepath);
701        if (!fip->sequentialaccess)
702            streampos += sip->segdatalen;
703        if (fip->sequentialaccess)
704            xfseeko(fip->file, (off_t) seekdist, SEEK_CUR, fip->filepath);
705        if (sip->endofpageflag && currentpage && (pip->stripinginfo >> 15))
706            pip->height = pip->stripedheight;
707    }
708    fip->phase = HAVEINFO;
709    if (sipavail)
710        xfree(sip);
711    xfclose(fip->file, fip->filepath);
712}
713
714@ @c
715static void wr_jbig2(PDF pdf, image_dict * idict, FILEINFO * fip,
716                     unsigned long page)
717{
718    LITEM *slip;
719    PAGEINFO *pip;
720    SEGINFO *sip;
721    unsigned long i;
722    if (page > 0) {
723        assert(idict != NULL);
724        pip = find_pageinfo(&(fip->pages), page);
725        assert(pip != NULL);
726        pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER);
727        pdf_begin_dict(pdf);
728        pdf_dict_add_name(pdf, "Type", "XObject");
729        pdf_dict_add_name(pdf, "Subtype", "Image");
730        pdf_dict_add_img_filename(pdf, idict);
731        pdf_dict_add_int(pdf, "Width", pip->width);
732        pdf_dict_add_int(pdf, "Height", pip->height);
733        pdf_dict_add_name(pdf, "ColorSpace", "DeviceGray");
734        pdf_dict_add_int(pdf, "BitsPerComponent", 1);
735        pdf_dict_add_int(pdf, "Length",
736                         getstreamlen(pip->segments.first, true));
737        pdf_add_name(pdf, "Filter");
738        pdf_begin_array(pdf);
739        pdf_add_name(pdf, "JBIG2Decode");
740        pdf_end_array(pdf);
741        if (fip->page0.last != NULL) {
742            if (fip->pdfpage0objnum == 0) {
743                fip->pdfpage0objnum =
744                    (unsigned long) pdf_create_obj(pdf, obj_type_others, 0);
745            }
746            pdf_add_name(pdf, "DecodeParms");
747            pdf_begin_array(pdf);
748            pdf_begin_dict(pdf);
749            pdf_dict_add_ref(pdf, "JBIG2Globals", fip->pdfpage0objnum);
750            pdf_end_dict(pdf);
751            pdf_end_array(pdf);
752        }
753        pdf_end_dict(pdf);
754    } else {
755        assert(idict == NULL);
756        pip = find_pageinfo(&(fip->page0), page);
757        assert(pip != NULL);
758        pdf_begin_obj(pdf, (int) fip->pdfpage0objnum, OBJSTM_NEVER);
759        pdf_begin_dict(pdf);
760        pdf_dict_add_int(pdf, "Length",
761                         getstreamlen(pip->segments.first, false));
762        pdf_end_dict(pdf);
763    }
764    pdf_begin_stream(pdf);
765    fip->file = xfopen(fip->filepath, FOPEN_RBIN_MODE);
766    for (slip = pip->segments.first; slip != NULL; slip = slip->next) { /* loop over page segments */
767        sip = slip->d;
768        if (sip->isrefered || page > 0) {
769            xfseeko(fip->file, (off_t) sip->hdrstart, SEEK_SET, fip->filepath);
770            /* mark refered-to page 0 segments, change segpages > 1 to 1 */
771            writeseghdr(pdf, fip, sip);
772            xfseeko(fip->file, (off_t) sip->datastart, SEEK_SET, fip->filepath);
773            for (i = sip->datastart; i < sip->dataend; i++)
774                pdf_out(pdf, ygetc(fip->file));
775        }
776    }
777    pdf_end_stream(pdf);
778    pdf_end_obj(pdf);
779    xfclose(fip->file, fip->filepath);
780}
781
782@ @c
783void read_jbig2_info(image_dict * idict)
784{
785    FILEINFO *fip, tmp;
786    PAGEINFO *pip;
787    void **aa;
788    assert(idict != NULL);
789    img_type(idict) = IMG_TYPE_JBIG2;
790    if (img_pagenum(idict) < 1)
791        luatex_fail
792            ("read_jbig2_info(): page %d not in JBIG2 image file; page must be > 0",
793             (int) img_pagenum(idict));
794    if (file_tree == NULL) {
795        file_tree = avl_create(comp_file_entry, NULL, &avl_xallocator);
796        assert(file_tree != NULL);
797    }
798    tmp.filepath = img_filepath(idict);
799    fip = (FILEINFO *) avl_find(file_tree, &tmp);
800    if (fip == NULL) {
801        fip = new_fileinfo();
802        fip->filepath = xstrdup(img_filepath(idict));
803        aa = avl_probe(file_tree, fip);
804        assert(aa != NULL);
805    }
806    if (fip->phase == INITIAL) {
807        rd_jbig2_info(fip);
808        pages_maketree(&(fip->pages));
809        if (fip->page0.last != NULL) {
810            pages_maketree(&(fip->page0));
811            pip = fip->page0.first->d;
812            segments_maketree(&(pip->segments));
813        }
814    }
815    pip = find_pageinfo(&(fip->pages), (unsigned long) img_pagenum(idict));
816    if (pip == NULL)
817        luatex_fail("read_jbig2_info(): page %d not found in JBIG2 image file",
818                    (int) img_pagenum(idict));
819    img_totalpages(idict) = (int) fip->numofpages;
820    img_xsize(idict) = (int) pip->width;
821    img_ysize(idict) = (int) pip->height;
822    img_xres(idict) = (int) (pip->xres * 0.0254 + 0.5);
823    img_yres(idict) = (int) (pip->yres * 0.0254 + 0.5);
824    img_colordepth(idict) = 1;
825}
826
827@ @c
828void write_jbig2(PDF pdf, image_dict * idict)
829{
830    FILEINFO *fip, tmp;
831    PAGEINFO *pip;
832    assert(idict != NULL);
833    assert(file_tree != NULL);
834    tmp.filepath = img_filepath(idict);
835    fip = (FILEINFO *) avl_find(file_tree, &tmp);
836    assert(fip != NULL);
837    assert(fip->phase == HAVEINFO);     /* don't write before |rd_jbig2_info()| call */
838    pip = find_pageinfo(&(fip->pages), (unsigned long) img_pagenum(idict));
839    assert(pip != NULL);
840    wr_jbig2(pdf, idict, fip, pip->pagenum);
841    img_file(idict) = NULL;
842}
843
844@ @c
845void flush_jbig2_page0_objects(PDF pdf)
846{
847    FILEINFO *fip;
848    struct avl_traverser t;
849    if (file_tree != NULL) {
850        avl_t_init(&t, file_tree);
851        for (fip = avl_t_first(&t, file_tree); fip != NULL;
852             fip = avl_t_next(&t)) {
853            if (fip->page0.last != NULL)
854                wr_jbig2(pdf, NULL, fip, 0);    /* NULL: page0 */
855        }
856    }
857}
858