/*! * readHeaderJp2k() * * Input: filename * &w (<optional return>) * &h (<optional return>) * &bps (<optional return>, bits/sample) * &spp (<optional return>, samples/pixel) * Return: 0 if OK, 1 on error */ l_int32 readHeaderJp2k(const char *filename, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp) { l_int32 ret; FILE *fp; PROCNAME("readHeaderJp2k"); if (pw) *pw = 0; if (ph) *ph = 0; if (pbps) *pbps = 0; if (pspp) *pspp = 0; if (!filename) return ERROR_INT("filename not defined", procName, 1); if ((fp = fopenReadStream(filename)) == NULL) return ERROR_INT("image file not found", procName, 1); ret = freadHeaderJp2k(fp, pw, ph, pbps, pspp); fclose(fp); return ret; }
// TODO(jbreiden) I hear that you can pull the flate stream out // of a PNG file and, by mentioning the predictor in the PDF object, // make most of them work without transcoding. If so that's a big win // versus what we do now. Try it out. bool TessPDFRenderer::fileToPDFObj(char *filename, long int objnum, char **pdf_object, long int *pdf_object_size) { char b1[kBasicBufSize]; char b2[kBasicBufSize]; if (!pdf_object_size || !pdf_object) return false; *pdf_object = NULL; *pdf_object_size = 0; if (!filename) return false; FILE *fp = fopen(filename, "rb"); if (!fp) return false; const char *filter; int bps, spp, w, h; int cmyk = false; int format; findFileFormatStream(fp, &format); switch(format) { case IFF_JFIF_JPEG: freadHeaderJpeg(fp, &w, &h, &spp, NULL, &cmyk); filter = "/DCTDecode"; break; case IFF_JP2: #if LIBLEPT_MINOR_VERSION == 70 && LIBLEPT_MAJOR_VERSION <= 1 freadHeaderJp2k(fp, &w, &h, &spp); #else freadHeaderJp2k(fp, &w, &h, &bps, &spp); #endif filter = "/JPXDecode"; break; default: fclose(fp); return false; } const char *colorspace; switch (spp) { case 1: colorspace = "/DeviceGray"; break; case 3: colorspace = "/DeviceRGB"; break; case 4: if (cmyk) colorspace = "/DeviceCMYK"; else return false; break; default: return false; } fseek(fp, 0, SEEK_END); long int file_size = ftell(fp); fseek(fp, 0, SEEK_SET); // IMAGE snprintf(b1, sizeof(b1), "%ld 0 obj\n" "<<\n" " /Length %ld\n" " /Subtype /Image\n" " /ColorSpace %s\n" " /Width %d\n" " /Height %d\n" " /BitsPerComponent 8\n" " /Filter %s\n" ">>\n" "stream\n", objnum, file_size, colorspace, w, h, filter); size_t b1_len = strlen(b1); snprintf(b2, sizeof(b2), "\n" "endstream\n" "endobj\n"); size_t b2_len = strlen(b2); *pdf_object_size = b1_len + file_size + b2_len; *pdf_object = new char[*pdf_object_size]; if (!pdf_object) return false; memcpy(*pdf_object, b1, b1_len); if (static_cast<int>(fread(*pdf_object + b1_len, 1, file_size, fp)) != file_size) { delete[] pdf_object; return false; } memcpy(*pdf_object + b1_len + file_size, b2, b2_len); fclose(fp); return true; }
/*! * pixReadStreamJp2k() * * Input: stream * reduction (scaling factor: 1, 2, 4, 8) * box (<optional> for extracting a subregion), can be null * hint (a bitwise OR of L_JP2K_* values; 0 for default) * debug (output callback messages, etc) * Return: pix (8 or 32 bpp), or null on error * * Notes: * (1) See pixReadJp2k() for usage. */ PIX * pixReadStreamJp2k(FILE *fp, l_uint32 reduction, BOX *box, l_int32 hint, l_int32 debug) { const char *opjVersion; l_int32 i, j, index, bx, by, bw, bh, val, rval, gval, bval, aval; l_int32 w, h, wpl, bps, spp, xres, yres, reduce, prec, colorspace; l_uint32 pixel; l_uint32 *data, *line; opj_dparameters_t parameters; /* decompression parameters */ opj_image_t *image = NULL; opj_codec_t *l_codec = NULL; /* handle to decompressor */ opj_stream_t *l_stream = NULL; /* opj stream */ PIX *pix = NULL; PROCNAME("pixReadStreamJp2k"); if (!fp) return (PIX *)ERROR_PTR("fp not defined", procName, NULL); opjVersion = opj_version(); if (opjVersion[0] != '2') { L_ERROR("version is %s; must be 2.0 or higher\n", procName, opjVersion); return NULL; } if ((opjVersion[2] - 0x30) != OPJ_VERSION_MINOR) { L_ERROR("version %s: differs from minor = %d\n", procName, opjVersion, OPJ_VERSION_MINOR); return NULL; } /* Get the resolution and the bits/sample */ rewind(fp); fgetJp2kResolution(fp, &xres, &yres); freadHeaderJp2k(fp, NULL, NULL, &bps, NULL); rewind(fp); if (bps > 8) { L_ERROR("found %d bps; can only handle 8 bps\n", procName, bps); return NULL; } /* Set decoding parameters to default values */ opj_set_default_decoder_parameters(¶meters); /* Find and set the reduce parameter, which is log2(reduction). * Valid reductions are powers of 2, and are determined when the * compressed string is made. A request for an invalid reduction * will cause an error in opj_read_header(), and no image will * be returned. */ for (reduce = 0; (1L << reduce) < reduction; reduce++) { } if ((1L << reduce) != reduction) { L_ERROR("invalid reduction %d; not power of 2\n", procName, reduction); return NULL; } parameters.cp_reduce = reduce; /* Open decompression 'stream'. In 2.0, we could call this: * opj_stream_create_default_file_stream(fp, 1) * but the file stream interface was removed in 2.1. */ if ((l_stream = opjCreateStream(fp, 1)) == NULL) { L_ERROR("failed to open the stream\n", procName); return NULL; } if ((l_codec = opj_create_decompress(OPJ_CODEC_JP2)) == NULL) { L_ERROR("failed to make the codec\n", procName); opj_stream_destroy(l_stream); return NULL; } /* Catch and report events using callbacks */ if (debug) { opj_set_info_handler(l_codec, info_callback, NULL); opj_set_warning_handler(l_codec, warning_callback, NULL); opj_set_error_handler(l_codec, error_callback, NULL); } /* Setup the decoding parameters using user parameters */ if (!opj_setup_decoder(l_codec, ¶meters)){ L_ERROR("failed to set up decoder\n", procName); opj_stream_destroy(l_stream); opj_destroy_codec(l_codec); return NULL; } /* Read the main header of the codestream and, if necessary, * the JP2 boxes */ if(!opj_read_header(l_stream, l_codec, &image)){ L_ERROR("failed to read the header\n", procName); opj_stream_destroy(l_stream); opj_destroy_codec(l_codec); opj_image_destroy(image); return NULL; } /* Set up to decode a rectangular region */ if (box) { boxGetGeometry(box, &bx, &by, &bw, &bh); if (!opj_set_decode_area(l_codec, image, bx, by, bx + bw, by + bh)) { L_ERROR("failed to set the region for decoding\n", procName); opj_stream_destroy(l_stream); opj_destroy_codec(l_codec); opj_image_destroy(image); return NULL; } } /* Get the decoded image */ if (!(opj_decode(l_codec, l_stream, image) && opj_end_decompress(l_codec, l_stream))) { L_ERROR("failed to decode the image\n", procName); opj_destroy_codec(l_codec); opj_stream_destroy(l_stream); opj_image_destroy(image); return NULL; } /* Close the byte stream */ opj_stream_destroy(l_stream); /* Get the image parameters */ spp = image->numcomps; w = image->comps[0].w; h = image->comps[0].h; prec = image->comps[0].prec; if (prec != bps) L_WARNING("precision %d != bps %d!\n", procName, prec, bps); if (debug) { L_INFO("w = %d, h = %d, bps = %d, spp = %d\n", procName, w, h, bps, spp); colorspace = image->color_space; if (colorspace == OPJ_CLRSPC_SRGB) L_INFO("colorspace is sRGB\n", procName); else if (colorspace == OPJ_CLRSPC_GRAY) L_INFO("colorspace is grayscale\n", procName); else if (colorspace == OPJ_CLRSPC_SYCC) L_INFO("colorspace is YUV\n", procName); } /* Free the codec structure */ if (l_codec) opj_destroy_codec(l_codec); /* Convert the image to a pix */ if (spp == 1) pix = pixCreate(w, h, 8); else pix = pixCreate(w, h, 32); pixSetResolution(pix, xres, yres); data = pixGetData(pix); wpl = pixGetWpl(pix); index = 0; if (spp == 1) { for (i = 0; i < h; i++) { line = data + i * wpl; for (j = 0; j < w; j++) { val = image->comps[0].data[index]; SET_DATA_BYTE(line, j, val); index++; } } } else if (spp == 2) { /* convert to RGBA */ for (i = 0; i < h; i++) { line = data + i * wpl; for (j = 0; j < w; j++) { val = image->comps[0].data[index]; aval = image->comps[1].data[index]; composeRGBAPixel(val, val, val, aval, &pixel); line[j] = pixel; index++; } } } else if (spp >= 3) { for (i = 0; i < h; i++) { line = data + i * wpl; for (j = 0; j < w; j++) { rval = image->comps[0].data[index]; gval = image->comps[1].data[index]; bval = image->comps[2].data[index]; if (spp == 3) { composeRGBPixel(rval, gval, bval, &pixel); } else { /* spp == 4 */ aval = image->comps[3].data[index]; composeRGBAPixel(rval, gval, bval, aval, &pixel); } line[j] = pixel; index++; } } } /* Free the opj image data structure */ opj_image_destroy(image); return pix; }