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