% writejp2.w % Copyright 2011 Taco Hoekwater % Copyright 2011 Hartmut Henkel % 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 . @ @c static const char _svn_version[] = "$Id: writejp2.w 4133 2011-04-11 16:54:11Z oneiros $ " "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.70.1/source/texk/web2c/luatexdir/image/writejp2.w $"; @ Basic JPEG~2000 image support. Section and Table references below: Information technology --- JPEG~2000 image coding system: Core coding system. ISO/IEC 15444-1, Second edition, 2004-09-15, file |15444-1annexi.pdf|. @c #include #include #include "ptexlib.h" #include "image/image.h" #include "image/writejp2.h" #include "image/writejbig2.h" /* read2bytes(), read4bytes() */ /* Table 1.2 -- Defined boxes */ #define BOX_JP 0x6A502020 #define BOX_FTYP 0x66747970 #define BOX_JP2H 0x6a703268 #define BOX_IHDR 0x69686472 #define BOX_BPCC 0x62706363 #define BOX_COLR 0x636D6170 #define BOX_CDEF 0x63646566 #define BOX_RES 0x72657320 #define BOX_RESC 0x72657363 #define BOX_RESD 0x72657364 #define BOX_JP2C 0x6A703263 /* 1.4 Box definition */ typedef struct { unsigned long long lbox; unsigned int tbox; } hdr_struct; static unsigned long long read8bytes(FILE * f) { unsigned long long l = read4bytes(f); l = (l << 32) + read4bytes(f); return l; } static hdr_struct read_boxhdr(image_dict * idict) { hdr_struct hdr; hdr.lbox = read4bytes(img_file(idict)); hdr.tbox = read4bytes(img_file(idict)); if (hdr.lbox == 1) hdr.lbox = read8bytes(img_file(idict)); if (hdr.lbox == 0 && hdr.tbox != BOX_JP2C) pdftex_fail("reading JP2 image failed (LBox == 0)"); return hdr; } /* 1.5.3.1 Image Header box */ static void scan_ihdr(image_dict * idict) { unsigned int height, width, nc; unsigned char bpc, c, unkc, ipr; height = read4bytes(img_file(idict)); width = read4bytes(img_file(idict)); img_ysize(idict) = (int) height; img_xsize(idict) = (int) width; nc = read2bytes(img_file(idict)); bpc = (unsigned char) xgetc(img_file(idict)); img_colordepth(idict) = bpc + 1; c = (unsigned char) xgetc(img_file(idict)); unkc = (unsigned char) xgetc(img_file(idict)); ipr = (unsigned char) xgetc(img_file(idict)); } /* 1.5.3.7.1 Capture Resolution box */ /* 1.5.3.7.2 Default Display Resolution box */ static void scan_resc_resd(image_dict * idict) { unsigned int vr_n, vr_d, hr_n, hr_d; unsigned char vr_e, hr_e; double hr_, vr_; vr_n = read2bytes(img_file(idict)); vr_d = read2bytes(img_file(idict)); hr_n = read2bytes(img_file(idict)); hr_d = read2bytes(img_file(idict)); vr_e = (unsigned char) xgetc(img_file(idict)); hr_e = (unsigned char) xgetc(img_file(idict)); hr_ = ((double) hr_n / hr_d) * exp(hr_e * log(10.0)) * 0.0254; vr_ = ((double) vr_n / vr_d) * exp(vr_e * log(10.0)) * 0.0254; img_xres(idict) = (int) (hr_ + 0.5); img_yres(idict) = (int) (vr_ + 0.5); } /* 1.5.3.7 Resolution box (superbox) */ static void scan_res(image_dict * idict, unsigned long long epos_s) { hdr_struct hdr; unsigned long long spos, epos; epos = xftell(img_file(idict), img_filepath(idict)); while (1) { spos = epos; hdr = read_boxhdr(idict); epos = spos + hdr.lbox; switch (hdr.tbox) { case (BOX_RESC): /* arbitrarily: let BOX_RESD have precedence */ if (img_xres(idict) == 0 && img_yres(idict) == 0) { scan_resc_resd(idict); if (xftell(img_file(idict), img_filepath(idict)) != (long)epos) pdftex_fail ("reading JP2 image failed (resc box size inconsistent)"); } break; case (BOX_RESD): scan_resc_resd(idict); if (xftell(img_file(idict), img_filepath(idict)) != (long)epos) pdftex_fail ("reading JP2 image failed (resd box size inconsistent)"); break; default:; } if (epos > epos_s) pdftex_fail("reading JP2 image failed (res box size inconsistent)"); if (epos == epos_s) break; xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict)); } } /* 1.5.3 JP2 Header box (superbox) */ static boolean scan_jp2h(image_dict * idict, unsigned long long epos_s) { boolean ihdr_found = false; hdr_struct hdr; unsigned long long spos, epos; epos = xftell(img_file(idict), img_filepath(idict)); while (1) { spos = epos; hdr = read_boxhdr(idict); epos = spos + hdr.lbox; switch (hdr.tbox) { case (BOX_IHDR): scan_ihdr(idict); if (xftell(img_file(idict), img_filepath(idict)) != (long)epos) pdftex_fail ("reading JP2 image failed (ihdr box size inconsistent)"); ihdr_found = true; break; case (BOX_RES): scan_res(idict, epos); break; default:; } if (epos > epos_s) pdftex_fail ("reading JP2 image failed (jp2h box size inconsistent)"); if (epos == epos_s) break; xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict)); } return ihdr_found; } static void close_and_cleanup_jp2(image_dict * idict) { assert(idict != NULL); assert(img_file(idict) != NULL); assert(img_filepath(idict) != NULL); xfclose(img_file(idict), img_filepath(idict)); img_file(idict) = NULL; assert(img_jp2_ptr(idict) != NULL); xfree(img_jp2_ptr(idict)); } void read_jp2_info(image_dict * idict, img_readtype_e readtype) { boolean ihdr_found = false; hdr_struct hdr; unsigned long long spos, epos; assert(img_type(idict) == IMG_TYPE_JP2); img_totalpages(idict) = 1; img_pagenum(idict) = 1; img_xres(idict) = img_yres(idict) = 0; assert(img_file(idict) == NULL); img_file(idict) = xfopen(img_filepath(idict), FOPEN_RBIN_MODE); assert(img_jp2_ptr(idict) == NULL); img_jp2_ptr(idict) = xtalloc(1, jp2_img_struct); xfseek(img_file(idict), 0, SEEK_END, img_filepath(idict)); img_jp2_ptr(idict)->length = (int) xftell(img_file(idict), img_filepath(idict)); xfseek(img_file(idict), 0, SEEK_SET, img_filepath(idict)); assert(sizeof(unsigned long long) >= 8); spos = epos = 0; /* 1.5.1 JPEG 2000 Signature box */ hdr = read_boxhdr(idict); assert(hdr.tbox == BOX_JP); /* has already been checked */ epos = spos + hdr.lbox; xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict)); /* 1.5.2 File Type box */ spos = epos; hdr = read_boxhdr(idict); if (hdr.tbox != BOX_FTYP) pdftex_fail("reading JP2 image failed (missing ftyp box)"); epos = spos + hdr.lbox; xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict)); while (!ihdr_found) { spos = epos; hdr = read_boxhdr(idict); epos = spos + hdr.lbox; switch (hdr.tbox) { case BOX_JP2H: ihdr_found = scan_jp2h(idict, epos); break; case BOX_JP2C: if (!ihdr_found) pdftex_fail("reading JP2 image failed (no ihdr box found)"); break; default:; } xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict)); } if (readtype == IMG_CLOSEINBETWEEN) close_and_cleanup_jp2(idict); } static void reopen_jp2(image_dict * idict) { int width, height, xres, yres; width = img_xsize(idict); height = img_ysize(idict); xres = img_xres(idict); yres = img_yres(idict); read_jp2_info(idict, IMG_KEEPOPEN); if (width != img_xsize(idict) || height != img_ysize(idict) || xres != img_xres(idict) || yres != img_yres(idict)) pdftex_fail("writejp2: image dimensions have changed"); } void write_jp2(PDF pdf, image_dict * idict) { long unsigned l; FILE *f; assert(idict != NULL); if (img_file(idict) == NULL) reopen_jp2(idict); xfseek(img_file(idict), 0, SEEK_SET, img_filepath(idict)); assert(img_jp2_ptr(idict) != NULL); pdf_puts(pdf, "/Type /XObject\n/Subtype /Image\n"); if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) pdf_printf(pdf, "%s\n", img_attr(idict)); pdf_printf(pdf, "/Width %i\n/Height %i\n/Length %i\n", (int) img_xsize(idict), (int) img_ysize(idict), (int) img_jp2_ptr(idict)->length); pdf_puts(pdf, "/Filter /JPXDecode\n>>\nstream\n"); for (l = (long unsigned int) img_jp2_ptr(idict)->length, f = img_file(idict); l > 0; l--) pdf_out(pdf, xgetc(f)); pdf_end_stream(pdf); close_and_cleanup_jp2(idict); }