/* * pdfportfolio -- manipulate embedded files in a PDF */ #include "mupdf/fitz.h" #include "mupdf/pdf.h" #include #include #include static pdf_document *doc = NULL; static fz_context *ctx = NULL; static void usage(void) { fprintf(stderr, "usage: mutool portfolio [options] portfolio.pdf [actions]\n"); fprintf(stderr, "\nOptions are:\n"); fprintf(stderr, "\t-p -\tpassword\n"); fprintf(stderr, "\t-o -\toutput (defaults to input file)\n"); fprintf(stderr, "\t-O -\tPDF output options (see mutool create)\n"); fprintf(stderr, "\nActions are:\n"); fprintf(stderr, "\tt\tdisplay a table listing the contents of the portfolio\n"); fprintf(stderr, "\tx N \n\t\textract Nth entry to \n"); fprintf(stderr, "\ta \n\t\tadd contents of as an entry named \n"); fprintf(stderr, "\nFor safety, only use ASCII characters in entry names for now.\n"); exit(1); } static void safe_print_pdf_string(fz_context *ctx, unsigned char *str, int len) { int c; if (len > 1 && str[0] == 0xFE && str[1] == 0xFF) { str += 2; len -= 2; while (len) { c = (*str++<<8); c += *str++; if (c >= 32 && c != 127 && c < 256) fprintf(stderr, "%c", c); else fprintf(stderr, "<%04x>", c); len -= 2; }; } else { while (len) { c = *str++; if (c >= 32 && c != 127 && c < 256) fprintf(stderr, "%c", c); else fprintf(stderr, "<%02x>", c); len--; }; } } static void safe_print_pdf_obj(fz_context *ctx, pdf_obj *obj, const char *dflt) { if (obj == NULL) fprintf(stderr, "%s", dflt); else if (pdf_is_string(ctx, obj)) safe_print_pdf_string(ctx, (unsigned char *)pdf_to_str_buf(ctx, obj), pdf_to_str_len(ctx, obj)); else pdf_print_obj(ctx, fz_stderr(ctx), obj, 1); } static void pdfportfolio_list(void) { /* List files */ int m = pdf_count_portfolio_schema(ctx, doc); int n = pdf_count_portfolio_entries(ctx, doc); int i, j; for (i = 0; i < n; i++) { pdf_obj *name = pdf_portfolio_entry_name(ctx, doc, i); fprintf(stderr, " %s%d: ", i < 10 ? " " : "", i); safe_print_pdf_obj(ctx, name, "(Unnamed)"); fprintf(stderr, "\n"); for (j = 0; j < m; j++) { pdf_portfolio_schema info; pdf_obj *obj; char *type; pdf_portfolio_schema_info(ctx, doc, j, &info); obj = pdf_portfolio_entry_info(ctx, doc, i, j); fprintf(stderr, " "); safe_print_pdf_obj(ctx, info.name, "(Unnamed)"); switch(info.type) { case PDF_SCHEMA_TEXT: type = "T"; break; case PDF_SCHEMA_DATE: type = "D"; break; case PDF_SCHEMA_NUMBER: type = "N"; break; case PDF_SCHEMA_FILENAME: type = "F"; break; case PDF_SCHEMA_DESC: type = "E"; break; case PDF_SCHEMA_MODDATE: type = "M"; break; case PDF_SCHEMA_CREATIONDATE: type = "C"; break; case PDF_SCHEMA_SIZE: type = "S"; break; default: type = "?"; break; } fprintf(stderr, ":%s:", type); safe_print_pdf_obj(ctx, obj, ""); if (info.editable) fprintf(stderr, " (Editable)"); if (info.visible) fprintf(stderr, " (Visible)"); fprintf(stderr, "\n"); } } } static void pdfportfolio_extract(int argc, char **argv) { int entry; const char *filename; fz_buffer *buf; unsigned char *data; int len; FILE *file; if (fz_optind > argc-2) usage(); entry = fz_atoi(argv[fz_optind++]); filename = argv[fz_optind++]; buf = pdf_portfolio_entry(ctx, doc, entry); len = fz_buffer_storage(ctx, buf, &data); file = fopen(filename, "wb"); if (file == NULL) { fprintf(stderr, "Failed to open '%s' for writing\n", filename); exit(1); } fwrite(data, 1, len, file); fclose(file); fz_drop_buffer(ctx, buf); } static void pdfportfolio_add(int argc, char **argv) { const char *filename; const char *ename; fz_buffer *buf; if (fz_optind > argc-2) usage(); filename = argv[fz_optind++]; ename = argv[fz_optind++]; if (ename == NULL) ename = filename; buf = fz_read_file(ctx, filename); pdf_add_portfolio_entry(ctx, doc, ename, strlen(ename), /* name */ ename, strlen(ename), /* desc */ ename, strlen(ename), /* filename */ ename, strlen(ename), /* unifile */ buf); fz_drop_buffer(ctx, buf); } int pdfportfolio_main(int argc, char **argv) { char *password = ""; char *outfile = NULL; char *outopts = "compress"; char *infile; int exit_code = 0; int do_save = 0; int has_old_file = 0; int c; while ((c = fz_getopt(argc, argv, "p:o:O:")) != -1) { switch (c) { case 'p': password = fz_optarg; break; case 'o': outfile = fz_optarg; break; case 'O': outopts = fz_optarg; break; default: usage(); break; } } if (fz_optind == argc) usage(); infile = argv[fz_optind++]; if (!outfile) outfile = infile; ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); if (!ctx) { fprintf(stderr, "cannot initialise context\n"); exit(1); } if (fz_file_exists(ctx, infile)) { doc = pdf_open_document(ctx, infile); if (pdf_needs_password(ctx, doc)) if (!pdf_authenticate_password(ctx, doc, password)) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot authenticate password: %s", infile); has_old_file = 1; } else { doc = pdf_create_document(ctx); /* add a blank page */ { const char *template = "BT /Tm 16 Tf 50 434 TD (This is a portfolio document.) Tj ET\n"; const unsigned char *data; int size; fz_font *font; pdf_obj *font_obj, *page_obj; pdf_obj *resources; fz_buffer *contents; fz_rect mediabox = { 0, 0, 400, 500 }; data = fz_lookup_base14_font(ctx, "Times-Roman", &size); font = fz_new_font_from_memory(ctx, "Times-Roman", data, size, 0, 0); font_obj = pdf_add_simple_font(ctx, doc, font, PDF_SIMPLE_ENCODING_LATIN); fz_drop_font(ctx, font); resources = pdf_add_new_dict(ctx, doc, 1); pdf_dict_putp_drop(ctx, resources, "Font/Tm", font_obj); contents = fz_new_buffer_from_shared_data(ctx, (const unsigned char *)template, strlen(template)); page_obj = pdf_add_page(ctx, doc, mediabox, 0, resources, contents); pdf_insert_page(ctx, doc, -1, page_obj); pdf_drop_obj(ctx, page_obj); pdf_drop_obj(ctx, resources); fz_drop_buffer(ctx, contents); } } if (fz_optind == argc) usage(); while (fz_optind < argc) { fz_optarg = argv[fz_optind++]; fz_try(ctx) { switch (*fz_optarg) { default: usage(); break; case 't': pdfportfolio_list(); break; case 'x': pdfportfolio_extract(argc, argv); break; case 'a': pdfportfolio_add(argc, argv); do_save = 1; break; } } fz_catch(ctx) { /* Swallow any errors */ exit_code = 1; } } if (do_save && !exit_code) { pdf_write_options opts; pdf_parse_write_options(ctx, &opts, outopts); if (has_old_file && infile == outfile) opts.do_incremental = 1; pdf_save_document(ctx, doc, outfile, &opts); } pdf_drop_document(ctx, doc); fz_flush_warnings(ctx); fz_drop_context(ctx); return exit_code; }