| 1 | #include "mupdf/fitz.h" | 
|---|
| 2 |  | 
|---|
| 3 | #include <string.h> | 
|---|
| 4 |  | 
|---|
| 5 | /* Return non-null terminated pointers to key/value entries in comma separated | 
|---|
| 6 | * option string. A plain key has the default value 'yes'. Use strncmp to compare | 
|---|
| 7 | * key/value strings. */ | 
|---|
| 8 | static const char * | 
|---|
| 9 | fz_get_option(fz_context *ctx, const char **key, const char **val, const char *opts) | 
|---|
| 10 | { | 
|---|
| 11 | if (!opts || *opts == 0) | 
|---|
| 12 | return NULL; | 
|---|
| 13 |  | 
|---|
| 14 | if (*opts == ',') | 
|---|
| 15 | ++opts; | 
|---|
| 16 |  | 
|---|
| 17 | *key = opts; | 
|---|
| 18 | while (*opts != 0 && *opts != ',' && *opts != '=') | 
|---|
| 19 | ++opts; | 
|---|
| 20 |  | 
|---|
| 21 | if (*opts == '=') | 
|---|
| 22 | { | 
|---|
| 23 | *val = ++opts; | 
|---|
| 24 | while (*opts != 0 && *opts != ',') | 
|---|
| 25 | ++opts; | 
|---|
| 26 | } | 
|---|
| 27 | else | 
|---|
| 28 | { | 
|---|
| 29 | *val = "yes"; | 
|---|
| 30 | } | 
|---|
| 31 |  | 
|---|
| 32 | return opts; | 
|---|
| 33 | } | 
|---|
| 34 |  | 
|---|
| 35 | int | 
|---|
| 36 | fz_has_option(fz_context *ctx, const char *opts, const char *key, const char **val) | 
|---|
| 37 | { | 
|---|
| 38 | const char *straw; | 
|---|
| 39 | size_t n = strlen(key); | 
|---|
| 40 | while ((opts = fz_get_option(ctx, &straw, val, opts))) | 
|---|
| 41 | if (!strncmp(straw, key, n) && (straw[n] == '=' || straw[n] == ',' || straw[n] == 0)) | 
|---|
| 42 | return 1; | 
|---|
| 43 | return 0; | 
|---|
| 44 | } | 
|---|
| 45 |  | 
|---|
| 46 | int | 
|---|
| 47 | fz_option_eq(const char *a, const char *b) | 
|---|
| 48 | { | 
|---|
| 49 | size_t n = strlen(b); | 
|---|
| 50 | return !strncmp(a, b, n) && (a[n] == ',' || a[n] == 0); | 
|---|
| 51 | } | 
|---|
| 52 |  | 
|---|
| 53 | int | 
|---|
| 54 | fz_copy_option(fz_context *ctx, const char *val, char *dest, size_t maxlen) | 
|---|
| 55 | { | 
|---|
| 56 | const char *e = val; | 
|---|
| 57 | size_t len, len2; | 
|---|
| 58 |  | 
|---|
| 59 | if (val == NULL) { | 
|---|
| 60 | if (maxlen) | 
|---|
| 61 | *dest = 0; | 
|---|
| 62 | return 0; | 
|---|
| 63 | } | 
|---|
| 64 |  | 
|---|
| 65 | while (*e != ',' && *e != 0) | 
|---|
| 66 | e++; | 
|---|
| 67 |  | 
|---|
| 68 | len = e-val; | 
|---|
| 69 | len2 = len+1; /* Allow for terminator */ | 
|---|
| 70 | if (len > maxlen) | 
|---|
| 71 | len = maxlen; | 
|---|
| 72 | memcpy(dest, val, len); | 
|---|
| 73 | if (len < maxlen) | 
|---|
| 74 | memset(dest+len, 0, maxlen-len); | 
|---|
| 75 |  | 
|---|
| 76 | return len2 >= maxlen ? len2 - maxlen : 0; | 
|---|
| 77 | } | 
|---|
| 78 |  | 
|---|
| 79 | /* | 
|---|
| 80 | Internal function to allocate a | 
|---|
| 81 | block for a derived document_writer structure, with the base | 
|---|
| 82 | structure's function pointers populated correctly, and the extra | 
|---|
| 83 | space zero initialised. | 
|---|
| 84 | */ | 
|---|
| 85 | fz_document_writer *fz_new_document_writer_of_size(fz_context *ctx, size_t size, fz_document_writer_begin_page_fn *begin_page, | 
|---|
| 86 | fz_document_writer_end_page_fn *end_page, fz_document_writer_close_writer_fn *close, fz_document_writer_drop_writer_fn *drop) | 
|---|
| 87 | { | 
|---|
| 88 | fz_document_writer *wri = Memento_label(fz_calloc(ctx, 1, size), "fz_document_writer"); | 
|---|
| 89 |  | 
|---|
| 90 | wri->begin_page = begin_page; | 
|---|
| 91 | wri->end_page = end_page; | 
|---|
| 92 | wri->close_writer = close; | 
|---|
| 93 | wri->drop_writer = drop; | 
|---|
| 94 |  | 
|---|
| 95 | return wri; | 
|---|
| 96 | } | 
|---|
| 97 |  | 
|---|
| 98 | fz_document_writer *fz_new_png_pixmap_writer(fz_context *ctx, const char *path, const char *options) | 
|---|
| 99 | { | 
|---|
| 100 | return fz_new_pixmap_writer(ctx, path, options, "out-%04d.png", 0, fz_save_pixmap_as_png); | 
|---|
| 101 | } | 
|---|
| 102 |  | 
|---|
| 103 | fz_document_writer *fz_new_pam_pixmap_writer(fz_context *ctx, const char *path, const char *options) | 
|---|
| 104 | { | 
|---|
| 105 | return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pam", 0, fz_save_pixmap_as_pam); | 
|---|
| 106 | } | 
|---|
| 107 |  | 
|---|
| 108 | fz_document_writer *fz_new_pnm_pixmap_writer(fz_context *ctx, const char *path, const char *options) | 
|---|
| 109 | { | 
|---|
| 110 | return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pnm", 0, fz_save_pixmap_as_pnm); | 
|---|
| 111 | } | 
|---|
| 112 |  | 
|---|
| 113 | fz_document_writer *fz_new_pgm_pixmap_writer(fz_context *ctx, const char *path, const char *options) | 
|---|
| 114 | { | 
|---|
| 115 | return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pgm", 1, fz_save_pixmap_as_pnm); | 
|---|
| 116 | } | 
|---|
| 117 |  | 
|---|
| 118 | fz_document_writer *fz_new_ppm_pixmap_writer(fz_context *ctx, const char *path, const char *options) | 
|---|
| 119 | { | 
|---|
| 120 | return fz_new_pixmap_writer(ctx, path, options, "out-%04d.ppm", 3, fz_save_pixmap_as_pnm); | 
|---|
| 121 | } | 
|---|
| 122 |  | 
|---|
| 123 | fz_document_writer *fz_new_pbm_pixmap_writer(fz_context *ctx, const char *path, const char *options) | 
|---|
| 124 | { | 
|---|
| 125 | return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pbm", 1, fz_save_pixmap_as_pbm); | 
|---|
| 126 | } | 
|---|
| 127 |  | 
|---|
| 128 | fz_document_writer *fz_new_pkm_pixmap_writer(fz_context *ctx, const char *path, const char *options) | 
|---|
| 129 | { | 
|---|
| 130 | return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pkm", 4, fz_save_pixmap_as_pkm); | 
|---|
| 131 | } | 
|---|
| 132 |  | 
|---|
| 133 | /* | 
|---|
| 134 | Create a new fz_document_writer, for a | 
|---|
| 135 | file of the given type. | 
|---|
| 136 |  | 
|---|
| 137 | path: The document name to write (or NULL for default) | 
|---|
| 138 |  | 
|---|
| 139 | format: Which format to write (currently cbz, html, pdf, pam, pbm, | 
|---|
| 140 | pgm, pkm, png, ppm, pnm, svg, text, xhtml) | 
|---|
| 141 |  | 
|---|
| 142 | options: NULL, or pointer to comma separated string to control | 
|---|
| 143 | file generation. | 
|---|
| 144 | */ | 
|---|
| 145 | fz_document_writer * | 
|---|
| 146 | fz_new_document_writer(fz_context *ctx, const char *path, const char *format, const char *options) | 
|---|
| 147 | { | 
|---|
| 148 | if (!format) | 
|---|
| 149 | { | 
|---|
| 150 | format = strrchr(path, '.'); | 
|---|
| 151 | if (!format) | 
|---|
| 152 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot detect document format"); | 
|---|
| 153 | format += 1; /* skip the '.' */ | 
|---|
| 154 | } | 
|---|
| 155 |  | 
|---|
| 156 | if (!fz_strcasecmp(format, "cbz")) | 
|---|
| 157 | return fz_new_cbz_writer(ctx, path, options); | 
|---|
| 158 | #if FZ_ENABLE_PDF | 
|---|
| 159 | if (!fz_strcasecmp(format, "pdf")) | 
|---|
| 160 | return fz_new_pdf_writer(ctx, path, options); | 
|---|
| 161 | #endif | 
|---|
| 162 | if (!fz_strcasecmp(format, "svg")) | 
|---|
| 163 | return fz_new_svg_writer(ctx, path, options); | 
|---|
| 164 |  | 
|---|
| 165 | if (!fz_strcasecmp(format, "png")) | 
|---|
| 166 | return fz_new_png_pixmap_writer(ctx, path, options); | 
|---|
| 167 | if (!fz_strcasecmp(format, "pam")) | 
|---|
| 168 | return fz_new_pam_pixmap_writer(ctx, path, options); | 
|---|
| 169 | if (!fz_strcasecmp(format, "pnm")) | 
|---|
| 170 | return fz_new_pnm_pixmap_writer(ctx, path, options); | 
|---|
| 171 | if (!fz_strcasecmp(format, "pgm")) | 
|---|
| 172 | return fz_new_pgm_pixmap_writer(ctx, path, options); | 
|---|
| 173 | if (!fz_strcasecmp(format, "ppm")) | 
|---|
| 174 | return fz_new_ppm_pixmap_writer(ctx, path, options); | 
|---|
| 175 | if (!fz_strcasecmp(format, "pbm")) | 
|---|
| 176 | return fz_new_pbm_pixmap_writer(ctx, path, options); | 
|---|
| 177 | if (!fz_strcasecmp(format, "pkm")) | 
|---|
| 178 | return fz_new_pkm_pixmap_writer(ctx, path, options); | 
|---|
| 179 |  | 
|---|
| 180 | if (!fz_strcasecmp(format, "pcl")) | 
|---|
| 181 | return fz_new_pcl_writer(ctx, path, options); | 
|---|
| 182 | if (!fz_strcasecmp(format, "pclm")) | 
|---|
| 183 | return fz_new_pclm_writer(ctx, path, options); | 
|---|
| 184 | if (!fz_strcasecmp(format, "ps")) | 
|---|
| 185 | return fz_new_ps_writer(ctx, path, options); | 
|---|
| 186 | if (!fz_strcasecmp(format, "pwg")) | 
|---|
| 187 | return fz_new_pwg_writer(ctx, path, options); | 
|---|
| 188 |  | 
|---|
| 189 | if (!fz_strcasecmp(format, "txt") || !fz_strcasecmp(format, "text")) | 
|---|
| 190 | return fz_new_text_writer(ctx, "text", path, options); | 
|---|
| 191 | if (!fz_strcasecmp(format, "html")) | 
|---|
| 192 | return fz_new_text_writer(ctx, format, path, options); | 
|---|
| 193 | if (!fz_strcasecmp(format, "xhtml")) | 
|---|
| 194 | return fz_new_text_writer(ctx, format, path, options); | 
|---|
| 195 | if (!fz_strcasecmp(format, "stext")) | 
|---|
| 196 | return fz_new_text_writer(ctx, format, path, options); | 
|---|
| 197 |  | 
|---|
| 198 | fz_throw(ctx, FZ_ERROR_GENERIC, "unknown output document format: %s", format); | 
|---|
| 199 | } | 
|---|
| 200 |  | 
|---|
| 201 | /* | 
|---|
| 202 | Called to end the process of writing | 
|---|
| 203 | pages to a document. | 
|---|
| 204 |  | 
|---|
| 205 | This writes any file level trailers required. After this | 
|---|
| 206 | completes successfully the file is up to date and complete. | 
|---|
| 207 | */ | 
|---|
| 208 | void | 
|---|
| 209 | fz_close_document_writer(fz_context *ctx, fz_document_writer *wri) | 
|---|
| 210 | { | 
|---|
| 211 | if (wri->close_writer) | 
|---|
| 212 | wri->close_writer(ctx, wri); | 
|---|
| 213 | wri->close_writer = NULL; | 
|---|
| 214 | } | 
|---|
| 215 |  | 
|---|
| 216 | /* | 
|---|
| 217 | Called to discard a fz_document_writer. | 
|---|
| 218 | This may be called at any time during the process to release all | 
|---|
| 219 | the resources owned by the writer. | 
|---|
| 220 |  | 
|---|
| 221 | Calling drop without having previously called close may leave | 
|---|
| 222 | the file in an inconsistent state. | 
|---|
| 223 | */ | 
|---|
| 224 | void | 
|---|
| 225 | fz_drop_document_writer(fz_context *ctx, fz_document_writer *wri) | 
|---|
| 226 | { | 
|---|
| 227 | if (!wri) | 
|---|
| 228 | return; | 
|---|
| 229 |  | 
|---|
| 230 | if (wri->close_writer) | 
|---|
| 231 | fz_warn(ctx, "dropping unclosed document writer"); | 
|---|
| 232 | if (wri->drop_writer) | 
|---|
| 233 | wri->drop_writer(ctx, wri); | 
|---|
| 234 | if (wri->dev) | 
|---|
| 235 | fz_drop_device(ctx, wri->dev); | 
|---|
| 236 | fz_free(ctx, wri); | 
|---|
| 237 | } | 
|---|
| 238 |  | 
|---|
| 239 | /* | 
|---|
| 240 | Called to start the process of writing a page to | 
|---|
| 241 | a document. | 
|---|
| 242 |  | 
|---|
| 243 | mediabox: page size rectangle in points. | 
|---|
| 244 |  | 
|---|
| 245 | Returns a fz_device to write page contents to. | 
|---|
| 246 | */ | 
|---|
| 247 | fz_device * | 
|---|
| 248 | fz_begin_page(fz_context *ctx, fz_document_writer *wri, fz_rect mediabox) | 
|---|
| 249 | { | 
|---|
| 250 | if (!wri) | 
|---|
| 251 | return NULL; | 
|---|
| 252 | if (wri->dev) | 
|---|
| 253 | fz_throw(ctx, FZ_ERROR_GENERIC, "called begin page without ending the previous page"); | 
|---|
| 254 | wri->dev = wri->begin_page(ctx, wri, mediabox); | 
|---|
| 255 | return wri->dev; | 
|---|
| 256 | } | 
|---|
| 257 |  | 
|---|
| 258 | /* | 
|---|
| 259 | Called to end the process of writing a page to a | 
|---|
| 260 | document. | 
|---|
| 261 | */ | 
|---|
| 262 | void | 
|---|
| 263 | fz_end_page(fz_context *ctx, fz_document_writer *wri) | 
|---|
| 264 | { | 
|---|
| 265 | fz_device *dev; | 
|---|
| 266 |  | 
|---|
| 267 | if (!wri) | 
|---|
| 268 | return; | 
|---|
| 269 | dev = wri->dev; | 
|---|
| 270 | wri->dev = NULL; | 
|---|
| 271 | wri->end_page(ctx, wri, dev); | 
|---|
| 272 | } | 
|---|
| 273 |  | 
|---|