From 38d0278ffd22928cfcc66516a5f5a75f2480e702 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Tue, 12 Apr 2016 15:54:22 +0200 Subject: Add 'mutool convert' and new document writer interface. Only supports CBZ writing for now. Also add a zip file writer. --- Makefile | 2 +- include/mupdf/fitz.h | 1 + include/mupdf/fitz/buffer.h | 2 + include/mupdf/fitz/output.h | 10 ++++ include/mupdf/fitz/unzip.h | 6 ++ include/mupdf/fitz/writer.h | 28 +++++++++ source/fitz/buffer.c | 14 +++++ source/fitz/output-cbz.c | 94 +++++++++++++++++++++++++++++ source/fitz/writer.c | 38 ++++++++++++ source/fitz/zip.c | 112 +++++++++++++++++++++++++++++++++++ source/tools/muconvert.c | 141 ++++++++++++++++++++++++++++++++++++++++++++ source/tools/mutool.c | 3 +- 12 files changed, 449 insertions(+), 2 deletions(-) create mode 100644 include/mupdf/fitz/writer.h create mode 100644 source/fitz/output-cbz.c create mode 100644 source/fitz/writer.c create mode 100644 source/fitz/zip.c create mode 100644 source/tools/muconvert.c diff --git a/Makefile b/Makefile index 074ef2eb..64726a9f 100644 --- a/Makefile +++ b/Makefile @@ -255,7 +255,7 @@ $(OUT)/cmapdump.o : include/mupdf/pdf/cmap.h source/pdf/pdf-cmap.c source/pdf/pd # --- Tools and Apps --- MUTOOL := $(addprefix $(OUT)/, mutool) -MUTOOL_OBJ := $(addprefix $(OUT)/tools/, mutool.o mudraw.o murun.o pdfclean.o pdfcreate.o pdfextract.o pdfinfo.o pdfposter.o pdfshow.o pdfpages.o pdfmerge.o) +MUTOOL_OBJ := $(addprefix $(OUT)/tools/, mutool.o muconvert.o mudraw.o murun.o pdfclean.o pdfcreate.o pdfextract.o pdfinfo.o pdfmerge.o pdfposter.o pdfpages.o pdfshow.o) $(MUTOOL_OBJ): $(FITZ_HDR) $(PDF_HDR) $(MUTOOL) : $(MUPDF_LIB) $(THIRD_LIB) $(MUTOOL) : $(MUTOOL_OBJ) diff --git a/include/mupdf/fitz.h b/include/mupdf/fitz.h index ec0c87a4..6f4e431d 100644 --- a/include/mupdf/fitz.h +++ b/include/mupdf/fitz.h @@ -58,6 +58,7 @@ extern "C" { #include "mupdf/fitz/util.h" /* Output formats */ +#include "mupdf/fitz/writer.h" #include "mupdf/fitz/output-pnm.h" #include "mupdf/fitz/output-png.h" #include "mupdf/fitz/output-pwg.h" diff --git a/include/mupdf/fitz/buffer.h b/include/mupdf/fitz/buffer.h index 2749c32c..1a8eb80c 100644 --- a/include/mupdf/fitz/buffer.h +++ b/include/mupdf/fitz/buffer.h @@ -130,6 +130,8 @@ void fz_append_buffer(fz_context *ctx, fz_buffer *buf, fz_buffer *extra); void fz_write_buffer(fz_context *ctx, fz_buffer *buf, const void *data, int len); void fz_write_buffer_byte(fz_context *ctx, fz_buffer *buf, int val); void fz_write_buffer_rune(fz_context *ctx, fz_buffer *buf, int val); +void fz_write_buffer_int32_le(fz_context *ctx, fz_buffer *buf, int x); +void fz_write_buffer_int16_le(fz_context *ctx, fz_buffer *buf, int x); void fz_write_buffer_bits(fz_context *ctx, fz_buffer *buf, int val, int bits); void fz_write_buffer_pad(fz_context *ctx, fz_buffer *buf); int fz_buffer_printf(fz_context *ctx, fz_buffer *buffer, const char *fmt, ...); diff --git a/include/mupdf/fitz/output.h b/include/mupdf/fitz/output.h index 8b59d386..35f61aa7 100644 --- a/include/mupdf/fitz/output.h +++ b/include/mupdf/fitz/output.h @@ -97,6 +97,16 @@ static inline void fz_write_int32_le(fz_context *ctx, fz_output *out, int x) fz_write(ctx, out, data, 4); } +static inline void fz_write_int16_le(fz_context *ctx, fz_output *out, int x) +{ + char data[2]; + + data[0] = x; + data[1] = x>>8; + + fz_write(ctx, out, data, 2); +} + static inline void fz_write_byte(fz_context *ctx, fz_output *out, unsigned char x) { fz_write(ctx, out, &x, 1); diff --git a/include/mupdf/fitz/unzip.h b/include/mupdf/fitz/unzip.h index b19ffbd9..64b2b94b 100644 --- a/include/mupdf/fitz/unzip.h +++ b/include/mupdf/fitz/unzip.h @@ -19,4 +19,10 @@ void fz_drop_archive(fz_context *ctx, fz_archive *ar); int fz_count_archive_entries(fz_context *ctx, fz_archive *zip); const char *fz_list_archive_entry(fz_context *ctx, fz_archive *zip, int idx); +typedef struct fz_zip_writer_s fz_zip_writer; + +fz_zip_writer *fz_new_zip_writer(fz_context *ctx, const char *filename); +void fz_write_zip_entry(fz_context *ctx, fz_zip_writer *zip, const char *name, fz_buffer *buf, int compress); +void fz_drop_zip_writer(fz_context *ctx, fz_zip_writer *zip); + #endif diff --git a/include/mupdf/fitz/writer.h b/include/mupdf/fitz/writer.h new file mode 100644 index 00000000..e318568e --- /dev/null +++ b/include/mupdf/fitz/writer.h @@ -0,0 +1,28 @@ +#ifndef MUPDF_FITZ_WRITER_H +#define MUPDF_FITZ_WRITER_H + +#include "mupdf/fitz/system.h" +#include "mupdf/fitz/context.h" +#include "mupdf/fitz/output.h" +#include "mupdf/fitz/document.h" +#include "mupdf/fitz/device.h" + +typedef struct fz_document_writer_s fz_document_writer; + +struct fz_document_writer_s +{ + fz_device *(*begin_page)(fz_context *ctx, fz_document_writer *wri, const fz_rect *mediabox, fz_matrix *ctm); + void (*end_page)(fz_context *ctx, fz_document_writer *wri, fz_device *dev); + void (*drop_imp)(fz_context *ctx, fz_document_writer *wri); +}; + +fz_document_writer *fz_new_document_writer(fz_context *ctx, const char *path, const char *format, const char *options); + +fz_device *fz_begin_page(fz_context *ctx, fz_document_writer *wri, const fz_rect *mediabox, fz_matrix *ctm); +void fz_end_page(fz_context *ctx, fz_document_writer *wri, fz_device *dev); +void fz_drop_document_writer(fz_context *ctx, fz_document_writer *wri); + +fz_document_writer *fz_new_cbz_writer(fz_context *ctx, const char *path, const char *options); +fz_document_writer *fz_new_pdf_writer(fz_context *ctx, const char *path, const char *options); + +#endif diff --git a/source/fitz/buffer.c b/source/fitz/buffer.c index 2d6f9932..4cff8715 100644 --- a/source/fitz/buffer.c +++ b/source/fitz/buffer.c @@ -197,6 +197,20 @@ void fz_write_buffer_rune(fz_context *ctx, fz_buffer *buf, int c) buf->unused_bits = 0; } +void fz_write_buffer_int32_le(fz_context *ctx, fz_buffer *buf, int x) +{ + fz_write_buffer_byte(ctx, buf, (x)&0xFF); + fz_write_buffer_byte(ctx, buf, (x>>8)&0xFF); + fz_write_buffer_byte(ctx, buf, (x>>16)&0xFF); + fz_write_buffer_byte(ctx, buf, (x>>24)&0xFF); +} + +void fz_write_buffer_int16_le(fz_context *ctx, fz_buffer *buf, int x) +{ + fz_write_buffer_byte(ctx, buf, (x)&0xFF); + fz_write_buffer_byte(ctx, buf, (x>>8)&0xFF); +} + void fz_write_buffer_bits(fz_context *ctx, fz_buffer *buf, int val, int bits) { int shift; diff --git a/source/fitz/output-cbz.c b/source/fitz/output-cbz.c new file mode 100644 index 00000000..df5c0640 --- /dev/null +++ b/source/fitz/output-cbz.c @@ -0,0 +1,94 @@ +#include "mupdf/fitz.h" + +#include + +typedef struct fz_cbz_writer_s fz_cbz_writer; + +struct fz_cbz_writer_s +{ + fz_document_writer super; + fz_zip_writer *zip; + int resolution; + fz_pixmap *pixmap; + int count; +}; + +static fz_device * +cbz_begin_page(fz_context *ctx, fz_document_writer *wri_, const fz_rect *mediabox, fz_matrix *ctm) +{ + fz_cbz_writer *wri = (fz_cbz_writer*)wri_; + fz_rect bbox; + fz_irect ibbox; + + fz_scale(ctm, wri->resolution / 72, wri->resolution / 72); + bbox = *mediabox; + fz_transform_rect(&bbox, ctm); + fz_round_rect(&ibbox, &bbox); + + wri->pixmap = fz_new_pixmap_with_bbox(ctx, fz_device_rgb(ctx), &ibbox); + fz_clear_pixmap_with_value(ctx, wri->pixmap, 0xFF); + + return fz_new_draw_device(ctx, wri->pixmap); +} + +static void +cbz_end_page(fz_context *ctx, fz_document_writer *wri_, fz_device *dev) +{ + fz_cbz_writer *wri = (fz_cbz_writer*)wri_; + fz_buffer *buffer; + char name[40]; + + wri->count += 1; + + fz_snprintf(name, sizeof name, "p%04d.png", wri->count); + + buffer = fz_new_buffer_from_pixmap_as_png(ctx, wri->pixmap); + fz_try(ctx) + fz_write_zip_entry(ctx, wri->zip, name, buffer, 0); + fz_always(ctx) + fz_drop_buffer(ctx, buffer); + fz_catch(ctx) + fz_rethrow(ctx); + + fz_drop_pixmap(ctx, wri->pixmap); + wri->pixmap = NULL; +} + +static void +cbz_drop_imp(fz_context *ctx, fz_document_writer *wri_) +{ + fz_cbz_writer *wri = (fz_cbz_writer*)wri_; + fz_try(ctx) + fz_drop_zip_writer(ctx, wri->zip); + fz_always(ctx) + fz_drop_pixmap(ctx, wri->pixmap); + fz_catch(ctx) + fz_rethrow(ctx); +} + +fz_document_writer * +fz_new_cbz_writer(fz_context *ctx, const char *path, const char *options) +{ + fz_cbz_writer *wri = fz_malloc_struct(ctx, fz_cbz_writer); + + wri->super.begin_page = cbz_begin_page; + wri->super.end_page = cbz_end_page; + wri->super.drop_imp = cbz_drop_imp; + + fz_try(ctx) + wri->zip = fz_new_zip_writer(ctx, path); + fz_catch(ctx) + { + fz_free(ctx, wri); + fz_rethrow(ctx); + } + + // TODO: getopt-like comma separated list of options + if (options) + wri->resolution = atoi(options); + + if (wri->resolution == 0) + wri->resolution = 96; + + return (fz_document_writer*)wri; +} diff --git a/source/fitz/writer.c b/source/fitz/writer.c new file mode 100644 index 00000000..a127a47d --- /dev/null +++ b/source/fitz/writer.c @@ -0,0 +1,38 @@ +#include "mupdf/fitz.h" + +fz_document_writer * +fz_new_document_writer(fz_context *ctx, const char *path, const char *format, const char *options) +{ + if (!format) + { + format = strrchr(path, '.'); + if (!format) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot detect document format"); + format += 1; /* skip the '.' */ + } + + if (!strcasecmp(format, "cbz")) + return fz_new_cbz_writer(ctx, path, options); + + fz_throw(ctx, FZ_ERROR_GENERIC, "unknown document format: %s", format); +} + +void +fz_drop_document_writer(fz_context *ctx, fz_document_writer *wri) +{ + if (wri->drop_imp) + wri->drop_imp(ctx, wri); + fz_free(ctx, wri); +} + +fz_device * +fz_begin_page(fz_context *ctx, fz_document_writer *wri, const fz_rect *mediabox, fz_matrix *ctm) +{ + return wri->begin_page(ctx, wri, mediabox, ctm); +} + +void +fz_end_page(fz_context *ctx, fz_document_writer *wri, fz_device *dev) +{ + return wri->end_page(ctx, wri, dev); +} diff --git a/source/fitz/zip.c b/source/fitz/zip.c new file mode 100644 index 00000000..285fb635 --- /dev/null +++ b/source/fitz/zip.c @@ -0,0 +1,112 @@ +#include "mupdf/fitz.h" + +#include + +#if !defined (INT32_MAX) +#define INT32_MAX 2147483647L +#endif + +#define ZIP_LOCAL_FILE_SIG 0x04034b50 +#define ZIP_CENTRAL_DIRECTORY_SIG 0x02014b50 +#define ZIP_END_OF_CENTRAL_DIRECTORY_SIG 0x06054b50 + +struct fz_zip_writer_s +{ + fz_output *output; + fz_buffer *central; + int count; +}; + +void +fz_write_zip_entry(fz_context *ctx, fz_zip_writer *zip, const char *name, fz_buffer *buf, int compress) +{ + int offset = fz_tell_output(ctx, zip->output); + int sum; + + sum = crc32(0, NULL, 0); + sum = crc32(sum, buf->data, buf->len); + + fz_write_buffer_int32_le(ctx, zip->central, ZIP_CENTRAL_DIRECTORY_SIG); + fz_write_buffer_int16_le(ctx, zip->central, 0); /* version made by: MS-DOS */ + fz_write_buffer_int16_le(ctx, zip->central, 20); /* version to extract: 2.0 */ + fz_write_buffer_int16_le(ctx, zip->central, 0); /* general purpose bit flag */ + fz_write_buffer_int16_le(ctx, zip->central, 0); /* compression method: store */ + fz_write_buffer_int16_le(ctx, zip->central, 0); /* TODO: last mod file time */ + fz_write_buffer_int16_le(ctx, zip->central, 0); /* TODO: last mod file date */ + fz_write_buffer_int32_le(ctx, zip->central, sum); /* crc-32 */ + fz_write_buffer_int32_le(ctx, zip->central, buf->len); /* csize */ + fz_write_buffer_int32_le(ctx, zip->central, buf->len); /* usize */ + fz_write_buffer_int16_le(ctx, zip->central, strlen(name)); /* file name length */ + fz_write_buffer_int16_le(ctx, zip->central, 0); /* extra field length */ + fz_write_buffer_int16_le(ctx, zip->central, 0); /* file comment length */ + fz_write_buffer_int16_le(ctx, zip->central, 0); /* disk number start */ + fz_write_buffer_int16_le(ctx, zip->central, 0); /* internal file attributes */ + fz_write_buffer_int32_le(ctx, zip->central, 0); /* external file attributes */ + fz_write_buffer_int32_le(ctx, zip->central, offset); /* relative offset of local header */ + fz_write_buffer(ctx, zip->central, name, strlen(name)); + + fz_write_int32_le(ctx, zip->output, ZIP_LOCAL_FILE_SIG); + fz_write_int16_le(ctx, zip->output, 20); /* version to extract: 2.0 */ + fz_write_int16_le(ctx, zip->output, 0); /* general purpose bit flag */ + fz_write_int16_le(ctx, zip->output, 0); /* compression method: store */ + fz_write_int16_le(ctx, zip->output, 0); /* TODO: last mod file time */ + fz_write_int16_le(ctx, zip->output, 0); /* TODO: last mod file date */ + fz_write_int32_le(ctx, zip->output, sum); /* crc-32 */ + fz_write_int32_le(ctx, zip->output, buf->len); /* csize */ + fz_write_int32_le(ctx, zip->output, buf->len); /* usize */ + fz_write_int16_le(ctx, zip->output, strlen(name)); /* file name length */ + fz_write_int16_le(ctx, zip->output, 0); /* extra field length */ + fz_write(ctx, zip->output, name, strlen(name)); + fz_write(ctx, zip->output, buf->data, buf->len); + + ++zip->count; +} + +void +fz_drop_zip_writer(fz_context *ctx, fz_zip_writer *zip) +{ + fz_try(ctx) + { + int offset = fz_tell_output(ctx, zip->output); + + fz_write(ctx, zip->output, zip->central->data, zip->central->len); + + fz_write_int32_le(ctx, zip->output, ZIP_END_OF_CENTRAL_DIRECTORY_SIG); + fz_write_int16_le(ctx, zip->output, 0); /* number of this disk */ + fz_write_int16_le(ctx, zip->output, 0); /* number of disk where central directory starts */ + fz_write_int16_le(ctx, zip->output, zip->count); /* entries in central directory in this disk */ + fz_write_int16_le(ctx, zip->output, zip->count); /* entries in central directory in total */ + fz_write_int32_le(ctx, zip->output, zip->central->len); /* size of the central directory */ + fz_write_int32_le(ctx, zip->output, offset); /* offset of the central directory */ + fz_write_int16_le(ctx, zip->output, 5); /* zip file comment length */ + + fz_write(ctx, zip->output, "MuPDF", 5); + } + fz_always(ctx) + { + fz_drop_output(ctx, zip->output); + fz_drop_buffer(ctx, zip->central); + fz_free(ctx, zip); + } + fz_catch(ctx) + fz_rethrow(ctx); +} + +fz_zip_writer * +fz_new_zip_writer(fz_context *ctx, const char *filename) +{ + fz_zip_writer *zip = fz_malloc_struct(ctx, fz_zip_writer); + fz_try(ctx) + { + zip->output = fz_new_output_with_path(ctx, filename, 0); + zip->central = fz_new_buffer(ctx, 0); + } + fz_catch(ctx) + { + fz_drop_output(ctx, zip->output); + fz_drop_buffer(ctx, zip->central); + fz_free(ctx, zip); + fz_rethrow(ctx); + } + return zip; +} diff --git a/source/tools/muconvert.c b/source/tools/muconvert.c new file mode 100644 index 00000000..5e72b5d3 --- /dev/null +++ b/source/tools/muconvert.c @@ -0,0 +1,141 @@ +/* + * muconvert -- command line tool for converting documents + */ + +#include "mupdf/fitz.h" + +/* input options */ +static const char *password = ""; +static int alphabits = 8; +static float layout_w = 450; +static float layout_h = 600; +static float layout_em = 12; +static char *layout_css = NULL; + +/* output options */ +static const char *output = "out.pdf"; +static const char *format = NULL; +static const char *options = ""; + +static fz_context *ctx; +static fz_document *doc; +static fz_document_writer *out; + +static void usage(void) +{ + fprintf(stderr, + "mutool convert version " FZ_VERSION "\n" + "Usage: mutool convert [options] file [pages]\n" + "\t-p -\tpassword\n" + "\n" + "\t-A -\tnumber of bits of antialiasing (0 to 8)\n" + "\t-W -\tpage width for EPUB layout\n" + "\t-H -\tpage height for EPUB layout\n" + "\t-S -\tfont size for EPUB layout\n" + "\t-U -\tfile name of user stylesheet for EPUB layout\n" + "\n" + "\t-o -\toutput file name (%%d for page number)\n" + "\t-F -\toutput format (default inferred from output file name)\n" + "\t\tcbz, pdf\n" + "\t-O -\toutput format options\n" + "\n" + "\tpages\tcomma separated list of page numbers and ranges\n" + ); + exit(1); +} + +int muconvert_main(int argc, char **argv) +{ + int i, k, n, c; + + while ((c = fz_getopt(argc, argv, "p:A:W:H:S:U:o:F:O:")) != -1) + { + switch (c) + { + default: usage(); break; + + case 'p': password = fz_optarg; break; + case 'A': alphabits = atoi(fz_optarg); break; + case 'W': layout_w = atof(fz_optarg); break; + case 'H': layout_h = atof(fz_optarg); break; + case 'S': layout_em = atof(fz_optarg); break; + case 'U': layout_css = fz_optarg; break; + + case 'o': output = fz_optarg; break; + case 'F': format = fz_optarg; break; + case 'O': options = fz_optarg; break; + } + } + + if (fz_optind == argc) + usage(); + + /* Create a context to hold the exception stack and various caches. */ + ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); + if (!ctx) + { + fprintf(stderr, "cannot create mupdf context\n"); + return EXIT_FAILURE; + } + + /* Register the default file types to handle. */ + fz_try(ctx) + fz_register_document_handlers(ctx); + fz_catch(ctx) + { + fprintf(stderr, "cannot register document handlers: %s\n", fz_caught_message(ctx)); + fz_drop_context(ctx); + return EXIT_FAILURE; + } + + fz_set_aa_level(ctx, alphabits); + + if (layout_css) + { + fz_buffer *buf = fz_read_file(ctx, layout_css); + fz_write_buffer_byte(ctx, buf, 0); + fz_set_user_css(ctx, (char*)buf->data); + fz_drop_buffer(ctx, buf); + } + + /* Open the output document. */ + fz_try(ctx) + out = fz_new_document_writer(ctx, output, format, options); + fz_catch(ctx) + { + fprintf(stderr, "cannot open document: %s\n", fz_caught_message(ctx)); + fz_drop_context(ctx); + return EXIT_FAILURE; + } + + for (i = fz_optind; i < argc; ++i) + { + doc = fz_open_document(ctx, argv[i]); + if (fz_needs_password(ctx, doc)) + if (!fz_authenticate_password(ctx, doc, password)) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot authenticate password: %s", argv[i]); + fz_layout_document(ctx, doc, layout_w, layout_h, layout_em); + + n = fz_count_pages(ctx, doc); + for (k = 0; k < n; ++k) + { + fz_matrix ctm; + fz_rect mediabox; + fz_page *page; + fz_device *dev; + + page = fz_load_page(ctx, doc, k); + fz_bound_page(ctx, page, &mediabox); + dev = fz_begin_page(ctx, out, &mediabox, &ctm); + fz_run_page(ctx, page, dev, &ctm, NULL); + fz_end_page(ctx, out, dev); + fz_drop_page(ctx, page); + } + + fz_drop_document(ctx, doc); + } + + fz_drop_document_writer(ctx, out); + fz_drop_context(ctx); + return EXIT_SUCCESS; +} diff --git a/source/tools/mutool.c b/source/tools/mutool.c index fd38be4d..3ba45ea0 100644 --- a/source/tools/mutool.c +++ b/source/tools/mutool.c @@ -8,6 +8,7 @@ #define main main_utf8 #endif +int muconvert_main(int argc, char *argv[]); int mudraw_main(int argc, char *argv[]); int murun_main(int argc, char *argv[]); int pdfclean_main(int argc, char *argv[]); @@ -24,6 +25,7 @@ static struct { char *name; char *desc; } tools[] = { + { muconvert_main, "convert", "convert document" }, { mudraw_main, "draw", "convert document" }, { murun_main, "run", "run javascript" }, { pdfclean_main, "clean", "rewrite pdf file" }, @@ -66,7 +68,6 @@ int main(int argc, char **argv) char buf[32]; int i; - if (argc == 0) { fprintf(stderr, "No command name found!\n"); -- cgit v1.2.3