Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/source/tools/pdfposter.c @ 3:2c135c81b16c
MERGE: upstream PyMuPDF 1.26.4 with MuPDF 1.26.7
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:44:09 +0200 |
| parents | b50eed0cc0ef |
| children |
comparison
equal
deleted
inserted
replaced
| 0:6015a75abc2d | 3:2c135c81b16c |
|---|---|
| 1 // Copyright (C) 2004-2024 Artifex Software, Inc. | |
| 2 // | |
| 3 // This file is part of MuPDF. | |
| 4 // | |
| 5 // MuPDF is free software: you can redistribute it and/or modify it under the | |
| 6 // terms of the GNU Affero General Public License as published by the Free | |
| 7 // Software Foundation, either version 3 of the License, or (at your option) | |
| 8 // any later version. | |
| 9 // | |
| 10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| 12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
| 13 // details. | |
| 14 // | |
| 15 // You should have received a copy of the GNU Affero General Public License | |
| 16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> | |
| 17 // | |
| 18 // Alternative licensing terms are available from the licensor. | |
| 19 // For commercial licensing, see <https://www.artifex.com/> or contact | |
| 20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 21 // CA 94129, USA, for further information. | |
| 22 | |
| 23 /* | |
| 24 * PDF posteriser; split pages within a PDF file into smaller lumps. | |
| 25 */ | |
| 26 | |
| 27 #include "mupdf/fitz.h" | |
| 28 #include "mupdf/pdf.h" | |
| 29 | |
| 30 #include <string.h> | |
| 31 #include <stdlib.h> | |
| 32 #include <stdio.h> | |
| 33 | |
| 34 static int x_factor = 0; | |
| 35 static int y_factor = 0; | |
| 36 static float margin = 0; | |
| 37 static int margin_is_percent = 0; | |
| 38 static int x_dir = 1; | |
| 39 | |
| 40 static int usage(void) | |
| 41 { | |
| 42 fprintf(stderr, | |
| 43 "usage: mutool poster [options] input.pdf [output.pdf]\n" | |
| 44 "\t-p -\tpassword\n" | |
| 45 "\t-m -\tmargin (overlap) between pages (pts, or %%)\n" | |
| 46 "\t-x\tx decimation factor\n" | |
| 47 "\t-y\ty decimation factor\n" | |
| 48 "\t-r\tsplit right-to-left\n" | |
| 49 ); | |
| 50 return 1; | |
| 51 } | |
| 52 | |
| 53 static void | |
| 54 intersect_box(fz_context *ctx, pdf_document *doc, pdf_obj *page, pdf_obj *box_name, fz_rect mb) | |
| 55 { | |
| 56 pdf_obj *box = pdf_dict_get(ctx, page, box_name); | |
| 57 pdf_obj *newbox; | |
| 58 fz_rect old_rect; | |
| 59 | |
| 60 if (box == NULL) | |
| 61 return; | |
| 62 | |
| 63 old_rect.x0 = pdf_array_get_real(ctx, box, 0); | |
| 64 old_rect.y0 = pdf_array_get_real(ctx, box, 1); | |
| 65 old_rect.x1 = pdf_array_get_real(ctx, box, 2); | |
| 66 old_rect.y1 = pdf_array_get_real(ctx, box, 3); | |
| 67 | |
| 68 if (old_rect.x0 < mb.x0) | |
| 69 old_rect.x0 = mb.x0; | |
| 70 if (old_rect.y0 < mb.y0) | |
| 71 old_rect.y0 = mb.y0; | |
| 72 if (old_rect.x1 > mb.x1) | |
| 73 old_rect.x1 = mb.x1; | |
| 74 if (old_rect.y1 > mb.y1) | |
| 75 old_rect.y1 = mb.y1; | |
| 76 | |
| 77 newbox = pdf_new_array(ctx, doc, 4); | |
| 78 pdf_array_push_real(ctx, newbox, old_rect.x0); | |
| 79 pdf_array_push_real(ctx, newbox, old_rect.y0); | |
| 80 pdf_array_push_real(ctx, newbox, old_rect.x1); | |
| 81 pdf_array_push_real(ctx, newbox, old_rect.y1); | |
| 82 pdf_dict_put_drop(ctx, page, box_name, newbox); | |
| 83 } | |
| 84 | |
| 85 /* | |
| 86 * Recreate page tree with our posterised pages in. | |
| 87 */ | |
| 88 | |
| 89 static void decimatepages(fz_context *ctx, pdf_document *doc) | |
| 90 { | |
| 91 pdf_obj *oldroot, *root, *pages, *kids; | |
| 92 int num_pages = pdf_count_pages(ctx, doc); | |
| 93 int page, kidcount; | |
| 94 fz_rect mediabox, cropbox; | |
| 95 int rotate; | |
| 96 | |
| 97 oldroot = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)); | |
| 98 pages = pdf_dict_get(ctx, oldroot, PDF_NAME(Pages)); | |
| 99 | |
| 100 root = pdf_new_dict(ctx, doc, 2); | |
| 101 pdf_dict_put(ctx, root, PDF_NAME(Type), pdf_dict_get(ctx, oldroot, PDF_NAME(Type))); | |
| 102 pdf_dict_put(ctx, root, PDF_NAME(Pages), pdf_dict_get(ctx, oldroot, PDF_NAME(Pages))); | |
| 103 | |
| 104 pdf_update_object(ctx, doc, pdf_to_num(ctx, oldroot), root); | |
| 105 | |
| 106 pdf_drop_obj(ctx, root); | |
| 107 | |
| 108 /* Create a new kids array with our new pages in */ | |
| 109 kids = pdf_new_array(ctx, doc, 1); | |
| 110 | |
| 111 kidcount = 0; | |
| 112 for (page=0; page < num_pages; page++) | |
| 113 { | |
| 114 pdf_obj *page_obj = pdf_lookup_page_obj(ctx, doc, page); | |
| 115 int xf = x_factor, yf = y_factor; | |
| 116 float w, h; | |
| 117 int x, y; | |
| 118 int xd, yd, y0, y1; | |
| 119 float xmargin, ymargin; | |
| 120 | |
| 121 rotate = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, page_obj, PDF_NAME(Rotate))); | |
| 122 mediabox = pdf_to_rect(ctx, pdf_dict_get_inheritable(ctx, page_obj, PDF_NAME(MediaBox))); | |
| 123 cropbox = pdf_to_rect(ctx, pdf_dict_get_inheritable(ctx, page_obj, PDF_NAME(CropBox))); | |
| 124 if (fz_is_empty_rect(mediabox)) | |
| 125 mediabox = fz_make_rect(0, 0, 612, 792); | |
| 126 if (!fz_is_empty_rect(cropbox)) | |
| 127 mediabox = fz_intersect_rect(mediabox, cropbox); | |
| 128 | |
| 129 w = mediabox.x1 - mediabox.x0; | |
| 130 h = mediabox.y1 - mediabox.y0; | |
| 131 | |
| 132 if (rotate == 90 || rotate == 270) | |
| 133 { | |
| 134 xf = y_factor; | |
| 135 yf = x_factor; | |
| 136 yd = -x_dir; | |
| 137 xd = 1; | |
| 138 } | |
| 139 else | |
| 140 { | |
| 141 xf = x_factor; | |
| 142 yf = y_factor; | |
| 143 xd = x_dir; | |
| 144 yd = -1; | |
| 145 } | |
| 146 | |
| 147 if (xf == 0 && yf == 0) | |
| 148 { | |
| 149 /* Nothing specified, so split along the long edge */ | |
| 150 if (w > h) | |
| 151 xf = 2, yf = 1; | |
| 152 else | |
| 153 xf = 1, yf = 2; | |
| 154 } | |
| 155 else if (xf == 0) | |
| 156 xf = 1; | |
| 157 else if (yf == 0) | |
| 158 yf = 1; | |
| 159 | |
| 160 xmargin = xf == 1 ? 0 : margin * (margin_is_percent ? .001 * w/xf : 1); | |
| 161 ymargin = yf == 1 ? 0 : margin * (margin_is_percent ? .001 * h/yf : 1); | |
| 162 | |
| 163 y0 = (yd > 0) ? 0 : yf-1; | |
| 164 y1 = (yd > 0) ? yf : -1; | |
| 165 for (y = y0; y != y1; y += yd) | |
| 166 { | |
| 167 int x0 = (xd > 0) ? 0 : xf-1; | |
| 168 int x1 = (xd > 0) ? xf : -1; | |
| 169 for (x = x0; x != x1; x += xd) | |
| 170 { | |
| 171 pdf_obj *newpageobj, *newpageref, *newmediabox; | |
| 172 fz_rect mb; | |
| 173 | |
| 174 newpageobj = pdf_copy_dict(ctx, pdf_lookup_page_obj(ctx, doc, page)); | |
| 175 pdf_flatten_inheritable_page_items(ctx, newpageobj); | |
| 176 newpageref = pdf_add_object(ctx, doc, newpageobj); | |
| 177 | |
| 178 newmediabox = pdf_new_array(ctx, doc, 4); | |
| 179 | |
| 180 mb.x0 = mediabox.x0 + (w/xf)*x; | |
| 181 if (x == xf-1) | |
| 182 mb.x1 = mediabox.x1; | |
| 183 else | |
| 184 mb.x1 = mb.x0 + (w/xf) + xmargin; | |
| 185 mb.y0 = mediabox.y0 + (h/yf)*y; | |
| 186 if (y == yf-1) | |
| 187 mb.y1 = mediabox.y1; | |
| 188 else | |
| 189 mb.y1 = mb.y0 + (h/yf) + ymargin; | |
| 190 | |
| 191 pdf_array_push_real(ctx, newmediabox, mb.x0); | |
| 192 pdf_array_push_real(ctx, newmediabox, mb.y0); | |
| 193 pdf_array_push_real(ctx, newmediabox, mb.x1); | |
| 194 pdf_array_push_real(ctx, newmediabox, mb.y1); | |
| 195 | |
| 196 pdf_dict_put(ctx, newpageobj, PDF_NAME(Parent), pages); | |
| 197 pdf_dict_put_drop(ctx, newpageobj, PDF_NAME(MediaBox), newmediabox); | |
| 198 | |
| 199 intersect_box(ctx, doc, newpageobj, PDF_NAME(CropBox), mb); | |
| 200 intersect_box(ctx, doc, newpageobj, PDF_NAME(BleedBox), mb); | |
| 201 intersect_box(ctx, doc, newpageobj, PDF_NAME(TrimBox), mb); | |
| 202 intersect_box(ctx, doc, newpageobj, PDF_NAME(ArtBox), mb); | |
| 203 | |
| 204 /* Store page object in new kids array */ | |
| 205 pdf_drop_obj(ctx, newpageobj); | |
| 206 pdf_array_push_drop(ctx, kids, newpageref); | |
| 207 | |
| 208 kidcount++; | |
| 209 } | |
| 210 } | |
| 211 } | |
| 212 | |
| 213 /* Update page count and kids array */ | |
| 214 pdf_dict_put_int(ctx, pages, PDF_NAME(Count), kidcount); | |
| 215 pdf_dict_put_drop(ctx, pages, PDF_NAME(Kids), kids); | |
| 216 } | |
| 217 | |
| 218 int pdfposter_main(int argc, char **argv) | |
| 219 { | |
| 220 char *infile; | |
| 221 char *outfile = "out.pdf"; | |
| 222 char *password = ""; | |
| 223 int c; | |
| 224 pdf_write_options opts = pdf_default_write_options; | |
| 225 pdf_document *doc; | |
| 226 fz_context *ctx; | |
| 227 int ret = 0; | |
| 228 | |
| 229 while ((c = fz_getopt(argc, argv, "m:x:y:p:r")) != -1) | |
| 230 { | |
| 231 switch (c) | |
| 232 { | |
| 233 case 'p': password = fz_optarg; break; | |
| 234 case 'm': margin = atof(fz_optarg); margin_is_percent = (strchr(fz_optarg, '%') != NULL); break; | |
| 235 case 'x': x_factor = atoi(fz_optarg); break; | |
| 236 case 'y': y_factor = atoi(fz_optarg); break; | |
| 237 case 'r': x_dir = -1; break; | |
| 238 default: return usage(); | |
| 239 } | |
| 240 } | |
| 241 | |
| 242 if (argc - fz_optind < 1) | |
| 243 return usage(); | |
| 244 | |
| 245 infile = argv[fz_optind++]; | |
| 246 | |
| 247 if (argc - fz_optind > 0 && | |
| 248 (strstr(argv[fz_optind], ".pdf") || strstr(argv[fz_optind], ".PDF"))) | |
| 249 { | |
| 250 outfile = fz_optpath(argv[fz_optind++]); | |
| 251 } | |
| 252 | |
| 253 ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); | |
| 254 if (!ctx) | |
| 255 { | |
| 256 fprintf(stderr, "cannot initialise context\n"); | |
| 257 exit(1); | |
| 258 } | |
| 259 | |
| 260 fz_var(doc); | |
| 261 | |
| 262 fz_try(ctx) | |
| 263 { | |
| 264 doc = pdf_open_document(ctx, infile); | |
| 265 if (pdf_needs_password(ctx, doc)) | |
| 266 if (!pdf_authenticate_password(ctx, doc, password)) | |
| 267 fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot authenticate password: %s", infile); | |
| 268 | |
| 269 decimatepages(ctx, doc); | |
| 270 | |
| 271 pdf_save_document(ctx, doc, outfile, &opts); | |
| 272 } | |
| 273 fz_always(ctx) | |
| 274 pdf_drop_document(ctx, doc); | |
| 275 fz_catch(ctx) | |
| 276 { | |
| 277 fz_report_error(ctx); | |
| 278 ret = 1; | |
| 279 } | |
| 280 fz_drop_context(ctx); | |
| 281 | |
| 282 return ret; | |
| 283 } |
