public static void displayImageInfo(ImageInfo info) {

    ImageSize size = info.getSize();

    Dimension2D dPt = size.getDimensionPt();
    Dimension dPx = size.getDimensionPx();

    log.debug(
        info.getOriginalURI()
            + " "
            + info.getMimeType()
            + " "
            + Math.round(dPx.getWidth())
            + "x"
            + Math.round(dPx.getHeight()));

    log.debug(
        "Resolution:"
            + Math.round(size.getDpiHorizontal())
            + "x"
            + Math.round(size.getDpiVertical()));
    log.debug(
        "Print size: "
            + Math.round(dPt.getWidth() / 72)
            + "\" x"
            + Math.round(dPt.getHeight() / 72)
            + "\"");
  }
    public static CxCy scale(ImageInfo imageInfo, double xEmu, double yEmu) {
      ImageSize size = imageInfo.getSize();
      double iwEmu = toEmu(size.getWidthPx(), size.getDpiHorizontal());
      double ihEmu = toEmu(size.getHeightPx(), size.getDpiVertical());

      return scaleToFit(iwEmu, ihEmu, xEmu, yEmu);
    }
  /**
   * Create a linked image part, and attach it as a rel of the specified source part (eg a header
   * part).
   *
   * <p>The current behaviour is that the part is added to the package, but since the target mode of
   * the rel is external, the part is redundant.
   *
   * @param wordMLPackage
   * @param sourcePart
   * @param url
   * @return
   * @throws Exception
   */
  public static BinaryPartAbstractImage createLinkedImagePart(
      OpcPackage opcPackage, Part sourcePart, URL url) throws Exception {

    log.debug("Incoming url for linked image: " + url.toString());

    ImageInfo info =
        ensureFormatIsSupported(url, null, null, false); // final param doesn't matter in this case

    ContentTypeManager ctm = opcPackage.getContentTypeManager();
    String proposedRelId = sourcePart.getRelationshipsPart().getNextId();
    // In order to ensure unique part name,
    // idea is to use the relId, which ought to be unique
    String ext = info.getMimeType().substring(info.getMimeType().indexOf("/") + 1);

    BinaryPartAbstractImage imagePart =
        (BinaryPartAbstractImage)
            ctm.newPartForContentType(
                info.getMimeType(),
                createImageName(opcPackage, sourcePart, proposedRelId, ext),
                null);

    // NB: contents never populated

    log.debug(
        "created part "
            + imagePart.getClass().getName()
            + " with name "
            + imagePart.getPartName().toString());

    imagePart.rels.add(
        sourcePart.addTargetPart(
            imagePart)); // want to create rel with suitable name; side effect is to add part
    imagePart.getRelLast().setTargetMode("External");

    opcPackage.getExternalResources().put(imagePart.getExternalTarget(), imagePart);

    //		if (!url.getProtocol().equals("file") && new File(url.toString() ).isFile()) {
    //			imagePart.rel.setTarget("file:///" + url);
    //		} else {
    //			imagePart.rel.setTarget(url.toString());
    //		}
    imagePart.getRelLast().setTarget(url.toString());

    imagePart.setImageInfo(info);

    return imagePart;
  }
  /**
   * Create an image part from the provided filePath image, attach it to the source part (eg the
   * main document part, a header part etc), and return it.
   *
   * <p>Works for both docx and pptx.
   *
   * @param opcPackage
   * @param sourcePart
   * @param filePath
   * @return
   * @throws Exception
   */
  public static BinaryPartAbstractImage createImagePart(
      OpcPackage opcPackage, Part sourcePart, File imageFile) throws Exception {

    final byte[] locByte = new byte[1];

    // We are in the case that image is not load (no byte Array) so isLoad is false
    ImageInfo info = ensureFormatIsSupported(imageFile, locByte, false);

    ContentTypeManager ctm = opcPackage.getContentTypeManager();

    // Ensure the relationships part exists
    if (sourcePart.getRelationshipsPart() == null) {
      RelationshipsPart.createRelationshipsPartForPart(sourcePart);
    }

    String proposedRelId = sourcePart.getRelationshipsPart().getNextId();

    String ext = info.getMimeType().substring(info.getMimeType().indexOf("/") + 1);

    BinaryPartAbstractImage imagePart =
        (BinaryPartAbstractImage)
            ctm.newPartForContentType(
                info.getMimeType(),
                createImageName(opcPackage, sourcePart, proposedRelId, ext),
                null);

    log.debug(
        "created part "
            + imagePart.getClass().getName()
            + " with name "
            + imagePart.getPartName().toString());

    FileInputStream fis = new FileInputStream(imageFile);
    imagePart.setBinaryData(fis);

    imagePart.rels.add(sourcePart.addTargetPart(imagePart, proposedRelId));

    imagePart.setImageInfo(info);

    return imagePart;
  }
  private ICC_Profile buildICCProfile(
      final ImageInfo info, final ColorSpace colorSpace, final ByteArrayOutputStream iccStream)
      throws IOException {
    if (iccStream != null && iccStream.size() > 0) {
      if (log.isDebugEnabled()) {
        log.debug("Effective ICC profile size: " + iccStream.size());
      }
      final int alignment = 4;
      final int padding = (alignment - iccStream.size() % alignment) % alignment;
      if (padding != 0) {
        try {
          iccStream.write(new byte[padding]);
        } catch (final IOException ioe) {
          throw new IOException("Error while aligning ICC stream: " + ioe.getMessage());
        }
      }

      ICC_Profile iccProfile = null;
      try {
        iccProfile = ColorProfileUtil.getICC_Profile(iccStream.toByteArray());
        if (log.isDebugEnabled()) {
          log.debug("JPEG has an ICC profile: " + iccProfile.toString());
        }
      } catch (final IllegalArgumentException iae) {
        log.warn(
            "An ICC profile is present in the JPEG file but it is invalid ("
                + iae.getMessage()
                + "). The color profile will be ignored. ("
                + info.getOriginalURI()
                + ")");
        return null;
      }
      if (iccProfile.getNumComponents() != colorSpace.getNumComponents()) {
        log.warn(
            "The number of components of the ICC profile ("
                + iccProfile.getNumComponents()
                + ") doesn't match the image ("
                + colorSpace.getNumComponents()
                + "). Ignoring the ICC color profile.");
        return null;
      } else {
        return iccProfile;
      }
    } else {
      return null; // no ICC profile available
    }
  }
  /**
   * Create a <wp:inline> element suitable for this image, which can be _embedded_ in
   * w:p/w:r/w:drawing.
   *
   * @param filenameHint Any text, for example the original filename
   * @param altText Like HTML's alt text
   * @param id1 An id unique in the document
   * @param id2 Another id unique in the document
   * @param cx Image width in twip
   * @param link true if this is to be linked not embedded None of these things seem to be exposed
   *     in Word 2007's user interface, but Word won't open the document if any of the attributes
   *     these go in (except @ desc) aren't present!
   * @throws Exception
   */
  public Inline createImageInline(
      String filenameHint, String altText, int id1, int id2, long cx, boolean link)
      throws Exception {

    ImageSize size = imageInfo.getSize();

    Dimension2D dPt = size.getDimensionPt();
    double imageWidthTwips = dPt.getWidth() * 20;
    log.debug("imageWidthTwips: " + imageWidthTwips);

    long cy;

    log.debug("Scaling image height to retain aspect ratio");
    cy = UnitsOfMeasurement.twipToEMU(dPt.getHeight() * 20 * cx / imageWidthTwips);

    // Now convert cx to EMU
    cx = UnitsOfMeasurement.twipToEMU(cx);

    log.debug("cx=" + cx + "; cy=" + cy);

    return createImageInline(filenameHint, altText, id1, id2, cx, cy, link);
  }
    public static CxCy scale(ImageInfo imageInfo, PageDimensions page) {

      double writableWidthTwips = page.getWritableWidthTwips();
      log.debug("writableWidthTwips: " + writableWidthTwips);

      ImageSize size = imageInfo.getSize();

      Dimension2D dPt = size.getDimensionPt();
      double imageWidthTwips = dPt.getWidth() * 20;
      log.debug("imageWidthTwips: " + imageWidthTwips);

      long cx;
      long cy;
      boolean scaled = false;
      if (imageWidthTwips > writableWidthTwips) {

        log.debug("Scaling image to fit page width");
        scaled = true;

        cx = UnitsOfMeasurement.twipToEMU(writableWidthTwips);
        cy =
            UnitsOfMeasurement.twipToEMU(
                dPt.getHeight() * 20 * writableWidthTwips / imageWidthTwips);

      } else {

        log.debug("Scaling image - not necessary");

        cx = UnitsOfMeasurement.twipToEMU(imageWidthTwips);
        cy = UnitsOfMeasurement.twipToEMU(dPt.getHeight() * 20);
      }

      log.debug("cx=" + cx + "; cy=" + cy);

      return new CxCy(cx, cy, scaled);
    }
  /** {@inheritDoc} */
  public void activateLayout() {
    initialize();

    FOUserAgent userAgent = pageSeq.getUserAgent();
    ImageManager imageManager = userAgent.getFactory().getImageManager();

    String uri = getExternalDocument().getSrc();
    Integer firstPageIndex = ImageUtil.getPageIndexFromURI(uri);
    boolean hasPageIndex = (firstPageIndex != null);

    try {
      ImageInfo info = imageManager.getImageInfo(uri, userAgent.getImageSessionContext());

      Object moreImages = info.getCustomObjects().get(ImageInfo.HAS_MORE_IMAGES);
      boolean hasMoreImages = moreImages != null && !Boolean.FALSE.equals(moreImages);

      Dimension intrinsicSize = info.getSize().getDimensionMpt();
      ImageLayout layout = new ImageLayout(getExternalDocument(), this, intrinsicSize);

      PageSequence pageSequence = new PageSequence(null);
      transferExtensions(pageSequence);
      areaTreeHandler.getAreaTreeModel().startPageSequence(pageSequence);
      if (log.isDebugEnabled()) {
        log.debug("Starting layout");
      }

      makePageForImage(info, layout);

      if (!hasPageIndex && hasMoreImages) {
        if (log.isTraceEnabled()) {
          log.trace("Starting multi-page processing...");
        }
        URI originalURI;
        try {
          originalURI = new URI(URISpecification.escapeURI(uri));
          int pageIndex = 1;
          while (hasMoreImages) {
            URI tempURI =
                new URI(
                    originalURI.getScheme(),
                    originalURI.getSchemeSpecificPart(),
                    "page=" + Integer.toString(pageIndex + 1));
            if (log.isTraceEnabled()) {
              log.trace("Subimage: " + tempURI.toASCIIString());
            }
            ImageInfo subinfo =
                imageManager.getImageInfo(
                    tempURI.toASCIIString(), userAgent.getImageSessionContext());

            moreImages = subinfo.getCustomObjects().get(ImageInfo.HAS_MORE_IMAGES);
            hasMoreImages = moreImages != null && !Boolean.FALSE.equals(moreImages);

            intrinsicSize = subinfo.getSize().getDimensionMpt();
            layout = new ImageLayout(getExternalDocument(), this, intrinsicSize);

            makePageForImage(subinfo, layout);

            pageIndex++;
          }
        } catch (URISyntaxException e) {
          getResourceEventProducer().uriError(this, uri, e, getExternalDocument().getLocator());
          return;
        }
      }
    } catch (FileNotFoundException fnfe) {
      getResourceEventProducer().imageNotFound(this, uri, fnfe, getExternalDocument().getLocator());
    } catch (IOException ioe) {
      getResourceEventProducer().imageIOError(this, uri, ioe, getExternalDocument().getLocator());
    } catch (ImageException ie) {
      getResourceEventProducer().imageError(this, uri, ie, getExternalDocument().getLocator());
    }
  }
 private void makePageForImage(ImageInfo info, ImageLayout layout) {
   this.imageLayout = layout;
   curPage = makeNewPage(false, false);
   fillPage(info.getOriginalURI());
   finishPage();
 }
