123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- /* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ==================================================================== */
-
- package org.apache.poi.hemf.usermodel;
-
- import java.awt.image.BufferedImage;
- import java.io.IOException;
- import java.util.ArrayDeque;
- import java.util.Deque;
- import java.util.Iterator;
- import java.util.NoSuchElementException;
-
- import javax.imageio.ImageIO;
-
- import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
- import org.apache.poi.hemf.record.emf.HemfComment;
- import org.apache.poi.hemf.record.emf.HemfComment.EmfComment;
- import org.apache.poi.hemf.record.emf.HemfComment.EmfCommentDataFormat;
- import org.apache.poi.hemf.record.emf.HemfComment.EmfCommentDataGeneric;
- import org.apache.poi.hemf.record.emf.HemfComment.EmfCommentDataMultiformats;
- import org.apache.poi.hemf.record.emf.HemfComment.EmfCommentDataPlus;
- import org.apache.poi.hemf.record.emf.HemfComment.EmfCommentDataWMF;
- import org.apache.poi.hemf.record.emf.HemfRecord;
- import org.apache.poi.hemf.record.emfplus.HemfPlusImage;
- import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusBitmapDataType;
- import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusImage;
- import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObject;
- import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectType;
- import org.apache.poi.hwmf.record.HwmfBitmapDib;
- import org.apache.poi.hwmf.record.HwmfFill;
- import org.apache.poi.hwmf.usermodel.HwmfEmbedded;
- import org.apache.poi.hwmf.usermodel.HwmfEmbeddedType;
- import org.apache.poi.poifs.filesystem.FileMagic;
- import org.apache.poi.util.IOUtils;
-
- public class HemfEmbeddedIterator implements Iterator<HwmfEmbedded> {
- //arbitrarily selected; may need to increase
- private static final int MAX_RECORD_LENGTH = 100_000_000;
-
- private final Deque<Iterator<?>> iterStack = new ArrayDeque<>();
- private Object current;
-
- public HemfEmbeddedIterator(HemfPicture emf) {
- this(emf.getRecords().iterator());
- }
-
- public HemfEmbeddedIterator(Iterator<HemfRecord> recordIterator) {
- iterStack.add(recordIterator);
- }
-
- @Override
- public boolean hasNext() {
- return moveNext();
- }
-
- private boolean moveNext() {
- if (iterStack.isEmpty()) {
- return false;
- }
-
- if (current != null) {
- // don't search twice and potentially skip items
- return true;
- }
-
- Iterator<?> iter;
- do {
- iter = iterStack.peek();
- while (iter.hasNext()) {
- Object obj = iter.next();
- if (obj instanceof EmfComment) {
- HemfComment.EmfCommentData cd = ((EmfComment)obj).getCommentData();
- if (
- cd instanceof EmfCommentDataWMF ||
- cd instanceof EmfCommentDataGeneric
- ) {
- current = obj;
- return true;
- }
-
- if (cd instanceof EmfCommentDataMultiformats) {
- Iterator<?> iter2 = ((EmfCommentDataMultiformats)cd).getFormats().iterator();
- if (iter2.hasNext()) {
- iterStack.push(iter2);
- continue;
- }
- }
-
- if (cd instanceof EmfCommentDataPlus) {
- Iterator<?> iter2 = ((EmfCommentDataPlus)cd).getRecords().iterator();
- if (iter2.hasNext()) {
- iter = iter2;
- iterStack.push(iter2);
- continue;
- }
- }
- }
-
- if (obj instanceof EmfCommentDataFormat) {
- current = obj;
- return true;
- }
-
- if (obj instanceof EmfPlusObject && ((EmfPlusObject)obj).getObjectType() == EmfPlusObjectType.IMAGE) {
- current = obj;
- return true;
- }
-
- if (obj instanceof HwmfFill.WmfStretchDib) {
- HwmfBitmapDib bitmap = ((HwmfFill.WmfStretchDib) obj).getBitmap();
- if (bitmap.isValid()) {
- current = obj;
- return true;
- }
- }
- }
- iterStack.pop();
- } while (!iterStack.isEmpty());
-
- return false;
- }
-
- @Override
- public HwmfEmbedded next() {
- HwmfEmbedded emb;
- if ((emb = checkEmfCommentDataWMF()) != null) {
- return emb;
- }
- if ((emb = checkEmfCommentDataGeneric()) != null) {
- return emb;
- }
- if ((emb = checkEmfCommentDataFormat()) != null) {
- return emb;
- }
- if ((emb = checkEmfPlusObject()) != null) {
- return emb;
- }
- if ((emb = checkWmfStretchDib()) != null) {
- return emb;
- }
-
- throw new NoSuchElementException("no further embedded wmf records found.");
- }
-
- private HwmfEmbedded checkEmfCommentDataWMF() {
- if (!(current instanceof EmfComment && ((EmfComment)current).getCommentData() instanceof EmfCommentDataWMF)) {
- return null;
- }
-
- EmfCommentDataWMF wmf = (EmfCommentDataWMF)((EmfComment)current).getCommentData();
- HwmfEmbedded emb = new HwmfEmbedded();
- emb.setEmbeddedType(HwmfEmbeddedType.WMF);
- emb.setData(wmf.getWMFData());
- current = null;
- return emb;
- }
-
- private HwmfEmbedded checkEmfCommentDataGeneric() {
- if (!(current instanceof EmfComment && ((EmfComment)current).getCommentData() instanceof EmfCommentDataGeneric)) {
- return null;
- }
- EmfCommentDataGeneric cdg = (EmfCommentDataGeneric)((EmfComment)current).getCommentData();
- HwmfEmbedded emb = new HwmfEmbedded();
- emb.setEmbeddedType(HwmfEmbeddedType.UNKNOWN);
- emb.setData(cdg.getPrivateData());
- current = null;
- return emb;
- }
-
- private HwmfEmbedded checkEmfCommentDataFormat() {
- if (!(current instanceof EmfCommentDataFormat)) {
- return null;
- }
- EmfCommentDataFormat cdf = (EmfCommentDataFormat)current;
- HwmfEmbedded emb = new HwmfEmbedded();
- boolean isEmf = (cdf.getSignature() == HemfComment.EmfFormatSignature.ENHMETA_SIGNATURE);
- emb.setEmbeddedType(isEmf ? HwmfEmbeddedType.EMF : HwmfEmbeddedType.EPS);
- emb.setData(cdf.getRawData());
- current = null;
- return emb;
- }
-
- private HwmfEmbedded checkWmfStretchDib() {
- if (!(current instanceof HwmfFill.WmfStretchDib)) {
- return null;
- }
- HwmfEmbedded emb = new HwmfEmbedded();
- emb.setData(((HwmfFill.WmfStretchDib) current).getBitmap().getBMPData());
- emb.setEmbeddedType(HwmfEmbeddedType.BMP);
- current = null;
- return emb;
- }
-
- private HwmfEmbedded checkEmfPlusObject() {
- if (!(current instanceof EmfPlusObject)) {
- return null;
- }
-
- EmfPlusObject epo = (EmfPlusObject)current;
- assert(epo.getObjectType() == EmfPlusObjectType.IMAGE);
- EmfPlusImage img = epo.getObjectData();
- assert(img.getImageDataType() != null);
-
- final HwmfEmbedded emb = getEmfPlusImageData();
- if (emb == null) {
- return null;
- }
-
- final HwmfEmbeddedType et;
- switch (img.getImageDataType()) {
- case BITMAP:
- if (img.getBitmapType() == EmfPlusBitmapDataType.COMPRESSED) {
- switch (FileMagic.valueOf(emb.getRawData())) {
- case JPEG:
- et = HwmfEmbeddedType.JPEG;
- break;
- case GIF:
- et = HwmfEmbeddedType.GIF;
- break;
- case PNG:
- et = HwmfEmbeddedType.PNG;
- break;
- case TIFF:
- et = HwmfEmbeddedType.TIFF;
- break;
- default:
- et = HwmfEmbeddedType.BITMAP;
- break;
- }
- } else {
- et = HwmfEmbeddedType.PNG;
- compressGDIBitmap(img, emb, et);
- }
- break;
- case METAFILE:
- assert(img.getMetafileType() != null);
- switch (img.getMetafileType()) {
- case Wmf:
- case WmfPlaceable:
- et = HwmfEmbeddedType.WMF;
- break;
- case Emf:
- case EmfPlusDual:
- case EmfPlusOnly:
- et = HwmfEmbeddedType.EMF;
- break;
- default:
- et = HwmfEmbeddedType.UNKNOWN;
- break;
- }
- break;
- default:
- et = HwmfEmbeddedType.UNKNOWN;
- break;
- }
- emb.setEmbeddedType(et);
-
- return emb;
- }
-
- /**
- * Compress GDIs internal format to something useful
- */
- private void compressGDIBitmap(EmfPlusImage img, HwmfEmbedded emb, HwmfEmbeddedType et) {
- BufferedImage bi = img.readGDIImage(emb.getRawData());
- try {
- UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream();
- // use HwmfEmbeddedType literal for conversion
- ImageIO.write(bi, et.toString(), bos);
- emb.setData(bos.toByteArray());
- } catch (IOException e) {
- // TODO: throw appropriate exception
- throw new RuntimeException(e);
- }
- }
-
-
- private HwmfEmbedded getEmfPlusImageData() {
- EmfPlusObject epo = (EmfPlusObject)current;
- assert(epo.getObjectType() == EmfPlusObjectType.IMAGE);
-
- final int objectId = epo.getObjectId();
-
- final HwmfEmbedded emb = new HwmfEmbedded();
-
- // totalSize is only set, if there are multiple chunks
- final int totalSize = epo.getTotalObjectSize() == 0
- ? ((EmfPlusImage)epo.getObjectData()).getImageData().length
- : epo.getTotalObjectSize();
- IOUtils.safelyAllocateCheck(totalSize, MAX_RECORD_LENGTH);
-
- try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(totalSize)) {
- boolean hasNext = false;
- do {
- EmfPlusImage img = epo.getObjectData();
- assert(img.getImageDataType() != null);
- assert(!hasNext || img.getImageDataType() == HemfPlusImage.EmfPlusImageDataType.CONTINUED);
- bos.write(img.getImageData());
- current = null;
- hasNext = moveNext() &&
- (current instanceof EmfPlusObject) &&
- ((epo = (EmfPlusObject) current).getObjectId() == objectId) &&
- bos.size() < totalSize-16;
- } while (hasNext);
-
- emb.setData(bos.toByteArray());
- return emb;
- } catch (IOException ignored) {
- // UnsynchronizedByteArrayOutputStream doesn't throw IOException
- return null;
- }
- }
- }
|