Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/jbig2dec/jbig2_page.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /* Copyright (C) 2001-2023 Artifex Software, Inc. | |
| 2 All Rights Reserved. | |
| 3 | |
| 4 This software is provided AS-IS with no warranty, either express or | |
| 5 implied. | |
| 6 | |
| 7 This software is distributed under license and may not be copied, | |
| 8 modified or distributed except as expressly authorized under the terms | |
| 9 of the license contained in the file LICENSE in this distribution. | |
| 10 | |
| 11 Refer to licensing information at http://www.artifex.com or contact | |
| 12 Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 13 CA 94129, USA, for further information. | |
| 14 */ | |
| 15 | |
| 16 /* | |
| 17 jbig2dec | |
| 18 */ | |
| 19 | |
| 20 #ifdef HAVE_CONFIG_H | |
| 21 #include "config.h" | |
| 22 #endif | |
| 23 #include "os_types.h" | |
| 24 | |
| 25 #include <stdlib.h> | |
| 26 | |
| 27 #ifdef OUTPUT_PBM | |
| 28 #include <stdio.h> | |
| 29 #endif | |
| 30 | |
| 31 #include "jbig2.h" | |
| 32 #include "jbig2_priv.h" | |
| 33 #include "jbig2_image.h" | |
| 34 #include "jbig2_page.h" | |
| 35 #include "jbig2_segment.h" | |
| 36 | |
| 37 /* dump the page struct info */ | |
| 38 static void | |
| 39 dump_page_info(Jbig2Ctx *ctx, Jbig2Segment *segment, Jbig2Page *page) | |
| 40 { | |
| 41 if (page->x_resolution == 0) { | |
| 42 jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "page %d image is %dx%d (unknown res)", page->number, page->width, page->height); | |
| 43 } else if (page->x_resolution == page->y_resolution) { | |
| 44 jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "page %d image is %dx%d (%d ppm)", page->number, page->width, page->height, page->x_resolution); | |
| 45 } else { | |
| 46 jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, | |
| 47 "page %d image is %dx%d (%dx%d ppm)", page->number, page->width, page->height, page->x_resolution, page->y_resolution); | |
| 48 } | |
| 49 if (page->striped) { | |
| 50 jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "\tmaximum stripe size: %d", page->stripe_size); | |
| 51 } | |
| 52 } | |
| 53 | |
| 54 /** | |
| 55 * jbig2_page_info: parse page info segment | |
| 56 * | |
| 57 * Parse the page info segment data and fill out a corresponding | |
| 58 * Jbig2Page struct and ready it for subsequent rendered data, | |
| 59 * including allocating an image buffer for the page (or the first stripe) | |
| 60 **/ | |
| 61 int | |
| 62 jbig2_page_info(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) | |
| 63 { | |
| 64 Jbig2Page *page, *pages; | |
| 65 | |
| 66 /* a new page info segment implies the previous page is finished */ | |
| 67 page = &(ctx->pages[ctx->current_page]); | |
| 68 if (page->number != 0 && (page->state == JBIG2_PAGE_NEW || page->state == JBIG2_PAGE_FREE)) { | |
| 69 page->state = JBIG2_PAGE_COMPLETE; | |
| 70 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unexpected page info segment, marking previous page finished"); | |
| 71 } | |
| 72 | |
| 73 /* find a free page */ | |
| 74 { | |
| 75 size_t index, j; | |
| 76 | |
| 77 index = ctx->current_page; | |
| 78 while (ctx->pages[index].state != JBIG2_PAGE_FREE) { | |
| 79 index++; | |
| 80 if (index >= ctx->max_page_index) { | |
| 81 /* grow the list */ | |
| 82 | |
| 83 if (ctx->max_page_index == UINT32_MAX) { | |
| 84 return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "too many pages in jbig2 image"); | |
| 85 } | |
| 86 else if (ctx->max_page_index > (UINT32_MAX >> 2)) { | |
| 87 ctx->max_page_index = UINT32_MAX; | |
| 88 } | |
| 89 | |
| 90 pages = jbig2_renew(ctx, ctx->pages, Jbig2Page, (ctx->max_page_index <<= 2)); | |
| 91 if (pages == NULL) { | |
| 92 return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to reallocate pages"); | |
| 93 } | |
| 94 ctx->pages = pages; | |
| 95 for (j = index; j < ctx->max_page_index; j++) { | |
| 96 ctx->pages[j].state = JBIG2_PAGE_FREE; | |
| 97 ctx->pages[j].number = 0; | |
| 98 ctx->pages[j].image = NULL; | |
| 99 } | |
| 100 } | |
| 101 } | |
| 102 page = &(ctx->pages[index]); | |
| 103 ctx->current_page = index; | |
| 104 page->state = JBIG2_PAGE_NEW; | |
| 105 page->number = segment->page_association; | |
| 106 } | |
| 107 | |
| 108 /* FIXME: would be nice if we tried to work around this */ | |
| 109 if (segment->data_length < 19) { | |
| 110 return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); | |
| 111 } | |
| 112 | |
| 113 /* 7.4.8.x */ | |
| 114 page->width = jbig2_get_uint32(segment_data); | |
| 115 page->height = jbig2_get_uint32(segment_data + 4); | |
| 116 | |
| 117 page->x_resolution = jbig2_get_uint32(segment_data + 8); | |
| 118 page->y_resolution = jbig2_get_uint32(segment_data + 12); | |
| 119 page->flags = segment_data[16]; | |
| 120 /* Check for T.88 amendment 3 */ | |
| 121 if (page->flags & 0x80) | |
| 122 return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "page segment indicates use of color segments (NYI)"); | |
| 123 | |
| 124 /* 7.4.8.6 */ | |
| 125 { | |
| 126 int16_t striping = jbig2_get_int16(segment_data + 17); | |
| 127 | |
| 128 if (striping & 0x8000) { | |
| 129 page->striped = TRUE; | |
| 130 page->stripe_size = striping & 0x7FFF; | |
| 131 } else { | |
| 132 page->striped = FALSE; | |
| 133 page->stripe_size = 0; /* would page->height be better? */ | |
| 134 } | |
| 135 } | |
| 136 if (page->height == 0xFFFFFFFF && page->striped == FALSE) { | |
| 137 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "height is unspecified but page is not marked as striped, assuming striped with maximum strip size"); | |
| 138 page->striped = TRUE; | |
| 139 page->stripe_size = 0x7FFF; | |
| 140 } | |
| 141 page->end_row = 0; | |
| 142 | |
| 143 if (segment->data_length > 19) { | |
| 144 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "extra data in segment"); | |
| 145 } | |
| 146 | |
| 147 dump_page_info(ctx, segment, page); | |
| 148 | |
| 149 /* allocate an appropriate page image buffer */ | |
| 150 /* 7.4.8.2 */ | |
| 151 if (page->height == 0xFFFFFFFF) { | |
| 152 page->image = jbig2_image_new(ctx, page->width, page->stripe_size); | |
| 153 } else { | |
| 154 page->image = jbig2_image_new(ctx, page->width, page->height); | |
| 155 } | |
| 156 if (page->image == NULL) { | |
| 157 return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate buffer for page image"); | |
| 158 } else { | |
| 159 /* 8.2 (3) fill the page with the default pixel value */ | |
| 160 jbig2_image_clear(ctx, page->image, (page->flags & 4)); | |
| 161 jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, | |
| 162 "allocated %dx%d page image (%d bytes)", page->image->width, page->image->height, page->image->stride * page->image->height); | |
| 163 } | |
| 164 | |
| 165 return 0; | |
| 166 } | |
| 167 | |
| 168 /** | |
| 169 * jbig2_end_of_stripe: parse and implement an end of stripe segment | |
| 170 **/ | |
| 171 int | |
| 172 jbig2_end_of_stripe(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) | |
| 173 { | |
| 174 Jbig2Page *page = &ctx->pages[ctx->current_page]; | |
| 175 uint32_t end_row; | |
| 176 | |
| 177 if (segment->data_length < 4) | |
| 178 return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); | |
| 179 end_row = jbig2_get_uint32(segment_data); | |
| 180 if (end_row < page->end_row) { | |
| 181 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, | |
| 182 "end of stripe segment with non-positive end row advance (new end row %d vs current end row %d)", end_row, page->end_row); | |
| 183 } else { | |
| 184 jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "end of stripe: advancing end row from %u to %u", page->end_row, end_row); | |
| 185 } | |
| 186 | |
| 187 page->end_row = end_row; | |
| 188 | |
| 189 return 0; | |
| 190 } | |
| 191 | |
| 192 /** | |
| 193 * jbig2_complete_page: complete a page image | |
| 194 * | |
| 195 * called upon seeing an 'end of page' segment, this routine | |
| 196 * marks a page as completed so it can be returned. | |
| 197 * compositing will have already happened in the previous | |
| 198 * segment handlers. | |
| 199 **/ | |
| 200 int | |
| 201 jbig2_complete_page(Jbig2Ctx *ctx) | |
| 202 { | |
| 203 int code; | |
| 204 | |
| 205 /* check for unfinished segments */ | |
| 206 if (ctx->segment_index != ctx->n_segments) { | |
| 207 Jbig2Segment *segment = ctx->segments[ctx->segment_index]; | |
| 208 | |
| 209 /* Some versions of Xerox Workcentre generate PDF files | |
| 210 with the segment data length field of the last segment | |
| 211 set to -1. Try to cope with this here. */ | |
| 212 if ((segment->data_length & 0xffffffff) == 0xffffffff) { | |
| 213 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "file has an invalid segment data length; trying to decode using the available data"); | |
| 214 segment->data_length = ctx->buf_wr_ix - ctx->buf_rd_ix; | |
| 215 code = jbig2_parse_segment(ctx, segment, ctx->buf + ctx->buf_rd_ix); | |
| 216 ctx->buf_rd_ix += segment->data_length; | |
| 217 ctx->segment_index++; | |
| 218 if (code < 0) { | |
| 219 return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to parse segment"); | |
| 220 } | |
| 221 } | |
| 222 } | |
| 223 | |
| 224 /* ensure image exists before marking page as complete */ | |
| 225 if (ctx->pages[ctx->current_page].image == NULL) { | |
| 226 return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "page has no image, cannot be completed"); | |
| 227 } | |
| 228 | |
| 229 ctx->pages[ctx->current_page].state = JBIG2_PAGE_COMPLETE; | |
| 230 return 0; | |
| 231 } | |
| 232 | |
| 233 /** | |
| 234 * jbig2_end_of_page: parse and implement an end of page segment | |
| 235 **/ | |
| 236 int | |
| 237 jbig2_end_of_page(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) | |
| 238 { | |
| 239 uint32_t page_number = ctx->pages[ctx->current_page].number; | |
| 240 int code; | |
| 241 | |
| 242 if (segment->page_association != page_number) { | |
| 243 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, | |
| 244 "end of page marker for page %d doesn't match current page number %d", segment->page_association, page_number); | |
| 245 } | |
| 246 | |
| 247 jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "end of page %d", page_number); | |
| 248 | |
| 249 code = jbig2_complete_page(ctx); | |
| 250 if (code < 0) | |
| 251 return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to complete page"); | |
| 252 | |
| 253 #ifdef OUTPUT_PBM | |
| 254 code = jbig2_image_write_pbm(ctx->pages[ctx->current_page].image, stdout); | |
| 255 if (code < 0) | |
| 256 return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to write page image"); | |
| 257 #endif | |
| 258 | |
| 259 return 0; | |
| 260 } | |
| 261 | |
| 262 /** | |
| 263 * jbig2_add_page_result: composite a decoding result onto a page | |
| 264 * | |
| 265 * this is called to add the results of segment decode (when it | |
| 266 * is an image) to a page image buffer | |
| 267 **/ | |
| 268 int | |
| 269 jbig2_page_add_result(Jbig2Ctx *ctx, Jbig2Page *page, Jbig2Image *image, uint32_t x, uint32_t y, Jbig2ComposeOp op) | |
| 270 { | |
| 271 int code; | |
| 272 | |
| 273 if (x > INT32_MAX || y > INT32_MAX) | |
| 274 return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "unsupported image coordinates"); | |
| 275 | |
| 276 /* ensure image exists first */ | |
| 277 if (page->image == NULL) | |
| 278 return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "page info possibly missing, no image defined"); | |
| 279 | |
| 280 /* grow the page to accommodate a new stripe if necessary */ | |
| 281 if (page->striped && page->height == 0xFFFFFFFF) { | |
| 282 uint32_t new_height; | |
| 283 | |
| 284 if (y > UINT32_MAX - image->height) | |
| 285 return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "adding image at coordinate would grow page out of bounds"); | |
| 286 new_height = y + image->height; | |
| 287 | |
| 288 if (page->image->height < new_height) { | |
| 289 Jbig2Image *resized_image = NULL; | |
| 290 | |
| 291 jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, JBIG2_UNKNOWN_SEGMENT_NUMBER, "growing page buffer to %u rows to accommodate new stripe", new_height); | |
| 292 resized_image = jbig2_image_resize(ctx, page->image, page->image->width, new_height, page->flags & 4); | |
| 293 if (resized_image == NULL) { | |
| 294 return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "unable to resize image to accommodate new stripe"); | |
| 295 } | |
| 296 page->image = resized_image; | |
| 297 } | |
| 298 } | |
| 299 | |
| 300 code = jbig2_image_compose(ctx, page->image, image, x, y, op); | |
| 301 if (code < 0) | |
| 302 return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to compose image with page"); | |
| 303 | |
| 304 return 0; | |
| 305 } | |
| 306 | |
| 307 /** | |
| 308 * jbig2_get_page: return the next available page image buffer | |
| 309 * | |
| 310 * the client can call this at any time to check if any pages | |
| 311 * have been decoded. If so, it returns the first available | |
| 312 * one. The client should then call jbig2_release_page() when | |
| 313 * it no longer needs to refer to the image buffer. | |
| 314 * | |
| 315 * since this is a public routine for the library clients, we | |
| 316 * return an image structure pointer, even though the function | |
| 317 * name refers to a page; the page structure is private. | |
| 318 **/ | |
| 319 Jbig2Image * | |
| 320 jbig2_page_out(Jbig2Ctx *ctx) | |
| 321 { | |
| 322 uint32_t index; | |
| 323 | |
| 324 /* search for a completed page */ | |
| 325 for (index = 0; index < ctx->max_page_index; index++) { | |
| 326 if (ctx->pages[index].state == JBIG2_PAGE_COMPLETE) { | |
| 327 Jbig2Image *img = ctx->pages[index].image; | |
| 328 uint32_t page_number = ctx->pages[index].number; | |
| 329 | |
| 330 if (img == NULL) { | |
| 331 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "page %d returned with no associated image", page_number); | |
| 332 continue; | |
| 333 } | |
| 334 | |
| 335 ctx->pages[index].state = JBIG2_PAGE_RETURNED; | |
| 336 jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, JBIG2_UNKNOWN_SEGMENT_NUMBER, "page %d returned to the client", page_number); | |
| 337 return jbig2_image_reference(ctx, img); | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 /* no pages available */ | |
| 342 return NULL; | |
| 343 } | |
| 344 | |
| 345 /** | |
| 346 * jbig2_release_page: tell the library a page can be freed | |
| 347 **/ | |
| 348 void | |
| 349 jbig2_release_page(Jbig2Ctx *ctx, Jbig2Image *image) | |
| 350 { | |
| 351 uint32_t index; | |
| 352 | |
| 353 if (image == NULL) | |
| 354 return; | |
| 355 | |
| 356 /* find the matching page struct and mark it released */ | |
| 357 for (index = 0; index < ctx->max_page_index; index++) { | |
| 358 if (ctx->pages[index].image == image) { | |
| 359 jbig2_image_release(ctx, image); | |
| 360 ctx->pages[index].state = JBIG2_PAGE_RELEASED; | |
| 361 jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, JBIG2_UNKNOWN_SEGMENT_NUMBER, "page %d released by the client", ctx->pages[index].number); | |
| 362 return; | |
| 363 } | |
| 364 } | |
| 365 | |
| 366 /* no matching pages */ | |
| 367 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to release unknown page"); | |
| 368 } |
