diff options
Diffstat (limited to 'Build/source/texk/xdvipdfmx/src/xbb.c')
-rw-r--r-- | Build/source/texk/xdvipdfmx/src/xbb.c | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/Build/source/texk/xdvipdfmx/src/xbb.c b/Build/source/texk/xdvipdfmx/src/xbb.c new file mode 100644 index 00000000000..cac27b3867b --- /dev/null +++ b/Build/source/texk/xdvipdfmx/src/xbb.c @@ -0,0 +1,509 @@ +/* $Header: /home/cvsroot/dvipdfmx/src/xbb.c,v 1.12 2008/05/18 14:31:06 matthias Exp $ + + This is extractbb, a bounding box extraction program. + + Copyright (C) 2008 by Jin-Hwan Cho <chofchof@ktug.or.kr> + + This program 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. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include <stdio.h> +#include <time.h> +#include <string.h> +#include "system.h" +#include "mem.h" +#include "mfileio.h" +#include "numbers.h" +#include "pdfobj.h" +#include "pdfparse.h" + +#include "config.h" + +#ifdef HAVE_LIBPNG +#include <png.h> +#endif + +#include "xbb.h" + +#define XBB_PROGRAM "extractbb" +#define XBB_VERSION "Version 0.2" + +static void usage(void) +{ + fprintf (stderr, "%s, version %s, Copyright (C) 2008 by Jin-Hwan Cho\n", + XBB_PROGRAM, XBB_VERSION); + fprintf (stderr, "A bounding box extraction utility from PDF, PNG, and JPG.\n"); + fprintf (stdout, "\nThis is free software; you can redistribute it and/or modify\n"); + fprintf (stdout, "it under the terms of the GNU General Public License as published by\n"); + fprintf (stdout, "the Free Software Foundation; either version 2 of the License, or\n"); + fprintf (stdout, "(at your option) any later version.\n"); + fprintf (stderr, "\nUsage: %s [-v] [-b] [-m|-x] [files]\n", XBB_PROGRAM); + fprintf (stderr, "\t-b\tWrite output file in binary mode\n"); + fprintf (stderr, "\t-m\tOutput .bb file used in DVIPDFM\n"); + fprintf (stderr, "\t-x\tOutput .xbb file used in DVIPDFMx (default)\n"); + fprintf (stderr, "\t-v\tVerbose\n"); + exit(1); +} + +static char verbose = 0; + +static void do_time(FILE *file) +{ + time_t current_time; + struct tm *bd_time; + + time(¤t_time); + bd_time = localtime(¤t_time); + fprintf(file, "%%%%CreationDate: %s\n", asctime(bd_time)); +} + +char *extensions[] = { + ".jpeg", ".JPEG", ".jpg", ".JPG", ".pdf", ".PDF", ".png", ".PNG" +}; + +static int xbb_output_mode = XBB_OUTPUT; + +static char *make_xbb_filename(const char *name) +{ + int i; + char *result; + + for (i = 0; i < sizeof(extensions) / sizeof(extensions[0]); i++) { + if (strlen(extensions[i]) < strlen(name) && + strncmp(name+strlen(name)-strlen(extensions[i]), extensions[i], strlen(extensions[i])) == 0) + break; + } + if (i == sizeof(extensions) / sizeof(extensions[0])) { + fprintf(stderr, "Warning: %s: Filename does not end in a recognizeable extension.\n", name); + result = NEW(strlen(name)+3, char); + strcpy(result, name); + } else { /* Remove extension */ + result = NEW(strlen(name)+3-strlen(extensions[i])+1, char); + strncpy(result, name, strlen(name)-strlen(extensions[i])); + result[strlen(name)-strlen(extensions[i])] = 0; + } + strcat(result, (xbb_output_mode == XBB_OUTPUT ? ".xbb" : ".bb")); + return result; +} + +static char *xbb_file_mode = FOPEN_W_MODE; + +static void write_xbb(char *fname, int bbllx, int bblly, int bburx, int bbury) +{ + char *outname; + FILE *fp; + + outname = make_xbb_filename(fname); + if ((fp = MFOPEN(outname, xbb_file_mode)) == NULL) { + fprintf(stderr, "Unable to open output file: %s\n", outname); + RELEASE(outname); + return; + } + if (verbose) { + fprintf(stderr, "Writing to %s: ", outname); + fprintf(stderr, "Bounding box: %d %d %d %d\n", bbllx, bblly, bburx, bbury); + } + fprintf(fp,"%%%%Title: %s\n", fname); + fprintf(fp,"%%%%Creator: %s %s\n", XBB_PROGRAM, XBB_VERSION); + fprintf(fp,"%%%%BoundingBox: %d %d %d %d\n", bbllx, bblly, bburx, bbury); + do_time(fp); + RELEASE(outname); + MFCLOSE(fp); +} + +typedef enum { + JM_SOF0 = 0xc0, JM_SOF1 = 0xc1, JM_SOF2 = 0xc2, JM_SOF3 = 0xc3, + JM_SOF5 = 0xc5, JM_DHT = 0xc4, JM_SOF6 = 0xc6, JM_SOF7 = 0xc7, + JM_SOF9 = 0xc9, JM_SOF10 = 0xca, JM_SOF11 = 0xcb, JM_DAC = 0xcc, + JM_SOF13 = 0xcd, JM_SOF14 = 0xce, JM_SOF15 = 0xcf, + + JM_RST0 = 0xd0, JM_RST1 = 0xd1, JM_RST2 = 0xd2, JM_RST3 = 0xd3, + JM_RST4 = 0xd4, JM_RST5 = 0xd5, JM_RST6 = 0xd6, JM_RST7 = 0xd7, + + JM_SOI = 0xd8, JM_EOI = 0xd9, JM_SOS = 0xda, JM_DQT = 0xdb, + JM_DNL = 0xdc, JM_DRI = 0xdd, JM_DHP = 0xde, JM_EXP = 0xdf, + + JM_APP0 = 0xe0, JM_APP2 = 0xe2, JM_APP14 = 0xee, JM_APP15 = 0xef, + + JM_COM = 0xfe +} JPEG_marker; + +static JPEG_marker JPEG_get_marker (FILE *fp) +{ + int c = fgetc(fp); + if (c != 255) return -1; + for (;;) { + c = fgetc(fp); + if (c < 0) return -1; + else if (c > 0 && c < 255) return c; + } + return -1; +} + +static int check_for_jpeg (FILE *fp) +{ + unsigned char jpeg_sig[2]; + rewind(fp); + if (fread(jpeg_sig, sizeof(unsigned char), 2, fp) != 2) return 0; + else if (jpeg_sig[0] != 0xff || jpeg_sig[1] != JM_SOI) return 0; + return 1; +} + +static int jpeg_get_info (FILE *fp, int *width, int *height) +{ + JPEG_marker marker; + unsigned short length; + int count; + float xdensity = 1.0, ydensity = 1.0; + char app_sig[128]; + + if (!check_for_jpeg(fp)) { + rewind(fp); + return -1; + } + rewind(fp); + count = 0; + while ((marker = JPEG_get_marker(fp)) >= 0) { + if (marker == JM_SOI || (marker >= JM_RST0 && marker <= JM_RST7)) { + count++; continue; + } + length = get_unsigned_pair(fp) - 2; + switch (marker) { + case JM_SOF0: case JM_SOF1: case JM_SOF2: case JM_SOF3: + case JM_SOF5: case JM_SOF6: case JM_SOF7: case JM_SOF9: + case JM_SOF10: case JM_SOF11: case JM_SOF13: case JM_SOF14: + case JM_SOF15: + get_unsigned_byte(fp); + if (xbb_output_mode != XBB_OUTPUT) { /* EBB_OUTPUT */ + xdensity = ydensity = 72.0 / 100.0; + } + *height = (int)(get_unsigned_pair(fp) * ydensity + 0.5); + *width = (int)(get_unsigned_pair(fp) * xdensity + 0.5); + return 0; + case JM_APP0: + if (length > 5) { + if (fread(app_sig, sizeof(char), 5, fp) != 5) return -1; + length -= 5; + if (!memcmp(app_sig, "JFIF\000", 5)) { + int units, xden, yden; + get_unsigned_pair(fp); + units = (int)get_unsigned_byte(fp); + xden = (int)get_unsigned_pair(fp); + yden = (int)get_unsigned_pair(fp); + switch (units) { + case 1: /* pixels per inch */ + xdensity = 72.0 / xden; + ydensity = 72.0 / yden; + break; + case 2: /* pixels per centimeter */ + xdensity = 72.0 / 2.54 / xden; + ydensity = 72.0 / 2.54 / yden; + break; + default: + break; + } + length -= 7; + } + } + seek_relative(fp, length); + break; + default: + seek_relative(fp, length); + break; + } + count++; + } + return -1; +} + +static void do_jpeg (FILE *fp, char *filename) +{ + int width, height; + + if (jpeg_get_info(fp, &width, &height) < 0) { + fprintf (stderr, "%s does not look like a JPEG file...\n", filename); + return; + } + write_xbb(filename, 0, 0, width, height); + return; +} + +#ifdef HAVE_LIBPNG +static int check_for_png (FILE *png_file) +{ + unsigned char sigbytes[4]; + rewind (png_file); + if (fread(sigbytes, 1, sizeof(sigbytes), png_file) != sizeof(sigbytes) || + (!png_check_sig (sigbytes, sizeof(sigbytes)))) return 0; + else return 1; +} + +static int png_get_info(FILE *png_file, int *width, int *height) +{ + png_structp png_ptr; + png_infop png_info_ptr; + png_uint_32 xppm, yppm; + + rewind(png_file); + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL || (png_info_ptr = png_create_info_struct(png_ptr)) == NULL) { + if (png_ptr) png_destroy_read_struct(&png_ptr, NULL, NULL); + return -1; + } + png_init_io(png_ptr, png_file); + png_read_info(png_ptr, png_info_ptr); + + *width = (int)png_get_image_width(png_ptr, png_info_ptr); + *height = (int)png_get_image_height(png_ptr, png_info_ptr); + + if (xbb_output_mode == XBB_OUTPUT) { + xppm = png_get_x_pixels_per_meter(png_ptr, png_info_ptr); + yppm = png_get_y_pixels_per_meter(png_ptr, png_info_ptr); + if (xppm > 0) + *width = (int)(*width * 72.0 / 0.0254 / xppm + 0.5); + if (yppm > 0) + *height = (int)(*height * 72.0 / 0.0254 / yppm + 0.5); + } else { /* EBB_OUTPUT */ + *width = (int)(*width * 72.0 / 100.0 + 0.5); + *height = (int)(*height * 72.0 / 100.0 + 0.5); + } + return 0; +} + +static void do_png (FILE *fp, char *filename) +{ + int width, height; + + if (png_get_info(fp, &width, &height) < 0) { + fprintf (stderr, "%s does not look like a PNG file...\n", filename); + return; + } + write_xbb(filename, 0, 0, width, height); + return; +} +#endif /* HAVE_LIBPNG */ + +static int rect_equal (pdf_obj *rect1, pdf_obj *rect2) +{ + int i; + if (!rect1 || !rect2) return 0; + for (i = 0; i < 4; i++) { + if (pdf_number_value(pdf_get_array(rect1, i)) != pdf_number_value(pdf_get_array(rect2, i))) return 0; + } + return 1; +} + +static int pdf_get_info (FILE *image_file, char *filename, int *llx, int *lly, int *urx, int *ury) +{ + pdf_obj *page_tree; + pdf_obj *bbox; + pdf_file *pf; + + page_tree = NULL; + { + pdf_obj *trailer, *catalog, *tmp; + + pf = pdf_open(filename, image_file); + if (!pf) + return -1; + + trailer = pdf_file_get_trailer(pf); + + if (pdf_lookup_dict(trailer, "Encrypt")) { + WARN("This PDF document is encrypted."); + pdf_release_obj(trailer); + pdf_close(pf); + return -1; + } + catalog = pdf_deref_obj(pdf_lookup_dict(trailer, "Root")); + if (!catalog) { + WARN("Catalog isn't where I expect it."); + pdf_close(pf); + return -1; + } + pdf_release_obj(trailer); + page_tree = pdf_deref_obj(pdf_lookup_dict(catalog, "Pages")); + pdf_release_obj(catalog); + } + if (!page_tree) { + WARN("Page tree not found."); + pdf_close(pf); + return -1; + } + { + pdf_obj *kids_ref, *kids; + pdf_obj *crop_box; + pdf_obj *tmp; + + tmp = pdf_lookup_dict(page_tree, "MediaBox"); + bbox = tmp ? pdf_deref_obj(tmp) : NULL; + tmp = pdf_lookup_dict(page_tree, "CropBox"); + crop_box = tmp ? pdf_deref_obj(tmp) : NULL; + + while ((kids_ref = pdf_lookup_dict(page_tree, "Kids")) != NULL) { + kids = pdf_deref_obj(kids_ref); + pdf_release_obj(page_tree); + page_tree = pdf_deref_obj(pdf_get_array(kids, 0)); + pdf_release_obj(kids); + + if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "MediaBox")))) { + if (bbox) + pdf_release_obj(bbox); + bbox = tmp; + } + if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "BleedBox")))) { + if (!rect_equal(tmp, bbox)) { + if (bbox) + pdf_release_obj(bbox); + bbox = tmp; + } else + pdf_release_obj(tmp); + } + if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "TrimBox")))) { + if (!rect_equal(tmp, bbox)) { + if (bbox) + pdf_release_obj(bbox); + bbox = tmp; + } else + pdf_release_obj(tmp); + } + if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "ArtBox")))) { + if (!rect_equal(tmp, bbox)) { + if (bbox) + pdf_release_obj(bbox); + bbox = tmp; + } else + pdf_release_obj(tmp); + } + if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "CropBox")))) { + if (crop_box) + pdf_release_obj(crop_box); + crop_box = tmp; + } + } + if (crop_box) { + pdf_release_obj(bbox); + bbox = crop_box; + } + } + pdf_release_obj(page_tree); + + if (!bbox) { + WARN("No BoundingBox information available."); + pdf_close(pf); + return -1; + } + + *llx = (int)pdf_number_value(pdf_get_array(bbox, 0)); + *lly = (int)pdf_number_value(pdf_get_array(bbox, 1)); + *urx = (int)pdf_number_value(pdf_get_array(bbox, 2)); + *ury = (int)pdf_number_value(pdf_get_array(bbox, 3)); + + pdf_release_obj(bbox); + + pdf_close(pf); + return 0; +} + +static void do_pdf (FILE *fp, char *filename) +{ + int llx, lly, urx, ury; + + if (pdf_get_info(fp, filename, &llx, &lly, &urx, &ury) < 0) { + fprintf (stderr, "%s does not look like a PDF file...\n", filename); + return; + } + write_xbb(filename, llx, lly, urx, ury); + return; +} + +int extractbb (int argc, char *argv[], int mode) +{ + mem_debug_init(); + + xbb_output_mode = mode; + + pdf_files_init(); + + pdf_set_version(5); + + kpse_set_program_name(argv[0], NULL); + + argc -= 1; argv += 1; + if (argc == 0) + usage(); + + while (argc > 0 && *argv[0] == '-') { + switch (*(argv[0]+1)) { + case 'b': + xbb_file_mode = FOPEN_WBIN_MODE; + argc -= 1; argv += 1; + break; + case 'm': + xbb_output_mode = EBB_OUTPUT; + argc -= 1; argv += 1; + break; + case 'x': + xbb_output_mode = XBB_OUTPUT; + argc -= 1; argv += 1; + break; + case 'v': + verbose = 1; + argc -= 1; argv += 1; + break; + case 'h': + usage(); + argc -= 1; argv += 1; + break; + default: + usage(); + } + } + for (; argc > 0; argc--, argv++) { + FILE *infile; + char *kpse_file_name; + if (!(kpse_file_name = kpse_find_pict(argv[0])) || + (infile = MFOPEN(kpse_file_name, FOPEN_RBIN_MODE)) == NULL) { + fprintf(stderr, "Can't find file (%s)...skipping\n", argv[0]); + goto cont; + } + if (check_for_jpeg(infile)) { + do_jpeg(infile, kpse_file_name); + goto cont; + } + if (check_for_pdf(infile)) { + do_pdf(infile, kpse_file_name); + goto cont; + } +#ifdef HAVE_LIBPNG + if (check_for_png(infile)) { + do_png(infile, kpse_file_name); + goto cont; + } +#endif /* HAVE_LIBPNG */ + fprintf(stderr, "Can't handle file type for file named %s\n", argv[0]); + cont: + if (kpse_file_name) + RELEASE(kpse_file_name); + if (infile) + MFCLOSE(infile); + } + + pdf_files_close(); + + mem_debug_check(); + + return 0; +} |