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);
    }
  }
Example #2
0
  /**
   * 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;
  }