public BufferedImage extract() throws DjatokaException { boolean useRegion = false; int left = 0; int top = 0; int width = 50; int height = 50; boolean useleftDouble = false; Double leftDouble = 0.0; boolean usetopDouble = false; Double topDouble = 0.0; boolean usewidthDouble = false; Double widthDouble = 0.0; boolean useheightDouble = false; Double heightDouble = 0.0; if (params.getRegion() != null) { StringTokenizer st = new StringTokenizer(params.getRegion(), "{},"); String token; // top if ((token = st.nextToken()).contains(".")) { topDouble = Double.parseDouble(token); usetopDouble = true; } else top = Integer.parseInt(token); // left if ((token = st.nextToken()).contains(".")) { leftDouble = Double.parseDouble(token); useleftDouble = true; } else left = Integer.parseInt(token); // height if ((token = st.nextToken()).contains(".")) { heightDouble = Double.parseDouble(token); useheightDouble = true; } else height = Integer.parseInt(token); // width if ((token = st.nextToken()).contains(".")) { widthDouble = Double.parseDouble(token); usewidthDouble = true; } else width = Integer.parseInt(token); useRegion = true; } try { if (is != null) { File f = File.createTempFile("tmp", ".jp2"); f.deleteOnExit(); FileOutputStream fos = new FileOutputStream(f); sourceFile = f.getAbsolutePath(); IOUtils.copyStream(is, fos); is.close(); fos.close(); } } catch (IOException e) { throw new DjatokaException(e); } try { Jp2_source inputSource = new Jp2_source(); Kdu_compressed_source input = null; Jp2_family_src jp2_family_in = new Jp2_family_src(); Jp2_locator loc = new Jp2_locator(); jp2_family_in.Open(sourceFile, true); inputSource.Open(jp2_family_in, loc); inputSource.Read_header(); input = inputSource; Kdu_codestream codestream = new Kdu_codestream(); codestream.Create(input); Kdu_channel_mapping channels = new Kdu_channel_mapping(); if (inputSource.Exists()) channels.Configure(inputSource, false); else channels.Configure(codestream); int ref_component = channels.Get_source_component(0); Kdu_coords ref_expansion = getReferenceExpansion(ref_component, channels, codestream); Kdu_dims image_dims = new Kdu_dims(); codestream.Get_dims(ref_component, image_dims); Kdu_coords imageSize = image_dims.Access_size(); Kdu_coords imagePosition = image_dims.Access_pos(); if (useleftDouble) left = imagePosition.Get_x() + (int) Math.round(leftDouble * imageSize.Get_x()); if (usetopDouble) top = imagePosition.Get_y() + (int) Math.round(topDouble * imageSize.Get_y()); if (useheightDouble) height = (int) Math.round(heightDouble * imageSize.Get_y()); if (usewidthDouble) width = (int) Math.round(widthDouble * imageSize.Get_x()); if (useRegion) { imageSize.Set_x(width); imageSize.Set_y(height); imagePosition.Set_x(left); imagePosition.Set_y(top); } int reduce = 1 << params.getLevelReductionFactor(); imageSize.Set_x(imageSize.Get_x() * ref_expansion.Get_x()); imageSize.Set_y(imageSize.Get_y() * ref_expansion.Get_y()); imagePosition.Set_x( imagePosition.Get_x() * ref_expansion.Get_x() / reduce - ((ref_expansion.Get_x() / reduce - 1) / 2)); imagePosition.Set_y( imagePosition.Get_y() * ref_expansion.Get_y() / reduce - ((ref_expansion.Get_y() / reduce - 1) / 2)); Kdu_dims view_dims = new Kdu_dims(); view_dims.Assign(image_dims); view_dims.Access_size().Set_x(imageSize.Get_x()); view_dims.Access_size().Set_y(imageSize.Get_y()); int region_buf_size = imageSize.Get_x() * imageSize.Get_y(); int[] region_buf = new int[region_buf_size]; Kdu_region_decompressor decompressor = new Kdu_region_decompressor(); decompressor.Start( codestream, channels, -1, params.getLevelReductionFactor(), 16384, image_dims, ref_expansion, new Kdu_coords(1, 1), false, Kdu_global.KDU_WANT_OUTPUT_COMPONENTS); Kdu_dims new_region = new Kdu_dims(); Kdu_dims incomplete_region = new Kdu_dims(); Kdu_coords viewSize = view_dims.Access_size(); incomplete_region.Assign(image_dims); int[] imgBuffer = new int[viewSize.Get_x() * viewSize.Get_y()]; int[] kduBuffer = null; while (decompressor.Process( region_buf, image_dims.Access_pos(), 0, 0, region_buf_size, incomplete_region, new_region)) { Kdu_coords newOffset = new_region.Access_pos(); Kdu_coords newSize = new_region.Access_size(); newOffset.Subtract(view_dims.Access_pos()); kduBuffer = region_buf; int imgBuffereIdx = newOffset.Get_x() + newOffset.Get_y() * viewSize.Get_x(); int kduBufferIdx = 0; int xDiff = viewSize.Get_x() - newSize.Get_x(); for (int j = 0; j < newSize.Get_y(); j++, imgBuffereIdx += xDiff) { for (int i = 0; i < newSize.Get_x(); i++) { imgBuffer[imgBuffereIdx++] = kduBuffer[kduBufferIdx++]; } } } BufferedImage image = new BufferedImage(imageSize.Get_x(), imageSize.Get_y(), BufferedImage.TYPE_INT_RGB); image.setRGB(0, 0, viewSize.Get_x(), viewSize.Get_y(), imgBuffer, 0, viewSize.Get_x()); if (params.getRotationDegree() > 0) { image = ImageProcessingUtils.rotate(image, params.getRotationDegree()); } decompressor.Native_destroy(); channels.Native_destroy(); if (codestream.Exists()) codestream.Destroy(); inputSource.Native_destroy(); input.Native_destroy(); jp2_family_in.Native_destroy(); return image; } catch (KduException e) { e.printStackTrace(); throw new DjatokaException(e); } catch (Exception e) { e.printStackTrace(); throw new DjatokaException(e); } }
public BufferedImage extractUsingCompositor() throws IOException, DjatokaException { boolean useRegion = false; int left = 0; int top = 0; int width = 50; int height = 50; boolean useleftDouble = false; Double leftDouble = 0.0; boolean usetopDouble = false; Double topDouble = 0.0; boolean usewidthDouble = false; Double widthDouble = 0.0; boolean useheightDouble = false; Double heightDouble = 0.0; if (params.getRegion() != null) { StringTokenizer st = new StringTokenizer(params.getRegion(), "{},"); String token; // top if ((token = st.nextToken()).contains(".")) { topDouble = Double.parseDouble(token); usetopDouble = true; } else top = Integer.parseInt(token); // left if ((token = st.nextToken()).contains(".")) { leftDouble = Double.parseDouble(token); useleftDouble = true; } else left = Integer.parseInt(token); // height if ((token = st.nextToken()).contains(".")) { heightDouble = Double.parseDouble(token); useheightDouble = true; } else height = Integer.parseInt(token); // width if ((token = st.nextToken()).contains(".")) { widthDouble = Double.parseDouble(token); usewidthDouble = true; } else width = Integer.parseInt(token); useRegion = true; } if (is != null) { File f = File.createTempFile("tmp", ".jp2"); f.deleteOnExit(); FileOutputStream fos = new FileOutputStream(f); sourceFile = f.getAbsolutePath(); IOUtils.copyStream(is, fos); } Kdu_simple_file_source raw_src = null; // Must be disposed last Jp2_family_src family_src = new Jp2_family_src(); // Dispose last Jpx_source wrapped_src = new Jpx_source(); // Dispose in the middle Kdu_region_compositor compositor = null; // Must be disposed first BufferedImage image = null; try { family_src.Open(sourceFile); int success = wrapped_src.Open(family_src, true); if (success < 0) { family_src.Close(); wrapped_src.Close(); raw_src = new Kdu_simple_file_source(sourceFile); } compositor = new Kdu_region_compositor(); if (raw_src != null) compositor.Create(raw_src); else compositor.Create(wrapped_src); Kdu_dims imageDimensions = new Kdu_dims(); compositor.Get_total_composition_dims(imageDimensions); Kdu_coords imageSize = imageDimensions.Access_size(); Kdu_coords imagePosition = imageDimensions.Access_pos(); if (useleftDouble) left = imagePosition.Get_x() + (int) Math.round(leftDouble * imageSize.Get_x()); if (usetopDouble) top = imagePosition.Get_y() + (int) Math.round(topDouble * imageSize.Get_y()); if (useheightDouble) height = (int) Math.round(heightDouble * imageSize.Get_y()); if (usewidthDouble) width = (int) Math.round(widthDouble * imageSize.Get_x()); if (useRegion) { imageSize.Set_x(width); imageSize.Set_y(height); imagePosition.Set_x(left); imagePosition.Set_y(top); } int reduce = 1 << params.getLevelReductionFactor(); imageSize.Set_x(imageSize.Get_x()); imageSize.Set_y(imageSize.Get_y()); imagePosition.Set_x(imagePosition.Get_x() / reduce - (1 / reduce - 1) / 2); imagePosition.Set_y(imagePosition.Get_y() / reduce - (1 / reduce - 1) / 2); Kdu_dims viewDims = new Kdu_dims(); viewDims.Assign(imageDimensions); viewDims.Access_size().Set_x(imageSize.Get_x()); viewDims.Access_size().Set_y(imageSize.Get_y()); compositor.Add_compositing_layer(0, viewDims, viewDims); if (params.getRotationDegree() == 90) compositor.Set_scale(true, false, true, 1.0F); else if (params.getRotationDegree() == 180) compositor.Set_scale(false, true, true, 1.0F); else if (params.getRotationDegree() == 270) compositor.Set_scale(true, true, false, 1.0F); else compositor.Set_scale(false, false, false, 1.0F); compositor.Get_total_composition_dims(viewDims); Kdu_coords viewSize = viewDims.Access_size(); compositor.Set_buffer_surface(viewDims); int[] imgBuffer = new int[viewSize.Get_x() * viewSize.Get_y()]; Kdu_compositor_buf compositorBuffer = compositor.Get_composition_buffer(viewDims); int regionBufferSize = 0; int[] kduBuffer = null; Kdu_dims newRegion = new Kdu_dims(); while (compositor.Process(100000, newRegion)) { Kdu_coords newOffset = newRegion.Access_pos(); Kdu_coords newSize = newRegion.Access_size(); newOffset.Subtract(viewDims.Access_pos()); int newPixels = newSize.Get_x() * newSize.Get_y(); if (newPixels == 0) continue; if (newPixels > regionBufferSize) { regionBufferSize = newPixels; kduBuffer = new int[regionBufferSize]; } compositorBuffer.Get_region(newRegion, kduBuffer); int imgBuffereIdx = newOffset.Get_x() + newOffset.Get_y() * viewSize.Get_x(); int kduBufferIdx = 0; int xDiff = viewSize.Get_x() - newSize.Get_x(); for (int j = 0; j < newSize.Get_y(); j++, imgBuffereIdx += xDiff) { for (int i = 0; i < newSize.Get_x(); i++) { imgBuffer[imgBuffereIdx++] = kduBuffer[kduBufferIdx++]; } } } if (params.getRotationDegree() == 90 || params.getRotationDegree() == 270) image = new BufferedImage(imageSize.Get_y(), imageSize.Get_x(), BufferedImage.TYPE_INT_RGB); else image = new BufferedImage(imageSize.Get_x(), imageSize.Get_y(), BufferedImage.TYPE_INT_RGB); image.setRGB(0, 0, viewSize.Get_x(), viewSize.Get_y(), imgBuffer, 0, viewSize.Get_x()); if (compositor != null) compositor.Native_destroy(); wrapped_src.Native_destroy(); family_src.Native_destroy(); if (raw_src != null) raw_src.Native_destroy(); return image; } catch (KduException e) { e.printStackTrace(); throw new DjatokaException(e); } catch (Exception e) { e.printStackTrace(); throw new DjatokaException(e); } }
/** * Extracts region defined in DjatokaDecodeParam as BufferedImage * * @param input absolute file path of PDF file. * @param params DjatokaDecodeParam instance containing region and transform settings. * @return extracted region as a BufferedImage * @throws DjatokaException */ @Override public BufferedImage process(String input, DjatokaDecodeParam params) throws DjatokaException { logger.debug("ExtractPDF.process:\n\tinput: " + input + "\n\tparams: " + params); if (input == null) throw new DjatokaException("Unknown failure while converting file: no image produced."); try { setPDFCommandsPath(); } catch (IllegalStateException e) { logger.error("Failed to set PDF commands path: ", e); throw e; } int page_number = 1 + params.getCompositingLayer(); // From 0-based to 1-based. int status = 0; BufferedImage processedImage = null; try { /* // First get max physical dim of bounding box of the page // to compute the DPI to ask for.. otherwise some AutoCAD // drawings can produce enormous files even at 75dpi, for // 48" drawings.. int dpi = 0; Dimension pageSize = getPDFPageSize(input, page_number); if (pageSize == null) { logger.error("Sanity check: Did not find \"Page " + page_number + " size\" line in output of pdfinfo, file="+input); throw new IllegalArgumentException("Failed to get \"Page " + page_number + " size\" of PDF with pdfinfo."); } else { double w = pageSize.getWidth(); double h = pageSize.getHeight(); int maxdim = (int)Math.max(Math.abs(w), Math.abs(h)); dpi = Math.min(MAX_DPI, (MAX_PX * 72 / maxdim)); logger.debug("DPI: pdfinfo method got dpi="+dpi+" for max dim="+maxdim+" (points, 1/72\")"); } */ // Scale int dpi = getScaledDPI(params); // Requires Sun JAI imageio additions to read ppm directly. // this will get "-[0]+1.ppm" appended to it by pdftoppm File outPrefixF = File.createTempFile("pdftopng", "out"); String outPrefix = outPrefixF.toString(); outPrefixF.delete(); // String pdfCmd[] = PDFTOPPM_COMMAND.clone(); ArrayList<String> pdfCmd = new ArrayList<String>(Arrays.asList(PDFTOPPM_COMMAND)); pdfCmd.set(PDFTOPPM_COMMAND_POSITION_BIN, pdftoppmPath); pdfCmd.set(PDFTOPPM_COMMAND_POSITION_FIRSTPAGE, "" + page_number); pdfCmd.set(PDFTOPPM_COMMAND_POSITION_LASTPAGE, "" + page_number); pdfCmd.set(PDFTOPPM_COMMAND_POSITION_DPI, String.valueOf(dpi)); pdfCmd.set(PDFTOPPM_COMMAND_POSITION_FILE, input.toString()); pdfCmd.set(PDFTOPPM_COMMAND_POSITION_OUTPUTFILE, outPrefix); // Crop Rectangle crop = getCropParam(params); if (crop != null) { String[] cropParams = { "-x", "" + (int) crop.getX(), "-y", "" + (int) crop.getY(), "-W", "" + (int) crop.getWidth(), "-H", "" + (int) crop.getHeight() }; pdfCmd.addAll(PDFTOPPM_COMMAND_POSITION_OPTIONAL_EXTRAS, Arrays.asList(cropParams)); } String[] pdfCmdA = pdfCmd.toArray(new String[pdfCmd.size()]); logger.debug("Running pdftoppm command: " + Arrays.deepToString(pdfCmdA)); // logger.debug("Running pdftoppm command: " + pdfCmd.toString()); File outf = null; Process pdfProc = null; try { pdfProc = Runtime.getRuntime().exec(pdfCmdA); status = pdfProc.waitFor(); logger.debug("status: " + status); // pdftoppm uses variable numbers of padding 0s to the output prefix. // E.g., may be prefix-000001.png, prefix-001.png or even prefix-01.png. // Version 0.12.3 (Poppler, not XPDF) seems to consider the total number of pages. // So, for example, in a PDF with 90 pages, the output will be "prefix-02.png"; // for a PDF with 350 pages, the output will be "prefix-002.png". // FIXME: try some approach where the PDF number of pages is considered without // running pdfinfo command again, thus making it simpler to determine the number // of padding zeros. Right now we going "brute force" because we do not know if // it is feasable to once again run the pdfinfo command. String tests[] = { outPrefix + "-" + page_number + ".png", outPrefix + "-0" + page_number + ".png", outPrefix + "-00" + page_number + ".png", outPrefix + "-000" + page_number + ".png", outPrefix + "-0000" + page_number + ".png", outPrefix + "-00000" + page_number + ".png" }; for (String outname : tests) { if ((new File(outname)).exists()) { outf = new File(outname); break; } } logger.debug( "PDFTOPPM output is: " + outf + ", exists=" + outf != null ? outf.exists() : "!"); processedImage = ImageIO.read(outf); // Rotate if (params.getRotationDegree() > 0) { processedImage = ImageProcessingUtils.rotate(processedImage, params.getRotationDegree()); } } catch (InterruptedException e) { logger.error("Failed converting PDF file to image: ", e); throw new IllegalArgumentException("Failed converting PDF file to image: ", e); } finally { if (outf != null) outf.delete(); // Our exec() should not produce any output, but we want to stay safe. // http://mark.koli.ch/2011/01/leaky-pipes-remember-to-close-your-streams-when-using-javas-runtimegetruntimeexec.html org.apache.commons.io.IOUtils.closeQuietly(pdfProc.getOutputStream()); org.apache.commons.io.IOUtils.closeQuietly(pdfProc.getInputStream()); org.apache.commons.io.IOUtils.closeQuietly(pdfProc.getErrorStream()); } } catch (Exception e) { logger.error("Failed converting PDF file to image: ", e); throw new IllegalArgumentException("Failed converting PDF file to image: ", e); } finally { if (status != 0) logger.error("PDF conversion proc failed, exit status=" + status + ", file=" + input); } return processedImage; }