Beispiel #10
0
  /** {@inheritDoc} */
  @Override
  public Image loadImage(final ImageInfo info, final Map hints, final ImageSessionContext session)
      throws ImageException, IOException {
    if (!MimeConstants.MIME_JPEG.equals(info.getMimeType())) {
      throw new IllegalArgumentException(
          "ImageInfo must be from a image with MIME type: " + MimeConstants.MIME_JPEG);
    }

    ColorSpace colorSpace = null;
    boolean appeFound = false;
    int sofType = 0;
    ByteArrayOutputStream iccStream = null;

    final Source src = session.needSource(info.getOriginalURI());
    final ImageInputStream in = ImageUtil.needImageInputStream(src);
    final JPEGFile jpeg = new JPEGFile(in);
    in.mark();
    try {
      outer:
      while (true) {
        int reclen;
        final int segID = jpeg.readMarkerSegment();
        if (log.isTraceEnabled()) {
          log.trace("Seg Marker: " + Integer.toHexString(segID));
        }
        switch (segID) {
          case EOI:
            log.trace("EOI found. Stopping.");
            break outer;
          case SOS:
            log.trace("SOS found. Stopping early."); // TODO Not sure if
            // this is safe
            break outer;
          case SOI:
          case NULL:
            break;
          case SOF0: // baseline
          case SOF1: // extended sequential DCT
          case SOF2: // progressive (since PDF 1.3)
          case SOFA: // progressive (since PDF 1.3)
            sofType = segID;
            if (log.isTraceEnabled()) {
              log.trace("SOF: " + Integer.toHexString(sofType));
            }
            in.mark();
            try {
              reclen = jpeg.readSegmentLength();
              in.skipBytes(1); // data precision
              in.skipBytes(2); // height
              in.skipBytes(2); // width
              final int numComponents = in.readUnsignedByte();
              if (numComponents == 1) {
                colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);
              } else if (numComponents == 3) {
                colorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
              } else if (numComponents == 4) {
                colorSpace = ColorSpaces.getDeviceCMYKColorSpace();
              } else {
                throw new ImageException(
                    "Unsupported ColorSpace for image "
                        + info
                        + ". The number of components supported are 1, 3 and 4.");
              }
            } finally {
              in.reset();
            }
            in.skipBytes(reclen);
            break;
          case APP2: // ICC (see ICC1V42.pdf)
            in.mark();
            try {
              reclen = jpeg.readSegmentLength();
              // Check for ICC profile
              final byte[] iccString = new byte[11];
              in.readFully(iccString);
              in.skipBytes(1); // string terminator (null byte)

              if ("ICC_PROFILE".equals(new String(iccString, "US-ASCII"))) {
                in.skipBytes(2); // chunk sequence number and total
                // number of chunks
                final int payloadSize = reclen - 2 - 12 - 2;
                if (ignoreColorProfile(hints)) {
                  log.debug("Ignoring ICC profile data in JPEG");
                  in.skipBytes(payloadSize);
                } else {
                  final byte[] buf = new byte[payloadSize];
                  in.readFully(buf);
                  if (iccStream == null) {
                    if (log.isDebugEnabled()) {
                      log.debug("JPEG has an ICC profile");
                      final DataInputStream din =
                          new DataInputStream(new ByteArrayInputStream(buf));
                      log.debug("Declared ICC profile size: " + din.readInt());
                    }
                    // ICC profiles can be split into several
                    // chunks
                    // so collect in a byte array output stream
                    iccStream = new ByteArrayOutputStream();
                  }
                  iccStream.write(buf);
                }
              }
            } finally {
              in.reset();
            }
            in.skipBytes(reclen);
            break;
          case APPE: // Adobe-specific (see 5116.DCT_Filter.pdf)
            in.mark();
            try {
              reclen = jpeg.readSegmentLength();
              // Check for Adobe header
              final byte[] adobeHeader = new byte[5];
              in.readFully(adobeHeader);

              if ("Adobe".equals(new String(adobeHeader, "US-ASCII"))) {
                // The reason for reading the APPE marker is that
                // Adobe Photoshop
                // generates CMYK JPEGs with inverted values. The
                // correct thing
                // to do would be to interpret the values in the
                // marker, but for now
                // only assume that if APPE marker is present and
                // colorspace is CMYK,
                // the image is inverted.
                appeFound = true;
              }
            } finally {
              in.reset();
            }
            in.skipBytes(reclen);
            break;
          default:
            jpeg.skipCurrentMarkerSegment();
        }
      }
    } finally {
      in.reset();
    }

    final ICC_Profile iccProfile = buildICCProfile(info, colorSpace, iccStream);
    if (iccProfile == null && colorSpace == null) {
      throw new ImageException("ColorSpace could not be identified for JPEG image " + info);
    }

    boolean invertImage = false;
    if (appeFound && colorSpace.getType() == ColorSpace.TYPE_CMYK) {
      if (log.isDebugEnabled()) {
        log.debug(
            "JPEG has an Adobe APPE marker. Note: CMYK Image will be inverted. ("
                + info.getOriginalURI()
                + ")");
      }
      invertImage = true;
    }

    final ImageRawJPEG rawImage =
        new ImageRawJPEG(
            info, ImageUtil.needInputStream(src), sofType, colorSpace, iccProfile, invertImage);
    return rawImage;
  }
  /**
   * @param bytes
   * @param imageFile
   * @return
   * @throws Exception
   * @throws FileNotFoundException
   * @throws IOException
   * @throws InterruptedException
   */
  private static ImageInfo ensureFormatIsSupported(
      URL url, File imageFile, byte[] bytes, boolean isLoad) throws Docx4JException {

    FileOutputStream fos;
    // ImageInfo can also tell us what sort of image it is

    ImageInfo info = null;
    boolean imagePreloaderFound = true;
    try {
      try {
        info = getImageInfo(url);

        // Debug ... note that these figures
        // aren't necessarily accurate for EPS
        displayImageInfo(info);
      } catch (org.apache.xmlgraphics.image.loader.ImageException e) {

        // Assume: The file format is not supported. No ImagePreloader found for /tmp/img55623.img
        // There is no preloader for eg PDFs.
        // (To use an image natively, we do need a preloader)
        imagePreloaderFound = false;
        log.warn(e.getMessage());
      }

      if (imagePreloaderFound
          && (info.getMimeType().equals(ContentTypes.IMAGE_TIFF)
              || info.getMimeType().equals(ContentTypes.IMAGE_EMF2) // ImageInfo
              || info.getMimeType().equals(ContentTypes.IMAGE_WMF)
              || info.getMimeType().equals(ContentTypes.IMAGE_PNG)
              || info.getMimeType().equals(ContentTypes.IMAGE_JPEG)
              || info.getMimeType().equals(ContentTypes.IMAGE_GIF)
              //					 || info.getMimeType().equals(ContentTypes.IMAGE_EPS)
              || info.getMimeType().equals(ContentTypes.IMAGE_BMP))) {
        // TODO: add other supported formats

        // If its a format Word supports natively,
        // do nothing here
        log.debug(".. supported natively by Word");

      } else if (imageFile != null && bytes != null) {

        // otherwise (eg if its an EPS or PDF), try to convert it
        // Although the Word UI suggests you can embed an EPS
        // directly, Word actually converts it to an EMF;
        // Word is unable to read a plain EPS image part.

        // (TODO: detect failure)

        log.debug(".. attempting to convert to PNG");

        // If image haven't been load (using function createImagePartFromFilePath), we load it
        if (isLoad == false) {

          // So first, we create tmpFile
          File tmpImageFile = File.createTempFile("img", ".img");
          fos = new FileOutputStream(tmpImageFile);

          // Now we get the inputStream, which is represented by imageFile in this case
          FileInputStream bais = new FileInputStream(imageFile);

          // We convert
          convertToPNG(bais, fos, density);

          // We don't forget to change locFile because the new image file is the converted image
          // file!!
          imageFile = tmpImageFile;

        } // Else image has been load in an array (using function cretaImagePart)
        else {
          ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
          fos = new FileOutputStream(imageFile);
          convertToPNG(bais, fos, density);
        }

        fos.close();
        fos = null;

        // We need to refresh image info
        getImageManager().getCache().clearCache();
        info = getImageInfo(new URL("file://" + imageFile.getAbsolutePath()));

        // Debug ...
        displayImageInfo(info);
      } else {
        throw new Docx4JException("Unsupported linked image type.");
      }
    } catch (Exception e) {
      throw new Docx4JException("Error checking image format", e);
    }
    return info;
  }
  /**
   * Create an image part from the provided byte array, attach it to the source part (eg the main
   * document part, a header part etc), and return it.
   *
   * <p>Works for both docx and pptx.
   *
   * <p>Note: this method creates a temp file (and attempts to delete it). That's because it uses
   * org.apache.xmlgraphics
   *
   * @param opcPackage
   * @param sourcePart
   * @param bytes
   * @return
   * @throws Exception
   */
  public static BinaryPartAbstractImage createImagePart(
      OpcPackage opcPackage, Part sourcePart, byte[] bytes) throws Exception {

    // Whatever image type this is, we're going to need
    // to know its dimensions.
    // For that we use ImageInfo, which can only
    // load an image from a URI.

    // So first, write the bytes to a temp file
    File tmpImageFile = File.createTempFile("img", ".img");

    FileOutputStream fos = new FileOutputStream(tmpImageFile);
    fos.write(bytes);
    fos.close();
    log.debug("created tmp file: " + tmpImageFile.getAbsolutePath());

    ImageInfo info = ensureFormatIsSupported(tmpImageFile, bytes, true);

    // In the absence of an exception, tmpImageFile now contains an image
    // Word will accept

    ContentTypeManager ctm = opcPackage.getContentTypeManager();

    // Ensure the relationships part exists
    if (sourcePart.getRelationshipsPart() == null) {
      RelationshipsPart.createRelationshipsPartForPart(sourcePart);
    }

    String proposedRelId = sourcePart.getRelationshipsPart().getNextId();

    String ext = info.getMimeType().substring(info.getMimeType().indexOf("/") + 1);

    //		System.out.println(ext);

    BinaryPartAbstractImage imagePart =
        (BinaryPartAbstractImage)
            ctm.newPartForContentType(
                info.getMimeType(),
                createImageName(opcPackage, sourcePart, proposedRelId, ext),
                null);

    log.debug(
        "created part "
            + imagePart.getClass().getName()
            + " with name "
            + imagePart.getPartName().toString());

    FileInputStream fis = new FileInputStream(tmpImageFile);
    imagePart.setBinaryData(fis);

    imagePart.rels.add(sourcePart.addTargetPart(imagePart, proposedRelId));

    imagePart.setImageInfo(info);

    // Delete the tmp file
    // As per http://stackoverflow.com/questions/991489/i-cant-delete-a-file-in-java
    // the following 3 lines are necessary, at least on Win 7 x64
    // Also reported on Win XP, but in my testing, the files were deleting OK anyway.
    fos = null;
    fis = null;
    if (Docx4jProperties.getProperty(
        "docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage.TempFiles.ForceGC",
        true)) {
      System.gc();
    }
    if (tmpImageFile.delete()) {
      log.debug(".. deleted " + tmpImageFile.getAbsolutePath());
    } else {
      log.warn("Couldn't delete tmp file " + tmpImageFile.getAbsolutePath());
      tmpImageFile.deleteOnExit();
      // If that doesn't work, see "Clean Up Your Mess: Managing Temp Files in Java Apps"
      // at devx.com
    }

    return imagePart;
  }