source: trunk/docx4j/src/main/java/org/docx4j/openpackaging/parts/WordprocessingML/BinaryPartAbstractImage.java @ 1680

Revision 1680, 34.1 KB checked in by jharrop, 8 months ago (diff)

createImagePart from FilePath? - refactored a bit

Line 
1/*
2 *  Copyright 2007-2008, Plutext Pty Ltd.
3 *   
4 *  This file is part of docx4j.
5
6    docx4j is licensed under the Apache License, Version 2.0 (the "License");
7    you may not use this file except in compliance with the License.
8
9    You may obtain a copy of the License at
10
11        http://www.apache.org/licenses/LICENSE-2.0
12
13    Unless required by applicable law or agreed to in writing, software
14    distributed under the License is distributed on an "AS IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18
19 */
20package org.docx4j.openpackaging.parts.WordprocessingML;
21
22import java.awt.Dimension;
23import java.awt.geom.Dimension2D;
24import java.io.BufferedInputStream;
25import java.io.BufferedOutputStream;
26import java.io.ByteArrayInputStream;
27import java.io.File;
28import java.io.FileInputStream;
29import java.io.FileNotFoundException;
30import java.io.FileOutputStream;
31import java.io.IOException;
32import java.io.InputStream;
33import java.io.OutputStream;
34import java.net.MalformedURLException;
35import java.net.URL;
36import java.util.List;
37
38import javax.xml.bind.JAXBElement;
39
40import org.apache.log4j.Logger;
41import org.apache.xmlgraphics.image.loader.ImageInfo;
42import org.apache.xmlgraphics.image.loader.ImageManager;
43import org.apache.xmlgraphics.image.loader.ImageSessionContext;
44import org.apache.xmlgraphics.image.loader.ImageSize;
45import org.apache.xmlgraphics.image.loader.impl.DefaultImageContext;
46import org.apache.xmlgraphics.image.loader.impl.DefaultImageSessionContext;
47import org.docx4j.UnitsOfMeasurement;
48import org.docx4j.dml.picture.Pic;
49import org.docx4j.dml.wordprocessingDrawing.Inline;
50import org.docx4j.model.structure.PageDimensions;
51import org.docx4j.model.structure.SectionWrapper;
52import org.docx4j.openpackaging.Base;
53import org.docx4j.openpackaging.contenttype.ContentTypeManager;
54import org.docx4j.openpackaging.contenttype.ContentTypes;
55import org.docx4j.openpackaging.exceptions.Docx4JException;
56import org.docx4j.openpackaging.exceptions.InvalidFormatException;
57import org.docx4j.openpackaging.packages.OpcPackage;
58import org.docx4j.openpackaging.packages.PresentationMLPackage;
59import org.docx4j.openpackaging.packages.SpreadsheetMLPackage;
60import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
61import org.docx4j.openpackaging.parts.ExternalTarget;
62import org.docx4j.openpackaging.parts.Part;
63import org.docx4j.openpackaging.parts.PartName;
64import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
65import org.docx4j.relationships.Relationship;
66
67public abstract class BinaryPartAbstractImage extends BinaryPart {
68       
69        protected static Logger log = Logger.getLogger(BinaryPartAbstractImage.class);
70        final static String IMAGE_DIR_PREFIX = "/word/media/";
71        final static String IMAGE_NAME_PREFIX = "image";
72       
73        public BinaryPartAbstractImage(PartName partName) throws InvalidFormatException {
74                super(partName);
75               
76                // Can't setContentType or setRelationshipType, since
77                // these will differ depending on the nature of the data.
78                // Common binary parts should extend this class to
79                // provide that information.
80       
81                this.getOwningRelationshipPart();
82               
83        }
84       
85        public BinaryPartAbstractImage(ExternalTarget externalTarget) {
86                super(externalTarget);
87        }
88        ImageInfo imageInfo;
89
90        public ImageInfo getImageInfo() {
91               
92        if (imageInfo == null) {
93                        // TODO - create it
94                        // Save byte buffer as a tmp file
95                        // Generate ImageInfo
96                        // Delete tmp file
97                }
98               
99                return imageInfo;
100        }
101
102        public void setImageInfo(ImageInfo imageInfo) {
103                this.imageInfo = imageInfo;
104        }
105        // TODO, instead of Part.getOwningRelationshipPart(),
106        // it would be better to have getOwningRelationship(),
107        // and if required, to get OwningRelationshipPart from that
108        // This is a temp workaround   
109        Relationship rel;
110        static int density = 150;       
111
112        /**
113         * Set the resolution at which a PDF or EPS is converted
114         * to PNG.  For best quality, you should set this to match
115         * the target output device.  Higher densities (eg 600)
116         * give better quality, at the expense of conversion time
117         * (and file size).
118         * @param density
119         */
120        public static void setDensity(int density) {
121                BinaryPartAbstractImage.density = density;
122        }
123
124        static {
125               
126                imageManager = new ImageManager(new DefaultImageContext());
127               
128        }
129        static ImageManager imageManager;
130
131        /**
132         * Create an image part from the provided byte array, attach it to the
133         * docx main document part, and return it.
134         *
135         * @param wordMLPackage
136         * @param sourcePart
137         * @param bytes
138         * @return
139         * @throws Exception
140         */
141        public static BinaryPartAbstractImage createImagePart(WordprocessingMLPackage wordMLPackage,
142                        byte[] bytes) throws Exception {
143               
144                return createImagePart(wordMLPackage,
145                                wordMLPackage.getMainDocumentPart(), bytes);
146
147        }
148       
149    /**
150     * Possibility to put directly an image filePath instead of giving an image byte array
151     * @param wordMLPackage
152     * @param imageFile
153     *
154     */
155    public static BinaryPartAbstractImage createImagePart(WordprocessingMLPackage wordMLPackage,
156                File imageFile) throws Exception {
157
158        return createImagePart(wordMLPackage,
159                wordMLPackage.getMainDocumentPart(), imageFile);
160
161    }
162
163        /**
164         * This method assumes your package is a docx (not a pptx or xlsx).
165         *
166         * @param sourcePart
167         * @param proposedRelId
168         * @param ext
169         * @return
170         */
171        @Deprecated
172    public static String createImageName(Base sourcePart, String proposedRelId, String ext) {
173               
174                return PartName.generateUniqueName(sourcePart, proposedRelId, 
175                                IMAGE_DIR_PREFIX, IMAGE_NAME_PREFIX, ext);
176        }
177
178    public static String createImageName(OpcPackage opcPackage, Base sourcePart, String proposedRelId, String ext) {
179               
180                if (opcPackage instanceof WordprocessingMLPackage) {           
181                        return PartName.generateUniqueName(sourcePart, proposedRelId, 
182                                        IMAGE_DIR_PREFIX, IMAGE_NAME_PREFIX, ext);
183                } else if (opcPackage instanceof PresentationMLPackage) {               
184                        return PartName.generateUniqueName(sourcePart, proposedRelId, 
185                                        "/ppt/media/", IMAGE_NAME_PREFIX, ext);
186                } else if (opcPackage instanceof SpreadsheetMLPackage) {               
187                        return PartName.generateUniqueName(sourcePart, proposedRelId, 
188                                        "/xl/media/", IMAGE_NAME_PREFIX, ext);
189                } else {
190                        // Shouldn't happen
191                        return PartName.generateUniqueName(sourcePart, proposedRelId, 
192                                        IMAGE_DIR_PREFIX, IMAGE_NAME_PREFIX, ext);                     
193                }
194        }
195       
196        /**
197         * Create an image part from the provided byte array, attach it to the source part
198         * (eg the main document part, a header part etc), and return it.
199         *
200         * Works for both docx and pptx.
201         *
202         * @param opcPackage
203         * @param sourcePart
204         * @param bytes
205         * @return
206         * @throws Exception
207         */
208        public static BinaryPartAbstractImage createImagePart(
209                        OpcPackage opcPackage,
210                        Part sourcePart, byte[] bytes) throws Exception {
211                               
212                // Whatever image type this is, we're going to need
213                // to know its dimensions.
214                // For that we use ImageInfo, which can only
215                // load an image from a URI.
216               
217                // So first, write the bytes to a temp file             
218                File tmpImageFile = File.createTempFile("img", ".img");
219               
220                FileOutputStream fos = new FileOutputStream(tmpImageFile);
221                fos.write(bytes);
222                fos.close();
223        log.debug("created tmp file: " + tmpImageFile.getAbsolutePath());
224                               
225                ImageInfo info = ensureFormatIsSupported(tmpImageFile, bytes, true);
226               
227                // In the absence of an exception, tmpImageFile now contains an image
228                // Word will accept
229               
230                ContentTypeManager ctm = opcPackage.getContentTypeManager();
231               
232                // Ensure the relationships part exists
233        if (sourcePart.getRelationshipsPart() == null) {
234                        RelationshipsPart.createRelationshipsPartForPart(sourcePart);
235        }
236
237                String proposedRelId = sourcePart.getRelationshipsPart().getNextId();
238                               
239        String ext = info.getMimeType().substring(info.getMimeType().indexOf("/") + 1);
240               
241//              System.out.println(ext);
242               
243                BinaryPartAbstractImage imagePart = 
244                (BinaryPartAbstractImage) ctm.newPartForContentType(
245                                info.getMimeType(), 
246                createImageName(opcPackage, sourcePart, proposedRelId, ext), null);
247                               
248        log.debug("created part " + imagePart.getClass().getName()
249                + " with name " + imagePart.getPartName().toString());
250               
251                FileInputStream fis = new FileInputStream(tmpImageFile);               
252        imagePart.setBinaryData(fis);
253                               
254        imagePart.rel = sourcePart.addTargetPart(imagePart, proposedRelId);
255               
256                imagePart.setImageInfo(info);
257
258                // Delete the tmp file
259                // As per http://stackoverflow.com/questions/991489/i-cant-delete-a-file-in-java
260                // the following 3 lines are necessary, at least on Win 7 x64
261                // Also reported on Win XP, but in my testing, the files were deleting OK anyway.
262                fos = null;
263                fis = null;
264                System.gc();           
265        if (tmpImageFile.delete()) {
266            log.debug(".. deleted " + tmpImageFile.getAbsolutePath());
267                } else {
268                        log.warn("Couldn't delete tmp file " + tmpImageFile.getAbsolutePath());
269                        tmpImageFile.deleteOnExit();
270                        // If that doesn't work, see "Clean Up Your Mess: Managing Temp Files in Java Apps"
271                        // at devx.com
272                }
273               
274                return imagePart;
275               
276        }
277
278
279        /**
280     * Create an image part from the provided filePath image, attach it to the source part
281     * (eg the main document part, a header part etc), and return it.
282     *
283     * Works for both docx and pptx.
284     *
285     * @param opcPackage
286     * @param sourcePart
287     * @param filePath
288     * @return
289     * @throws Exception
290     */
291    public static BinaryPartAbstractImage createImagePart(
292            OpcPackage opcPackage,
293            Part sourcePart, File imageFile) throws Exception {
294
295        final byte[] locByte = new byte[1];
296
297        //We are in the case that image is not load (no byte Array) so isLoad is false
298        ImageInfo info = ensureFormatIsSupported(imageFile, locByte, false);
299
300        ContentTypeManager ctm = opcPackage.getContentTypeManager();
301
302        // Ensure the relationships part exists
303        if (sourcePart.getRelationshipsPart() == null) {
304            RelationshipsPart.createRelationshipsPartForPart(sourcePart);
305        }
306
307        String proposedRelId = sourcePart.getRelationshipsPart().getNextId();
308
309        String ext = info.getMimeType().substring(info.getMimeType().indexOf("/") + 1);
310
311        BinaryPartAbstractImage imagePart =
312                (BinaryPartAbstractImage) ctm.newPartForContentType(
313                info.getMimeType(),
314                createImageName(opcPackage, sourcePart, proposedRelId, ext), null);
315
316        log.debug("created part " + imagePart.getClass().getName()
317                + " with name " + imagePart.getPartName().toString());
318
319        FileInputStream fis = new FileInputStream(imageFile);
320        imagePart.setBinaryData(fis);
321
322        imagePart.rel = sourcePart.addTargetPart(imagePart, proposedRelId);
323
324        imagePart.setImageInfo(info);
325
326        return imagePart;
327
328    }
329
330        private static ImageInfo ensureFormatIsSupported(File imageFile, byte[] bytes, boolean isLoad) throws Docx4JException, MalformedURLException {
331                return ensureFormatIsSupported(imageFile.toURI().toURL(),  imageFile,  bytes, isLoad);
332        }
333       
334    /**
335         * @param bytes
336         * @param imageFile
337         * @return
338         * @throws Exception
339         * @throws FileNotFoundException
340         * @throws IOException
341         * @throws InterruptedException
342         */
343        private static ImageInfo ensureFormatIsSupported(URL url, File imageFile, byte[] bytes, boolean isLoad) throws Docx4JException {
344               
345                FileOutputStream fos;
346                // ImageInfo can also tell us what sort of image it is 
347               
348                ImageInfo info = null;
349                boolean imagePreloaderFound = true;
350                try {
351                        try {
352                                info = getImageInfo(url);
353
354                                // Debug ... note that these figures
355                                // aren't necessarily accurate for EPS
356                                displayImageInfo(info);
357                        } catch (org.apache.xmlgraphics.image.loader.ImageException e) {
358                               
359                                // Assume: The file format is not supported. No ImagePreloader found for /tmp/img55623.img
360                                // There is no preloader for eg PDFs.
361                                // (To use an image natively, we do need a preloader)
362                                imagePreloaderFound = false;
363                log.warn(e.getMessage());
364                        }
365                       
366            if (imagePreloaderFound
367                    && (info.getMimeType().equals(ContentTypes.IMAGE_TIFF)
368                                        || info.getMimeType().equals(ContentTypes.IMAGE_EMF2) // ImageInfo
369                                        || info.getMimeType().equals(ContentTypes.IMAGE_WMF) 
370                                        || info.getMimeType().equals(ContentTypes.IMAGE_PNG) 
371                                        || info.getMimeType().equals(ContentTypes.IMAGE_JPEG) 
372                                        || info.getMimeType().equals(ContentTypes.IMAGE_GIF) 
373//                                       || info.getMimeType().equals(ContentTypes.IMAGE_EPS)
374                    || info.getMimeType().equals(ContentTypes.IMAGE_BMP))) {
375                                        // TODO: add other supported formats
376                               
377                                // If its a format Word supports natively,
378                                // do nothing here
379                                log.debug(".. supported natively by Word");                                     
380                               
381            } else if (imageFile != null && bytes != null) {
382                               
383                                // otherwise (eg if its an EPS or PDF), try to convert it
384                                // Although the Word UI suggests you can embed an EPS
385                                // directly, Word actually converts it to an EMF;
386                                // Word is unable to read a plain EPS image part.
387                               
388                               
389                                // (TODO: detect failure)
390
391                                log.debug(".. attempting to convert to PNG");           
392                               
393                //If image haven't been load (using function createImagePartFromFilePath), we load it
394                if (isLoad == false) {
395
396                    // So first, we create tmpFile     
397                    File tmpImageFile = File.createTempFile("img", ".img");
398                    fos = new FileOutputStream(tmpImageFile);
399
400                    //Now we get the inputStream, which is represented by imageFile in this case
401                    FileInputStream bais = new FileInputStream(imageFile);
402
403                    //We convert
404                    convertToPNG(bais, fos, density);
405
406                    //We don't forget to change locFile because the new image file is the converted image file!!
407                    imageFile = tmpImageFile;
408                   
409                } //Else image has been load in an array (using function cretaImagePart)
410                else {
411                                        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);                   
412                                        fos = new FileOutputStream(imageFile); 
413                                        convertToPNG(bais, fos, density);
414                }
415
416                                fos.close();
417                                fos = null;
418                               
419                                // We need to refresh image info
420                                imageManager.getCache().clearCache();
421                info = getImageInfo(new URL(imageFile.getAbsolutePath()));
422                               
423                                // Debug ...
424                                displayImageInfo(info);
425                        } else {
426                                throw new Docx4JException("Unsupported linked image type.");
427                        }
428                } catch (Exception e) {
429                        throw new Docx4JException("Error checking image format", e);
430                } 
431                return info;
432        }
433       
434        /**
435         * Create a linked image part, and attach it as a rel of the docx main document part
436         * @param wordMLPackage
437         * @param fileurl
438         * @return
439         * @throws Exception
440         */
441        public static BinaryPartAbstractImage createLinkedImagePart(WordprocessingMLPackage wordMLPackage, 
442                        URL fileurl) throws Exception {
443               
444                return createLinkedImagePart(wordMLPackage,
445                                wordMLPackage.getMainDocumentPart(), fileurl);
446        }
447       
448        /**
449         * Create a linked image part, and attach it as a rel of the specified source part
450         * (eg a header part).
451         *
452         * The current behaviour is that the part is added to the package, but
453         * since the target mode of the rel is external, the part is redundant.
454         *
455         * @param wordMLPackage
456         * @param sourcePart
457         * @param url
458         * @return
459         * @throws Exception
460         */
461        public static BinaryPartAbstractImage createLinkedImagePart(OpcPackage opcPackage, 
462                        Part sourcePart, URL url) throws Exception {
463               
464                log.debug("Incoming url for linked image: " + url.toString() );
465               
466        ImageInfo info = ensureFormatIsSupported(url, null, null, false); // final param doesn't matter in this case
467
468                ContentTypeManager ctm = opcPackage.getContentTypeManager();
469                String proposedRelId = sourcePart.getRelationshipsPart().getNextId();
470                // In order to ensure unique part name,
471                // idea is to use the relId, which ought to be unique
472        String ext = info.getMimeType().substring(info.getMimeType().indexOf("/") + 1);
473               
474                BinaryPartAbstractImage imagePart = 
475                (BinaryPartAbstractImage) ctm.newPartForContentType(
476                                info.getMimeType(), 
477                createImageName(opcPackage, sourcePart, proposedRelId, ext), null);
478               
479                // NB: contents never populated
480                               
481                log.debug("created part " + imagePart.getClass().getName()
482                                + " with name " + imagePart.getPartName().toString());
483
484                imagePart.rel = sourcePart.addTargetPart(imagePart); // want to create rel with suitable name; side effect is to add part
485                imagePart.rel.setTargetMode("External");
486
487                opcPackage.getExternalResources().put(imagePart.getExternalTarget(), 
488                                imagePart);                     
489               
490//              if (!url.getProtocol().equals("file") && new File(url.toString() ).isFile()) {
491//                      imagePart.rel.setTarget("file:///" + url);
492//              } else {
493//                      imagePart.rel.setTarget(url.toString());
494//              }
495                imagePart.rel.setTarget(url.toString());
496
497                imagePart.setImageInfo(info);
498               
499                return imagePart;
500        }       
501
502        /**
503         * Create a <wp:inline> element suitable for this image,
504         * which can be _embedded_ in w:p/w:r/w:drawing.
505         * If the image is wider than the page, it will be scaled
506         * automatically. To avoid the deprecated warning, use the
507         * same method, but with an additional argument of false appended. 
508         * @param filenameHint Any text, for example the original filename
509         * @param altText  Like HTML's alt text
510         * @param id1   An id unique in the document
511         * @param id2   Another id unique in the document
512         * None of these things seem to be exposed in Word 2007's
513         * user interface, but Word won't open the document if
514         * any of the attributes these go in (except @ desc) aren't present!
515         * @throws Exception
516         */
517        @Deprecated
518        public Inline createImageInline(String filenameHint, String altText, 
519                        int id1, int id2) throws Exception {
520               
521        return createImageInline(filenameHint, altText,
522                id1, id2, false);
523
524        }
525       
526        /**
527         * Create a <wp:inline> element suitable for this image,
528         * which can be linked or embedded in w:p/w:r/w:drawing.
529         * If the image is wider than the page, it will be scaled
530         * automatically.
531         * @param filenameHint Any text, for example the original filename
532         * @param altText  Like HTML's alt text
533         * @param id1   An id unique in the document
534         * @param id2   Another id unique in the document
535         * @param link  true if this is to be linked not embedded
536         * None of these things seem to be exposed in Word 2007's
537         * user interface, but Word won't open the document if
538         * any of the attributes these go in (except @ desc) aren't present!
539         * @throws Exception
540         */
541        public Inline createImageInline(String filenameHint, String altText, 
542                        int id1, int id2, boolean link) throws Exception {
543                               
544        WordprocessingMLPackage wmlPackage = ((WordprocessingMLPackage) this.getPackage());
545               
546                List<SectionWrapper> sections = wmlPackage.getDocumentModel().getSections();
547        PageDimensions page = sections.get(sections.size() - 1).getPageDimensions();
548               
549                CxCy cxcy = CxCy.scale(imageInfo, page);
550 
551        return createImageInline(filenameHint, altText,
552                id1, id2, cxcy.getCx(), cxcy.getCy(), link);
553        }
554       
555        /**
556         * Create a <wp:inline> element suitable for this image,
557         * which can be _embedded_ in w:p/w:r/w:drawing. To avoid the deprecated warning, use the
558         * same method, but with an additional argument of false appended.
559         * @param filenameHint Any text, for example the original filename
560         * @param altText  Like HTML's alt text
561         * @param id1   An id unique in the document
562         * @param id2   Another id unique in the document
563         * @param cx    Image width in twip
564         * None of these things seem to be exposed in Word 2007's
565         * user interface, but Word won't open the document if
566         * any of the attributes these go in (except @ desc) aren't present!
567         * @throws Exception
568         */
569        @Deprecated
570        public Inline createImageInline(String filenameHint, String altText, 
571                        int id1, int id2, long cx) throws Exception {
572               
573        return createImageInline(filenameHint, altText,
574                id1, id2, cx, false);
575
576        }
577       
578        /**
579         * Create a <wp:inline> element suitable for this image,
580         * which can be _embedded_ in w:p/w:r/w:drawing.
581         * @param filenameHint Any text, for example the original filename
582         * @param altText  Like HTML's alt text
583         * @param id1   An id unique in the document
584         * @param id2   Another id unique in the document
585         * @param cx    Image width in twip
586         * @param link  true if this is to be linked not embedded
587         * None of these things seem to be exposed in Word 2007's
588         * user interface, but Word won't open the document if
589         * any of the attributes these go in (except @ desc) aren't present!
590         * @throws Exception
591         */
592        public Inline createImageInline(String filenameHint, String altText, 
593                        int id1, int id2, long cx, boolean link) throws Exception {
594               
595                ImageSize size = imageInfo.getSize();
596
597                Dimension2D dPt = size.getDimensionPt();
598                double imageWidthTwips = dPt.getWidth() * 20;
599                log.debug("imageWidthTwips: " + imageWidthTwips);
600
601                long cy;
602
603                log.debug("Scaling image height to retain aspect ratio");
604                cy = UnitsOfMeasurement.twipToEMU(dPt.getHeight() * 20 * cx / imageWidthTwips);
605
606                // Now convert cx to EMU
607                cx = UnitsOfMeasurement.twipToEMU(cx);
608               
609
610                log.debug("cx=" + cx + "; cy=" + cy);
611
612                return createImageInline(filenameHint, altText, id1, id2, cx, cy, link);               
613        }
614
615        /**
616         * Create a <wp:inline> element suitable for this image, which can be
617         * linked or embedded in w:p/w:r/w:drawing, specifying height and width.  Note
618         * that you'd ordinarily use one of the methods which don't require
619         * you to specify height (cy).
620         *
621         * @param filenameHint
622         *            Any text, for example the original filename
623         * @param altText
624         *            Like HTML's alt text
625         * @param id1
626         *            An id unique in the document
627         * @param id2
628         *            Another id unique in the document None of these things seem to
629         *            be exposed in Word 2007's user interface, but Word won't open
630         * the document if any of the attributes these go in (except @ desc) aren't
631         *            present!
632         * @param cx    Image width in EMU
633         * @param cy    Image height in EMU
634         * @param link  true if this is to be linked not embedded
635         * @throws Exception
636         */
637        public Inline createImageInline(String filenameHint, String altText, 
638                        int id1, int id2, long cx, long cy, boolean link) throws Exception {
639               
640        if (filenameHint == null) {
641                        filenameHint = "";
642                }
643        if (altText == null) {
644                        altText = "";
645                }
646               
647                String type;
648                if (link) {
649                        type = "r:link";
650                } else {
651                        type = "r:embed";
652                }
653               
654        String ml =
655//              "<w:p ><w:r>" +
656//        "<w:drawing>" +
657                "<wp:inline distT=\"0\" distB=\"0\" distL=\"0\" distR=\"0\"" + namespaces + ">"
658                + "<wp:extent cx=\"${cx}\" cy=\"${cy}\"/>"
659                + "<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>" + //l=\"19050\"
660                "<wp:docPr id=\"${id1}\" name=\"${filenameHint}\" descr=\"${altText}\"/><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" noChangeAspect=\"1\"/></wp:cNvGraphicFramePr><a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">"
661                + "<a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
662                + "<pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\"><pic:nvPicPr><pic:cNvPr id=\"${id2}\" name=\"${filenameHint}\"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill>"
663                + "<a:blip " + type + "=\"${rEmbedId}\"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill>"
664                + "<pic:spPr><a:xfrm><a:off x=\"0\" y=\"0\"/><a:ext cx=\"${cx}\" cy=\"${cy}\"/></a:xfrm><a:prstGeom prst=\"rect\"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic>"
665                + "</wp:inline>"; // +
666//        "</w:drawing>" +
667//        "</w:r></w:p>";
668        java.util.HashMap<String, String> mappings = new java.util.HashMap<String, String>();
669       
670        mappings.put("cx", Long.toString(cx));
671        mappings.put("cy", Long.toString(cy));
672        mappings.put("filenameHint", filenameHint);
673        mappings.put("altText", altText);
674        mappings.put("rEmbedId", rel.getId());
675        mappings.put("id1", Integer.toString(id1));
676        mappings.put("id2", Integer.toString(id2));
677
678        Object o = org.docx4j.XmlUtils.unmarshallFromTemplate(ml, mappings);
679        Inline inline = (Inline) ((JAXBElement) o).getValue();
680       
681                return inline;         
682        }
683    final static String namespaces = " xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" "
684            + "xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" "
685            + "xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\"";
686       
687        public static ImageInfo getImageInfo(URL url) throws Exception {
688               
689                // XmlGraphics images caches images by their URI;
690                // therefore it can only load images from a URI, rather
691                // than say a byte array, byte buffer, or input stream.
692
693                ImageSessionContext sessionContext = new DefaultImageSessionContext(
694                                imageManager.getImageContext(), null);
695
696                ImageInfo info = imageManager.getImageInfo(url.toString(), sessionContext);
697               
698                // Note that these figures do not appear to be reliable for EPS
699                // eg ImageMagick 6.2.4 10/02/07 Q16
700                // identify fig1.eps
701                // reports:
702                // fig1.eps PS 516x429 516x429+0+0 DirectClass 869kb
703                // whereas ImageInfo reports 1147x953
704               
705                /* Note2: odd results for PNG?
706                 *
707                        If for an image, ImageMagick (v.6.2.4 and 6.3.7) identify says:
708                       
709                          Resolution: 320x320 (or whatever)
710                          Units: Undefined  <---------------------
711
712                        then ImageInfo will report a default value, using Toolkit.getDefaultToolkit().getScreenResolution(),
713                        which may be say 160.
714                       
715                        To prevent the "Undefined", be sure to use -units when you call convert.
716                       
717                 * When PreloaderImageIO.preloadImage does:
718
719                                ImageIOUtil.extractResolution(iiometa, size);
720                       
721                        it is finding the Dimension child, but not "HorizontalPixelSize" or VerticalPixelSize (these are null).
722                 * */
723               
724                return info;
725               
726        }
727       
728        public static void main(String[] args) throws Exception {
729               
730                //String uri = System.getProperty("user.dir") + "/sample-docs/metafile-samples/gradient.emf";
731                String uri = System.getProperty("user.dir") + "/sample-docs/metafile-samples/freehand_picture_saveas.wmf";
732                System.out.println(uri);
733               
734                //String uri = "/tmp/img4448.img";
735               
736                ImageInfo ii = getImageInfo(new URL(uri));
737               
738                displayImageInfo(ii);
739        }
740       
741    public static void displayImageInfo(ImageInfo info) {
742       
743                  ImageSize size = info.getSize();
744                 
745                  Dimension2D dPt = size.getDimensionPt();
746                  Dimension dPx = size.getDimensionPx();
747
748                  log.debug(info.getOriginalURI() + " " + info.getMimeType() 
749                + " " + Math.round(dPx.getWidth()) + "x" + Math.round(dPx.getHeight()));
750                                 
751        log.debug("Resolution:" + Math.round(size.getDpiHorizontal()) + "x" + Math.round(size.getDpiVertical()));
752        log.debug("Print size: " + Math.round(dPt.getWidth() / 72) + "\" x" + Math.round(dPt.getHeight() / 72) + "\"");
753               
754        }
755       
756        /**
757         * Convenience method, given a Graphic in a document,
758         * to get the byte[] representing
759         * the associated image
760         *
761         * @param wmlPkg
762         * @param graphic
763         * @return
764         */
765        public static byte[] getImage(WordprocessingMLPackage wmlPkg,
766                        org.docx4j.dml.Graphic graphic) {
767               
768                if (wmlPkg == null 
769                        || wmlPkg.getMainDocumentPart() == null
770                        || wmlPkg.getMainDocumentPart().getRelationshipsPart() == null) {
771                        return null;
772                }
773               
774                Pic pic = graphic.getGraphicData().getPic();
775                String rId = pic.getBlipFill().getBlip().getEmbed();
776                if (rId.equals("")) {
777                        rId = pic.getBlipFill().getBlip().getLink();
778                }
779                log.debug("Image rel id: " + rId);
780                org.docx4j.relationships.Relationship rel = 
781                        wmlPkg.getMainDocumentPart().getRelationshipsPart().getRelationshipByID(rId);
782                if (rel != null) {
783                        org.docx4j.openpackaging.parts.Part part = 
784                                wmlPkg.getMainDocumentPart().getRelationshipsPart().getPart(rel);
785                        if (part == null) {
786                                log.error("Couldn't get Part!");
787                        } else if (part instanceof org.docx4j.openpackaging.parts.WordprocessingML.BinaryPart) {
788                                log.debug("getting bytes...");
789                                org.docx4j.openpackaging.parts.WordprocessingML.BinaryPart binaryPart =
790                                        (org.docx4j.openpackaging.parts.WordprocessingML.BinaryPart) part;
791                                java.nio.ByteBuffer bb = binaryPart.getBuffer();
792                bb.clear();
793                byte[] bytes = new byte[bb.capacity()];
794                bb.get(bytes, 0, bytes.length);
795
796                                return bytes;
797                        } else {                               
798                log.error("Part was a " + part.getClass().getName());
799                        }
800                } else {
801                        log.error("Couldn't find rel " + rId);
802                }
803               
804                return null;
805        }
806       
807        public static class CxCy {
808               
809                long cx;
810
811                /**
812                 * @return the resulting cx
813                 */
814                public long getCx() {
815                        return cx;
816                }
817                long cy;
818
819                /**
820                 * @return the resulting cy
821                 */
822                public long getCy() {
823                        return cy;
824                }
825                boolean scaled;
826
827                /**
828                 * @return whether it was necessary to scale
829                 * the image to fit the page width
830                 */
831                public boolean isScaled() {
832                        return scaled;
833                }
834               
835                CxCy(long cx, long cy, boolean scaled) {
836                       
837                        this.cx = cx;
838                        this.cy = cy;
839                        this.scaled = scaled;
840                       
841                }
842               
843                public static CxCy scale(ImageInfo imageInfo, PageDimensions page) {
844                       
845                        double writableWidthTwips = page.getWritableWidthTwips();                               
846                        log.debug("writableWidthTwips: " + writableWidthTwips);
847                       
848                          ImageSize size = imageInfo.getSize();
849                         
850                          Dimension2D dPt = size.getDimensionPt();
851                        double imageWidthTwips = dPt.getWidth() * 20;
852                        log.debug("imageWidthTwips: " + imageWidthTwips);
853                       
854                        long cx;
855                        long cy;
856                        boolean scaled = false;
857            if (imageWidthTwips > writableWidthTwips) {
858                               
859                                log.debug("Scaling image to fit page width");
860                                scaled = true;
861                               
862                                cx = UnitsOfMeasurement.twipToEMU(writableWidthTwips);
863                cy = UnitsOfMeasurement.twipToEMU(dPt.getHeight() * 20 * writableWidthTwips / imageWidthTwips);
864                               
865                        } else {
866
867                                log.debug("Scaling image - not necessary");
868                               
869                                cx = UnitsOfMeasurement.twipToEMU(imageWidthTwips);
870                                cy = UnitsOfMeasurement.twipToEMU(dPt.getHeight() * 20);                       
871                               
872                        }
873                       
874                        log.debug("cx=" + cx + "; cy=" + cy);
875                       
876                        return new CxCy(cx, cy, scaled);
877                       
878                       
879                }
880        }
881
882        /**
883         * Convert image formats which are not supported by Word (eg EPS, PDF),
884         * into ones which are.  This requires ImageMagick to be on your
885         * system's path; for EPS and PDF images, Ghostscript is also required.
886         *
887         * @param is
888         * @param os
889         * @param density  PixelsPerInch
890         * @throws IOException
891         * @throws InterruptedException
892         */
893    public static void convertToPNG(InputStream is, OutputStream os, int density) throws IOException, InterruptedException {
894               
895        /*
896         * See http://www.eichberger.de/2006/05/imagemagick-in-servlets.html
897         *
898         * "Calling 'convert - png:-' as an external command and feeding it the
899         * source image as standard input and reading the converted image
900         * (in this case png) as standard output"
901         *
902         */
903               
904         log.info("Start ImageMagick...");
905         Process p = Runtime.getRuntime().exec("imconvert -density " + density + " -units PixelsPerInch - png:-"); 
906         
907         // GraphicsMagick is a little quicker than ImageMagick,
908         // but v1.3.3 (of Dec 2008) still has the now fixed in GM bug
909         // whereby the right most ~10% of the resulting image is chopped off
910         //Process p = Runtime.getRuntime().exec("gm convert -density " + density + " -units PixelsPerInch - png:-"); 
911         
912         /* On Windows, if this results in "Invalid Parameter",
913          * then either ImageMagick is not installed,
914          * or exec is finding the wrong convert
915          * program.  See http://studio.imagemagick.org/pipermail/magick-users/2005-October/016464.html
916          * and http://www.imagemagick.org/discourse-server/viewtopic.php?f=1&t=8324&start=0
917          *
918          * Rather than use full path, rename convert to imconvert (which Alfresco and others do)
919          *
920          */
921         
922         //initialize Gobblers
923         StreamGobbler inGobbler = new StreamGobbler(p.getInputStream(), os);
924         StreamGobbler errGobbler = new StreamGobbler(p.getErrorStream(), System.err);
925         //start them
926         inGobbler.start();
927         errGobbler.start();
928         
929         // p.getOutputStream() is the _output stream_ of the subprocess, so
930         // this copies is into the standard input stream of the process
931         try {
932                 copy2(is, new BufferedOutputStream(p.getOutputStream()));
933                 p.getOutputStream().close();
934                 log.debug("Image copied...");
935         } catch (IOException ioe) {
936                 
937                 ioe.printStackTrace();
938                 // debug
939                 copy2(p.getErrorStream(), System.err);
940         }
941         
942        if (p.waitFor() != 0) {
943          log.error("Error");
944         }
945         log.debug("End Process...");
946        }
947
948        public static void copy2(InputStream is, OutputStream os) throws IOException {
949            byte[] buffer = new byte[512];
950            while (true) {
951             int bytesRead = is.read(buffer);
952            if (bytesRead == -1) {
953                break;
954            }
955             os.write(buffer, 0, bytesRead);
956            }
957            os.flush();
958           }//method
959        }//class
960
961class StreamGobbler extends Thread {
962                // The term "StreamGobbler" was taken from an article called "When Runtime.exec() won't",
963                // see http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html.
964               
965          InputStream is;
966          OutputStream os;
967
968    StreamGobbler(InputStream is, OutputStream redirect) {
969        this.is = new BufferedInputStream(is);
970              this.os = redirect;
971          }
972
973    public void run() {
974        try {
975                  BinaryPartAbstractImage.copy2(is, os);
976        } catch (IOException ioe) {
977                  ioe.printStackTrace();
978                  }
979          }
980        }
Note: See TracBrowser for help on using the repository browser.