source: trunk/docx4j/src/main/java/org/docx4j/openpackaging/packages/WordprocessingMLPackage.java @ 1667

Revision 1667, 16.7 KB checked in by jharrop, 5 months ago (diff)

Albert Aymerich's patch to load content (docx and xml) from inputstreams.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
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 */
20
21package org.docx4j.openpackaging.packages;
22
23
24import java.io.FileInputStream;
25import java.io.FileNotFoundException;
26import java.io.FileOutputStream;
27import java.io.InputStream;
28import java.util.HashMap;
29import java.util.Map;
30
31import javax.xml.bind.JAXBContext;
32import javax.xml.bind.JAXBElement;
33import javax.xml.bind.JAXBException;
34import javax.xml.bind.Marshaller;
35import javax.xml.bind.PropertyException;
36import javax.xml.bind.Unmarshaller;
37import javax.xml.transform.Source;
38import javax.xml.transform.Templates;
39import javax.xml.transform.stream.StreamSource;
40
41import org.apache.log4j.Logger;
42import org.docx4j.Docx4jProperties;
43import org.docx4j.XmlUtils;
44import org.docx4j.convert.out.flatOpcXml.FlatOpcXmlCreator;
45import org.docx4j.convert.out.pdf.viaXSLFO.Conversion;
46import org.docx4j.convert.out.pdf.viaXSLFO.PartTracker;
47import org.docx4j.fonts.IdentityPlusMapper;
48import org.docx4j.fonts.Mapper;
49import org.docx4j.jaxb.Context;
50import org.docx4j.jaxb.NamespacePrefixMapperUtils;
51import org.docx4j.model.TransformState;
52import org.docx4j.model.structure.DocumentModel;
53import org.docx4j.model.structure.HeaderFooterPolicy;
54import org.docx4j.model.structure.PageDimensions;
55import org.docx4j.model.structure.PageSizePaper;
56import org.docx4j.openpackaging.contenttype.ContentType;
57import org.docx4j.openpackaging.contenttype.ContentTypeManager;
58import org.docx4j.openpackaging.contenttype.ContentTypes;
59import org.docx4j.openpackaging.exceptions.Docx4JException;
60import org.docx4j.openpackaging.exceptions.InvalidFormatException;
61import org.docx4j.openpackaging.io.LoadFromZipNG;
62import org.docx4j.openpackaging.io.SaveToZipFile;
63import org.docx4j.openpackaging.parts.DocPropsCorePart;
64import org.docx4j.openpackaging.parts.DocPropsCustomPart;
65import org.docx4j.openpackaging.parts.DocPropsExtendedPart;
66import org.docx4j.openpackaging.parts.JaxbXmlPart;
67import org.docx4j.openpackaging.parts.Part;
68import org.docx4j.openpackaging.parts.WordprocessingML.FontTablePart;
69import org.docx4j.openpackaging.parts.WordprocessingML.GlossaryDocumentPart;
70import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
71import org.docx4j.openpackaging.parts.opendope.ConditionsPart;
72import org.docx4j.openpackaging.parts.opendope.XPathsPart;
73import org.docx4j.openpackaging.parts.relationships.Namespaces;
74import org.docx4j.wml.Document;
75import org.docx4j.wml.SectPr;
76import org.docx4j.wml.Styles;
77
78
79
80
81
82
83
84/**
85 * @author jharrop
86 *
87 */
88public class WordprocessingMLPackage extends OpcPackage {
89       
90        // What is a Word document these days?
91        //
92        // Well, a package is a logical entity which holds a collection of parts       
93        // And a word document is exactly a WordProcessingML package   
94        // Which has a Main Document Part, and optionally, a Glossary Document Part
95
96        /* So its a Word doc if:
97         * 1. _rels/.rels tells you where to find an office document
98         * 2. [Content_Types].xml tells you that office document is   
99         *    of content type application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml
100       
101         * A minimal docx has:
102         *
103         * [Content_Types].xml containing:
104         * 1. <Default Extension="rels" ...
105         * 2. <Override PartName="/word/document.xml"...
106         *
107         * _rels/.rels with a target for word/document.xml
108         *
109         * word/document.xml
110         */
111       
112        protected static Logger log = Logger.getLogger(WordprocessingMLPackage.class);
113               
114       
115        // Main document
116        protected MainDocumentPart mainDoc;
117       
118        // (optional) Glossary document
119        protected GlossaryDocumentPart glossaryDoc;
120       
121        private DocumentModel documentModel;
122        public DocumentModel getDocumentModel() {
123                if (documentModel==null) {
124                        documentModel = new DocumentModel(this);
125                }
126                return documentModel;
127        }
128       
129       
130        private HeaderFooterPolicy headerFooterPolicy; 
131        @Deprecated     
132        public HeaderFooterPolicy getHeaderFooterPolicy() {
133                int last = getDocumentModel().getSections().size();
134                if (last>0) {
135                        // Should always be the case, since we add one,
136                        // even if the document contains no sectPr
137                        return getDocumentModel().getSections().get(last-1).getHeaderFooterPolicy();
138                } else {
139                        log.error("Unexpected - zero sections?!");
140                        return null;
141                }
142        }
143       
144        /**
145         * Constructor.  Also creates a new content type manager
146         *
147         */     
148        public WordprocessingMLPackage() {
149                super();
150                setContentType(new ContentType(ContentTypes.WORDPROCESSINGML_DOCUMENT));
151        }
152        /**
153         * Constructor.
154         * 
155         * @param contentTypeManager
156         *            The content type manager to use
157         */
158        public WordprocessingMLPackage(ContentTypeManager contentTypeManager) {
159                super(contentTypeManager);
160                setContentType(new ContentType(ContentTypes.WORDPROCESSINGML_DOCUMENT));
161        }
162       
163        /**
164         * Convenience method to create a WordprocessingMLPackage
165         * from an existing File (.docx zip or .xml Flat OPC).
166     *
167         * @param docxFile
168         *            The docx file
169         */     
170        public static WordprocessingMLPackage load(java.io.File docxFile) throws Docx4JException {
171               
172                return (WordprocessingMLPackage)OpcPackage.load(docxFile);
173        }
174
175        /**
176         * Convenience method to create a WordprocessingMLPackage
177         * from an existing stream(.docx zip or .xml Flat OPC).
178     *
179         * @param docxFile
180         *            The docx file
181         */     
182        public static WordprocessingMLPackage load(InputStream is) throws Docx4JException {
183               
184                return (WordprocessingMLPackage)OpcPackage.load(is);
185        }
186       
187        /**
188         * Convenience method to save a WordprocessingMLPackage
189         * to a File.
190     *
191         * @param docxFile
192         *            The docx file
193         */     
194        public void save(java.io.File docxFile) throws Docx4JException {
195
196                if (docxFile.getName().endsWith(".xml")) {
197                       
198                        // Create a org.docx4j.wml.Package object
199                        FlatOpcXmlCreator worker = new FlatOpcXmlCreator(this);
200                        org.docx4j.xmlPackage.Package pkg = worker.get();
201               
202                // Now marshall it
203                        JAXBContext jc = Context.jcXmlPackage;
204                        try {
205                                Marshaller marshaller=jc.createMarshaller();
206                               
207                                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
208                                NamespacePrefixMapperUtils.setProperty(marshaller,
209                                                NamespacePrefixMapperUtils.getPrefixMapper());                 
210                               
211                                marshaller.marshal(pkg, new FileOutputStream(docxFile));
212                        } catch (Exception e) {
213                                throw new Docx4JException("Error saving Flat OPC XML", e);
214                        }       
215                        return;
216                }
217                       
218                SaveToZipFile saver = new SaveToZipFile(this);
219                saver.save(docxFile);
220        }
221       
222       
223        public boolean setPartShortcut(Part part, String relationshipType) {
224               
225//              log.info("setPartShortcut for part " + part.getClass().getName() );
226               
227                if (relationshipType.equals(Namespaces.PROPERTIES_CORE)) {
228                        docPropsCorePart = (DocPropsCorePart)part;
229                        log.debug("Set shortcut for docPropsCorePart");
230                        return true;                   
231                } else if (relationshipType.equals(Namespaces.PROPERTIES_EXTENDED)) {
232                        docPropsExtendedPart = (DocPropsExtendedPart)part;
233                        log.debug("Set shortcut for docPropsExtendedPart");
234                        return true;                   
235                } else if (relationshipType.equals(Namespaces.PROPERTIES_CUSTOM)) {
236                        docPropsCustomPart = (DocPropsCustomPart)part;
237                        log.debug("Set shortcut for docPropsCustomPart");
238                        return true;                   
239                } else if (relationshipType.equals(Namespaces.DOCUMENT)) {
240                        mainDoc = (MainDocumentPart)part;
241                        log.debug("Set shortcut for mainDoc");
242                        return true;
243                } else {       
244                        return false;
245                }
246        }
247       
248        public MainDocumentPart getMainDocumentPart() {
249                return mainDoc;
250        }
251       
252   
253    /**
254     * Use an XSLT to alter the contents of this package.
255     * The output of the transformation must be valid
256     * pck:package/pck:part format, as emitted by Word 2007.
257     *
258     * @param is
259     * @param transformParameters
260     * @throws Exception
261     */   
262    public void transform(Templates xslt,
263                          Map<String, Object> transformParameters) throws Exception {
264
265        // Prepare in the input document
266       
267                FlatOpcXmlCreator worker = new FlatOpcXmlCreator(this);
268                org.docx4j.xmlPackage.Package pkg = worker.get();
269       
270                JAXBContext jc = Context.jcXmlPackage;
271                Marshaller marshaller=jc.createMarshaller();
272                org.w3c.dom.Document doc = org.docx4j.XmlUtils.neww3cDomDocument();
273                marshaller.marshal(pkg, doc);
274                       
275//              javax.xml.bind.util.JAXBResult result = new javax.xml.bind.util.JAXBResult(jc );
276               
277                // Use constructor which takes Unmarshaller, rather than JAXBContext,
278                // so we can set JaxbValidationEventHandler
279                Unmarshaller u = jc.createUnmarshaller();
280                u.setEventHandler(new org.docx4j.jaxb.JaxbValidationEventHandler());
281                javax.xml.bind.util.JAXBResult result = new javax.xml.bind.util.JAXBResult(u );
282               
283                // Perform the transformation           
284                org.docx4j.XmlUtils.transform(doc, xslt, transformParameters, result);
285               
286
287                //org.docx4j.xmlPackage.Package wmlPackageEl = (org.docx4j.xmlPackage.Package)result.getResult();
288                javax.xml.bind.JAXBElement je = (javax.xml.bind.JAXBElement)result.getResult();
289                org.docx4j.xmlPackage.Package wmlPackageEl = (org.docx4j.xmlPackage.Package)je.getValue();
290                org.docx4j.convert.in.FlatOpcXmlImporter xmlPackage = new org.docx4j.convert.in.FlatOpcXmlImporter( wmlPackageEl);
291               
292                ContentTypeManager ctm = new ContentTypeManager();
293               
294                Part tmpDocPart = xmlPackage.getRawPart(ctm,  "/word/document.xml", null);
295                Part tmpStylesPart = xmlPackage.getRawPart(ctm,  "/word/styles.xml", null);
296               
297                // This code assumes all the existing rels etc of
298                // the existing main document part are still relevant.
299//              if (wmlDocument==null) {
300//                      log.warn("Couldn't get main document part from package transform result!");                     
301//              } else {
302//                      this.getMainDocumentPart().setJaxbElement(wmlDocument);
303//              }       
304                this.getMainDocumentPart().setJaxbElement(
305                                ((JaxbXmlPart<Document>) tmpDocPart).getJaxbElement() );
306//                             
307//              if (wmlStyles==null) {
308//                      log.warn("Couldn't get style definitions part from package transform result!");                 
309//              } else {
310//                      this.getMainDocumentPart().getStyleDefinitionsPart().setJaxbElement(wmlStyles);
311//              }
312                this.getMainDocumentPart().getStyleDefinitionsPart().setJaxbElement(
313                                ((JaxbXmlPart<Styles>) tmpStylesPart).getJaxbElement() );
314       
315    }
316   
317    public void filter( FilterSettings filterSettings ) throws Exception {
318
319        if (filterTemplate==null) { // first use
320                        Source xsltSource = new StreamSource(
321                                org.docx4j.utils.ResourceUtils.getResource(
322                                                "org/docx4j/openpackaging/packages/filter.xslt"));
323                        filterTemplate = XmlUtils.getTransformerTemplate(xsltSource);
324        }
325        transform(filterTemplate, filterSettings.getSettings() );
326       
327    }
328
329    static Templates filterTemplate;
330   
331/* There should be a mapper per document,
332 * but PhysicalFonts should be system wide.
333 *
334 * The only way PhysicalFonts will change
335 * is if fonts are added/removed while
336 * docx4j is executing (which can happen eg if an
337 * obfuscated font part is read)
338 */
339
340    public void setFontMapper(Mapper fm) throws Exception {
341        log.debug("setFontMapper invoked");
342        if (fm == null) {
343                throw new IllegalArgumentException("Font Substituter cannot be null.");
344        }
345                fontMapper = fm;
346                org.docx4j.wml.Fonts fonts = null;
347
348                // 1.  Get a list of all the fonts in the document
349                java.util.Map fontsInUse = this.getMainDocumentPart().fontsInUse();
350               
351                //if ( fm instanceof BestMatchingMapper ) {
352                if ( fm.getClass().getName().equals("org.docx4j.fonts.BestMatchingMapper") ) {
353                       
354                       
355                        // 2.  For each font, find the closest match on the system (use OO's VCL.xcu to do this)
356                        //     - do this in a general way, since docx4all needs this as well to display fonts           
357                        FontTablePart fontTablePart= this.getMainDocumentPart().getFontTablePart();     
358                       
359                        if (fontTablePart==null) {
360                                log.warn("FontTable missing; creating default part.");
361                                fontTablePart= new org.docx4j.openpackaging.parts.WordprocessingML.FontTablePart();
362                                fontTablePart.unmarshalDefaultFonts();
363                                fontTablePart.processEmbeddings();
364                        }
365                       
366                        fonts = (org.docx4j.wml.Fonts)fontTablePart.getJaxbElement();
367                }
368               
369                fontMapper.populateFontMappings(fontsInUse, fonts);     
370       
371    }
372
373    public Mapper getFontMapper() {
374        if (fontMapper==null) {
375                fontMapper = new IdentityPlusMapper();
376                // This invokes populateFontMappings
377                try {
378                                setFontMapper(fontMapper);
379                        } catch (Exception e) {
380                                // TODO Auto-generated catch block
381                                e.printStackTrace();
382                        }
383        }
384                return fontMapper;
385        }
386
387        private Mapper fontMapper;
388       
389       
390        private String defaultFont;
391        public String getDefaultFont() {
392               
393                if (defaultFont==null) {
394                        defaultFont = mainDoc.getPropertyResolver().getDefaultFont();
395                        log.debug("Identified default font: " + defaultFont);
396                }
397                return defaultFont;             
398        }
399       
400
401        /**
402         * Creates a WordprocessingMLPackage, using default page size and orientation.
403         * From 2.7.1, these are read from docx4j.properties, or if not found, default
404         * to A4 portrait.
405         */
406        public static WordprocessingMLPackage createPackage() throws InvalidFormatException {
407               
408                String papersize= Docx4jProperties.getProperties().getProperty("docx4j.PageSize", "A4");
409                log.info("Using paper size: " + papersize);
410               
411                String landscapeString = Docx4jProperties.getProperties().getProperty("docx4j.PageOrientationLandscape", "false");
412                boolean landscape= Boolean.parseBoolean(landscapeString);
413                log.info("Landscape orientation: " + landscape);
414               
415                return createPackage(
416                                PageSizePaper.valueOf(papersize), landscape);
417        }
418       
419        public static WordprocessingMLPackage createPackage(PageSizePaper sz, boolean landscape ) throws InvalidFormatException {
420               
421                               
422                // Create a package
423                WordprocessingMLPackage wmlPack = new WordprocessingMLPackage();
424
425                // Create main document part
426                MainDocumentPart wordDocumentPart = new MainDocumentPart();             
427               
428                // Create main document part content
429                org.docx4j.wml.ObjectFactory factory = Context.getWmlObjectFactory();
430                org.docx4j.wml.Body  body = factory.createBody();               
431                org.docx4j.wml.Document wmlDocumentEl = factory.createDocument();
432               
433                wmlDocumentEl.setBody(body);
434               
435                // Create a basic sectPr using our Page model
436                PageDimensions page = new PageDimensions();
437                page.setPgSize(sz, landscape);
438               
439                SectPr sectPr = factory.createSectPr();
440                body.setSectPr(sectPr);
441                sectPr.setPgSz(  page.getPgSz() );
442                sectPr.setPgMar( page.getPgMar() );
443                               
444                // Put the content in the part
445                wordDocumentPart.setJaxbElement(wmlDocumentEl);
446                                               
447                // Add the main document part to the package relationships
448                // (creating it if necessary)
449                wmlPack.addTargetPart(wordDocumentPart);
450                               
451                // Create a styles part
452                Part stylesPart = new org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart();
453                try {
454                        ((org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart) stylesPart)
455                                        .unmarshalDefaultStyles();
456                       
457                        // Add the styles part to the main document part relationships
458                        // (creating it if necessary)
459                        wordDocumentPart.addTargetPart(stylesPart); // NB - add it to main doc part, not package!                       
460                       
461                } catch (Exception e) {
462                        // TODO: handle exception
463                        //e.printStackTrace(); 
464                        log.error(e);
465                }
466               
467                // Metadata: docx4j 2.7.1 can populate some of this from docx4j.properties
468                DocPropsCorePart core = new DocPropsCorePart();
469                org.docx4j.docProps.core.ObjectFactory coreFactory = new org.docx4j.docProps.core.ObjectFactory();
470                core.setJaxbElement(coreFactory.createCoreProperties() );
471                wmlPack.addTargetPart(core);
472               
473                DocPropsExtendedPart app = new DocPropsExtendedPart();
474                org.docx4j.docProps.extended.ObjectFactory extFactory = new org.docx4j.docProps.extended.ObjectFactory();
475                app.setJaxbElement(extFactory.createProperties() );
476                wmlPack.addTargetPart(app);             
477               
478                // Return the new package
479                return wmlPack;
480               
481        }
482
483        public static class FilterSettings {
484               
485                Boolean removeProofErrors = Boolean.FALSE;             
486                public void setRemoveProofErrors(boolean val) {
487                        removeProofErrors = new Boolean(val);
488                }
489
490                Boolean removeContentControls = Boolean.FALSE;         
491                public void setRemoveContentControls(boolean val) {
492                        removeContentControls = new Boolean(val);
493                }
494               
495                Boolean removeRsids = Boolean.FALSE;           
496                public void setRemoveRsids(boolean val) {
497                        removeRsids = new Boolean(val);
498                }
499               
500                Boolean tidyForDocx4all = Boolean.FALSE;               
501                public void setTidyForDocx4all(boolean val) {
502                        tidyForDocx4all = new Boolean(val);
503                }
504               
505               
506                public Map<String, Object> getSettings() {
507                        Map<String, Object> settings = new java.util.HashMap<String, Object>();
508                       
509                        settings.put("removeProofErrors", removeProofErrors);
510                        settings.put("removeContentControls", removeContentControls);
511                        settings.put("removeRsids", removeRsids);
512                        settings.put("tidyForDocx4all", tidyForDocx4all);
513                       
514                        return settings;
515                }
516               
517               
518        }
519
520
521       
522}
Note: See TracBrowser for help on using the repository browser.