comparison mupdf-source/source/tools/pdfcreate.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-2021 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 creation tool: Tool for creating pdf content.
25 *
26 * Simple test bed to work with adding content and creating PDFs
27 */
28
29 #include "mupdf/fitz.h"
30 #include "mupdf/pdf.h"
31
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35
36 static int usage(void)
37 {
38 fprintf(stderr,
39 "usage: mutool create [-o output.pdf] [-O options] page.txt [page2.txt ...]\n"
40 "\t-o -\tname of PDF file to create\n"
41 "\t-O -\tcomma separated list of output options\n"
42 "\tpage.txt\tcontent stream with annotations for creating resources\n\n"
43 "Content stream special commands:\n"
44 "\t%%%%MediaBox LLX LLY URX URY\n"
45 "\t%%%%Rotate Angle\n"
46 "\t%%%%Font Name Filename Encoding\n"
47 "\t\tFilename is either a file or a base 14 font name\n"
48 "\t\tEncoding=Latin|Greek|Cyrillic\n"
49 "\t%%%%CJKFont Name Language WMode Style\n"
50 "\t\tLanguage=zh-Hant|zh-Hans|ja|ko\n"
51 "\t\tWMode=H|V\n"
52 "\t\tStyle=serif|sans)\n"
53 "\t%%%%Image Name Filename\n\n"
54 );
55 fputs(fz_pdf_write_options_usage, stderr);
56 return 1;
57 }
58
59 static fz_context *ctx = NULL;
60 static pdf_document *doc = NULL;
61
62 static void add_font_res(pdf_obj *resources, char *name, char *path, char *encname)
63 {
64 const unsigned char *data;
65 int size, enc;
66 fz_font *font;
67 pdf_obj *subres, *ref;
68
69 data = fz_lookup_base14_font(ctx, path, &size);
70 if (data)
71 font = fz_new_font_from_memory(ctx, path, data, size, 0, 0);
72 else
73 font = fz_new_font_from_file(ctx, NULL, path, 0, 0);
74
75 subres = pdf_dict_get(ctx, resources, PDF_NAME(Font));
76 if (!subres)
77 {
78 subres = pdf_new_dict(ctx, doc, 10);
79 pdf_dict_put_drop(ctx, resources, PDF_NAME(Font), subres);
80 }
81
82 enc = PDF_SIMPLE_ENCODING_LATIN;
83 if (encname)
84 {
85 if (!strcmp(encname, "Latin") || !strcmp(encname, "Latn"))
86 enc = PDF_SIMPLE_ENCODING_LATIN;
87 else if (!strcmp(encname, "Greek") || !strcmp(encname, "Grek"))
88 enc = PDF_SIMPLE_ENCODING_GREEK;
89 else if (!strcmp(encname, "Cyrillic") || !strcmp(encname, "Cyrl"))
90 enc = PDF_SIMPLE_ENCODING_CYRILLIC;
91 }
92
93 ref = pdf_add_simple_font(ctx, doc, font, enc);
94 pdf_dict_puts(ctx, subres, name, ref);
95 pdf_drop_obj(ctx, ref);
96
97 fz_drop_font(ctx, font);
98 }
99
100 static void add_cjkfont_res(pdf_obj *resources, char *name, char *lang, char *wm, char *style)
101 {
102 const unsigned char *data;
103 int size, index, ordering, wmode, serif;
104 fz_font *font;
105 pdf_obj *subres, *ref;
106
107 ordering = fz_lookup_cjk_ordering_by_language(lang);
108
109 if (wm && !strcmp(wm, "V"))
110 wmode = 1;
111 else
112 wmode = 0;
113
114 if (style && (!strcmp(style, "sans") || !strcmp(style, "sans-serif")))
115 serif = 0;
116 else
117 serif = 1;
118
119 data = fz_lookup_cjk_font(ctx, ordering, &size, &index);
120 font = fz_new_font_from_memory(ctx, NULL, data, size, index, 0);
121
122 subres = pdf_dict_get(ctx, resources, PDF_NAME(Font));
123 if (!subres)
124 {
125 subres = pdf_new_dict(ctx, doc, 10);
126 pdf_dict_put_drop(ctx, resources, PDF_NAME(Font), subres);
127 }
128
129 ref = pdf_add_cjk_font(ctx, doc, font, ordering, wmode, serif);
130 pdf_dict_puts(ctx, subres, name, ref);
131 pdf_drop_obj(ctx, ref);
132
133 fz_drop_font(ctx, font);
134 }
135
136 static void add_image_res(pdf_obj *resources, char *name, char *path)
137 {
138 fz_image *image;
139 pdf_obj *subres, *ref;
140
141 image = fz_new_image_from_file(ctx, path);
142
143 subres = pdf_dict_get(ctx, resources, PDF_NAME(XObject));
144 if (!subres)
145 {
146 subres = pdf_new_dict(ctx, doc, 10);
147 pdf_dict_put_drop(ctx, resources, PDF_NAME(XObject), subres);
148 }
149
150 ref = pdf_add_image(ctx, doc, image);
151 pdf_dict_puts(ctx, subres, name, ref);
152 pdf_drop_obj(ctx, ref);
153
154 fz_drop_image(ctx, image);
155 }
156
157 /*
158 The input is a raw content stream, with commands embedded in comments:
159
160 %%MediaBox LLX LLY URX URY
161 %%Rotate Angle
162 %%Font Name Filename (or base 14 font name) [Encoding (Latin, Greek or Cyrillic)]
163 %%CJKFont Name Language WMode Style (Language=zh-Hant|zh-Hans|ja|ko, WMode=H|V, Style=serif|sans)
164 %%Image Name Filename
165 */
166 static void create_page(char *input)
167 {
168 fz_rect mediabox = { 0, 0, 595, 842 };
169 int rotate = 0;
170
171 char line[4096];
172 char *s, *p;
173 fz_stream *stm = NULL;
174
175 fz_buffer *contents = NULL;
176 pdf_obj *resources;
177 pdf_obj *page = NULL;
178
179 resources = pdf_new_dict(ctx, doc, 2);
180
181 fz_var(stm);
182 fz_var(page);
183 fz_var(contents);
184
185 fz_try(ctx)
186 {
187 contents = fz_new_buffer(ctx, 1024);
188
189 stm = fz_open_file(ctx, input);
190 while (fz_read_line(ctx, stm, line, sizeof line))
191 {
192 if (line[0] == '%' && line[1] == '%')
193 {
194 p = line;
195 s = fz_strsep(&p, " ");
196 if (!strcmp(s, "%%MediaBox"))
197 {
198 mediabox.x0 = fz_atoi(fz_strsep(&p, " "));
199 mediabox.y0 = fz_atoi(fz_strsep(&p, " "));
200 mediabox.x1 = fz_atoi(fz_strsep(&p, " "));
201 mediabox.y1 = fz_atoi(fz_strsep(&p, " "));
202 }
203 else if (!strcmp(s, "%%Rotate"))
204 {
205 rotate = fz_atoi(fz_strsep(&p, " "));
206 }
207 else if (!strcmp(s, "%%Font"))
208 {
209 char *name = fz_strsep(&p, " ");
210 char *path = fz_strsep(&p, " ");
211 char *enc = fz_strsep(&p, " ");
212 if (!name || !path)
213 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Font directive missing arguments");
214 add_font_res(resources, name, path, enc);
215 }
216 else if (!strcmp(s, "%%CJKFont"))
217 {
218 char *name = fz_strsep(&p, " ");
219 char *lang = fz_strsep(&p, " ");
220 char *wmode = fz_strsep(&p, " ");
221 char *style = fz_strsep(&p, " ");
222 if (!name || !lang)
223 fz_throw(ctx, FZ_ERROR_ARGUMENT, "CJKFont directive missing arguments");
224 add_cjkfont_res(resources, name, lang, wmode, style);
225 }
226 else if (!strcmp(s, "%%Image"))
227 {
228 char *name = fz_strsep(&p, " ");
229 char *path = fz_strsep(&p, " ");
230 if (!name || !path)
231 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Image directive missing arguments");
232 add_image_res(resources, name, path);
233 }
234 }
235 else
236 {
237 fz_append_string(ctx, contents, line);
238 fz_append_byte(ctx, contents, '\n');
239 }
240 }
241
242 page = pdf_add_page(ctx, doc, mediabox, rotate, resources, contents);
243 pdf_insert_page(ctx, doc, -1, page);
244 }
245 fz_always(ctx)
246 {
247 fz_drop_stream(ctx, stm);
248 pdf_drop_obj(ctx, page);
249 fz_drop_buffer(ctx, contents);
250 pdf_drop_obj(ctx, resources);
251 }
252 fz_catch(ctx)
253 fz_rethrow(ctx);
254 }
255
256 int pdfcreate_main(int argc, char **argv)
257 {
258 pdf_write_options opts = pdf_default_write_options;
259 char *output = "out.pdf";
260 char *flags = "compress";
261 int i, c;
262 int error = 0;
263
264 while ((c = fz_getopt(argc, argv, "o:O:")) != -1)
265 {
266 switch (c)
267 {
268 case 'o': output = fz_optpath(fz_optarg); break;
269 case 'O': flags = fz_optarg; break;
270 default: return usage();
271 }
272 }
273
274 if (fz_optind == argc)
275 return usage();
276
277 ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
278 if (!ctx)
279 {
280 fprintf(stderr, "cannot initialise context\n");
281 exit(1);
282 }
283
284 pdf_parse_write_options(ctx, &opts, flags);
285
286 fz_var(doc);
287
288 fz_try(ctx)
289 {
290 doc = pdf_create_document(ctx);
291
292 for (i = fz_optind; i < argc; ++i)
293 create_page(argv[i]);
294
295 pdf_save_document(ctx, doc, output, &opts);
296 }
297 fz_always(ctx)
298 pdf_drop_document(ctx, doc);
299 fz_catch(ctx)
300 {
301 fz_report_error(ctx);
302 error = 1;
303 }
304
305 fz_flush_warnings(ctx);
306 fz_drop_context(ctx);
307 return error;
308 }