source: trunk/docx4j/src/main/java/org/docx4j/openpackaging/parts/WordprocessingML/MainDocumentPart.java @ 1777

Revision 1777, 27.8 KB checked in by jharrop, 6 weeks ago (diff)

Convenience method to create hyperlink to bookmark, plus example of use.

  • 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.parts.WordprocessingML;
22
23
24
25import java.util.ArrayList;
26import java.util.HashMap;
27import java.util.Iterator;
28import java.util.List;
29import java.util.Map;
30import java.util.Stack;
31
32import javax.xml.bind.Binder;
33import javax.xml.bind.JAXBElement;
34import javax.xml.bind.JAXBException;
35import javax.xml.bind.UnmarshalException;
36import javax.xml.bind.Unmarshaller;
37import javax.xml.bind.util.JAXBResult;
38import javax.xml.parsers.DocumentBuilderFactory;
39import javax.xml.transform.Result;
40import javax.xml.transform.Templates;
41import javax.xml.transform.dom.DOMResult;
42
43import org.apache.log4j.Logger;
44import org.docx4j.TraversalUtil;
45import org.docx4j.XmlUtils;
46import org.docx4j.TraversalUtil.CallbackImpl;
47import org.docx4j.dml.CTNonVisualDrawingProps;
48import org.docx4j.jaxb.Context;
49import org.docx4j.jaxb.JaxbValidationEventHandler;
50import org.docx4j.model.PropertyResolver;
51import org.docx4j.model.listnumbering.AbstractListNumberingDefinition;
52import org.docx4j.model.styles.StyleTree;
53import org.docx4j.openpackaging.exceptions.Docx4JException;
54import org.docx4j.openpackaging.exceptions.InvalidFormatException;
55import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
56import org.docx4j.openpackaging.parts.Part;
57import org.docx4j.openpackaging.parts.PartName;
58import org.docx4j.openpackaging.parts.relationships.Namespaces;
59import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
60import org.docx4j.relationships.Relationship;
61import org.docx4j.wml.Body;
62import org.docx4j.wml.CTEndnotes;
63import org.docx4j.wml.CTFootnotes;
64import org.docx4j.wml.CTShd;
65import org.docx4j.wml.Comments;
66import org.docx4j.wml.ContentAccessor;
67import org.docx4j.wml.Ftr;
68import org.docx4j.wml.Hdr;
69import org.docx4j.wml.Lvl;
70import org.docx4j.wml.Numbering;
71import org.docx4j.wml.P;
72import org.docx4j.wml.SdtBlock;
73import org.docx4j.wml.SdtContentBlock;
74import org.docx4j.wml.SdtPr;
75import org.docx4j.wml.Style;
76import org.docx4j.wml.Styles;
77import org.docx4j.wml.Tag;
78import org.docx4j.wml.Tc;
79import org.docx4j.wml.P.Hyperlink;
80import org.docx4j.wml.PPrBase.PBdr;
81import org.w3c.dom.Node;
82
83
84/**
85 * @author jharrop
86 *
87 */
88public class MainDocumentPart extends DocumentPart<org.docx4j.wml.Document> implements ContentAccessor  {
89       
90        private static Logger log = Logger.getLogger(MainDocumentPart.class);
91               
92       
93        public MainDocumentPart(PartName partName) throws InvalidFormatException {
94                super(partName);
95                init();
96        }
97        public MainDocumentPart() throws InvalidFormatException {
98                super(new PartName("/word/document.xml"));
99                init();
100        }
101               
102        public void init() {
103                // Used if this Part is added to [Content_Types].xml
104                setContentType(new  org.docx4j.openpackaging.contenttype.ContentType( 
105                                org.docx4j.openpackaging.contenttype.ContentTypes.WORDPROCESSINGML_DOCUMENT));
106
107                // Used when this Part is added to a rels
108                setRelationshipType(Namespaces.DOCUMENT);
109        }       
110
111    /**
112     * Convenience method to getJaxbElement().getBody().getContent()
113     * @since 2.7
114     */
115    public List<Object> getContent() {
116       
117        if (this.getJaxbElement()==null) {             
118                this.setJaxbElement( Context.getWmlObjectFactory().createDocument() );
119        }
120        if (this.getJaxbElement().getBody()==null) {
121                this.getJaxbElement().setBody(
122                                Context.getWmlObjectFactory().createBody() );
123        }
124       
125        return this.getJaxbElement().getContent();
126    }   
127       
128        private Binder<Node> binder;
129       
130       
131        /**
132         * Enables synchronization between XML infoset nodes and JAXB objects
133         * representing same XML document.
134         *
135         * An instance of this class maintains the association between XML nodes
136         * of an infoset preserving view and a JAXB representation of an XML document.
137         * Navigation between the two views is provided by the methods
138         * getXMLNode(Object) and getJAXBNode(Object) .
139         *
140         * In theory, modifications can be made to either the infoset preserving view or
141         * the JAXB representation of the document while the other view remains
142         * unmodified. The binder ought to be able to synchronize the changes made in
143         * the modified view back into the other view using the appropriate
144         * Binder update methods, #updateXML(Object, Object) or #updateJAXB(Object).
145         *
146         * But JAXB doesn't currently work as advertised .. access to this
147         * object is offered for advanced users on an experimental basis only.
148         */
149        public Binder<Node> getBinder() {
150               
151//              if (binder ==null) {
152//                      binder = jc.createBinder();                     
153//              }               
154                return binder;
155        }
156       
157        /**
158         * Fetch JAXB Nodes matching an XPath (for example "//w:p").
159         *
160         * If you have modified your JAXB objects (eg added or changed a
161         * w:p paragraph), you need to update the association. The problem
162         * is that this can only be done ONCE, owing to a bug in JAXB:
163         * see https://jaxb.dev.java.net/issues/show_bug.cgi?id=459
164         *
165         * So this is left for you to choose to do via the refreshXmlFirst parameter.   
166         *
167         * @param xpathExpr
168         * @param refreshXmlFirst
169         * @return
170         * @throws JAXBException
171         */     
172        public List<Object> getJAXBNodesViaXPath(String xpathExpr, boolean refreshXmlFirst) 
173                        throws JAXBException {
174               
175                return XmlUtils.getJAXBNodesViaXPath(binder, jaxbElement, xpathExpr, refreshXmlFirst);
176        }       
177
178        /**
179         * Fetch JAXB Nodes matching an XPath (for example ".//w:p" - note the dot,
180         * which is necessary for this sort of relative path).
181         *
182         * If you have modified your JAXB objects (eg added or changed a
183         * w:p paragraph), you need to update the association. The problem
184         * is that this can only be done ONCE, owing to a bug in JAXB:
185         * see https://jaxb.dev.java.net/issues/show_bug.cgi?id=459
186         *
187         * So this is left for you to choose to do via the refreshXmlFirst parameter.   
188
189         * @param xpathExpr
190         * @param someJaxbElement
191         * @param refreshXmlFirst
192         * @return
193         * @throws JAXBException
194         */
195        public List<Object> getJAXBNodesViaXPath(String xpathExpr, Object someJaxbElement, boolean refreshXmlFirst) 
196                throws JAXBException {
197
198                return XmlUtils.getJAXBNodesViaXPath(binder, someJaxbElement, xpathExpr, refreshXmlFirst);
199        }       
200       
201    private PropertyResolver propertyResolver;
202        public PropertyResolver getPropertyResolver() {
203                if (propertyResolver==null) {
204                        try {
205                                propertyResolver = new PropertyResolver( (WordprocessingMLPackage)this.pack);
206                        } catch (Docx4JException e) {
207                                e.printStackTrace();
208                        }                       
209                }
210                return propertyResolver;
211        }
212       
213        private StyleTree styleTree;
214        public StyleTree getStyleTree() {
215                return getStyleTree(false); // preserve existing behaviour
216        }
217       
218        public StyleTree getStyleTree(boolean refresh) {
219                // refresh is post 2.7.1
220               
221                if (refresh || styleTree==null) {
222                       
223                        log.info("Preparing StyleTree");
224
225                        // Styles actually in use in the document
226                        List<String> stylesInUse = new ArrayList<String>();
227                        Iterator it = getStylesInUse().entrySet().iterator();
228                    while (it.hasNext()) {
229                        Map.Entry pairs = (Map.Entry)it.next();
230                        String styleId = (String)pairs.getKey();
231                        stylesInUse.add(styleId);
232                                //log.debug("style in use: " + styleId );
233                    }
234                                   
235                    try {
236                                getStyleDefinitionsPart().createVirtualStylesForDocDefaults();
237                        } catch (Docx4JException e) {
238                                // Shouldn't happen, so catch here
239                                log.error(e);
240                        }
241               
242                        // Get these first, so we can be sure they are defined...
243                        Style defaultParagraphStyle = getStyleDefinitionsPart().getDefaultParagraphStyle();
244                        Style defaultCharacterStyle = getStyleDefinitionsPart().getDefaultCharacterStyle();
245                       
246                        // Styles defined in StyleDefinitionsPart
247                        Map<String, Style> allStyles = new HashMap<String, Style>();
248                        Styles styles = getStyleDefinitionsPart().getJaxbElement();             
249                        for ( org.docx4j.wml.Style s : styles.getStyle() ) {                           
250                                allStyles.put(s.getStyleId(), s);       
251                                //log.debug("live style: " + s.getStyleId() );
252                        }
253                        styleTree = new StyleTree(stylesInUse, allStyles, 
254                                        defaultParagraphStyle.getStyleId(),
255                                        defaultCharacterStyle.getStyleId());
256                               
257                }
258                return styleTree;
259               
260        }
261       
262               
263    /**
264     * Unmarshal XML data from the specified InputStream and return the
265     * resulting content tree.  Validation event location information may
266     * be incomplete when using this form of the unmarshal API.
267     *
268     * <p>
269     * Implements <a href="#unmarshalGlobal">Unmarshal Global Root Element</a>.
270     *
271     * @param is the InputStream to unmarshal XML data from
272     * @return the newly created root object of the java content tree
273     *
274     * @throws JAXBException
275     *     If any unexpected errors occur while unmarshalling
276     */
277        @Override
278    public org.docx4j.wml.Document unmarshal( java.io.InputStream is ) throws JAXBException {
279       
280                try {
281                       
282                        log.info("For MDP, unmarshall via binder");
283                        // InputStream to Document
284                        javax.xml.parsers.DocumentBuilderFactory dbf
285                                = DocumentBuilderFactory.newInstance();
286                        dbf.setNamespaceAware(true);
287                        org.w3c.dom.Document doc = dbf.newDocumentBuilder().parse(is);
288
289                        //
290                        binder = jc.createBinder();
291                       
292                        JaxbValidationEventHandler eventHandler = new JaxbValidationEventHandler();
293                        eventHandler.setContinue(false);
294                        binder.setEventHandler(eventHandler);
295                       
296                        try {
297                                jaxbElement =  (org.docx4j.wml.Document) binder.unmarshal( doc );
298                        } catch (UnmarshalException ue) {
299                                log.info("encountered unexpected content; pre-processing");
300                                /* Always try our preprocessor, since if what is first encountered is
301                                 * eg:
302                                 *
303                                  <w14:glow w14:rad="101600"> ...
304                                 *
305                                 * the error would be:
306                                 * 
307                                 *    unexpected element (uri:"http://schemas.microsoft.com/office/word/2010/wordml", local:"glow")
308                                 *
309                                 * but there could well be mc:AlternateContent somewhere
310                                 * further down in the document.
311                                 */
312
313                                // mimic docx4j 2.7.0 and earlier behaviour; this will
314                                // drop w14:glow etc; the preprocessor doesn't need to
315                                // do that
316                                eventHandler.setContinue(true);
317                               
318                                // There is no JAXBResult(binder),
319                                // so use a
320                                DOMResult result = new DOMResult();
321                               
322                                Templates mcPreprocessorXslt = JaxbValidationEventHandler.getMcPreprocessor();
323                                XmlUtils.transform(doc, mcPreprocessorXslt, null, result);
324                               
325                                doc = (org.w3c.dom.Document)result.getNode();
326                               
327                                try {
328                                        jaxbElement =  (org.docx4j.wml.Document) binder.unmarshal( doc );
329                                } catch (ClassCastException cce) {
330 
331                                        log.warn("Binder not available for this docx");
332                                        Unmarshaller u = jc.createUnmarshaller();
333                                        jaxbElement = (org.docx4j.wml.Document) u.unmarshal( doc );                                     
334                                        /*
335                                         * Work around for issue with JAXB binder, in Java 1.6
336                                         * encountered with /src/test/resources/jaxb-binder-issue.docx
337                                         * See http://old.nabble.com/BinderImpl.associativeUnmarshal-ClassCastException-casting-to-JAXBElement-td32456585.html
338                                         * and  http://java.net/jira/browse/JAXB-874
339                                         *
340                                         * java.lang.ClassCastException: org.docx4j.wml.PPr cannot be cast to javax.xml.bind.JAXBElement
341                                                at com.sun.xml.internal.bind.v2.runtime.ElementBeanInfoImpl$IntercepterLoader.intercept(Unknown Source)
342                                                at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.endElement(Unknown Source)
343                                                at com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor.endElement(Unknown Source)
344                                                at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.endElement(Unknown Source)
345                                                at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
346                                                at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
347                                                at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
348                                                at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
349                                                at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
350                                                at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
351                                                at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
352                                                at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
353                                                at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
354                                                at com.sun.xml.internal.bind.unmarshaller.DOMScanner.scan(Unknown Source)
355                                                at com.sun.xml.internal.bind.unmarshaller.DOMScanner.scan(Unknown Source)
356                                                at com.sun.xml.internal.bind.unmarshaller.DOMScanner.scan(Unknown Source)
357                                                at com.sun.xml.internal.bind.v2.runtime.BinderImpl.associativeUnmarshal(Unknown Source)
358                                                at com.sun.xml.internal.bind.v2.runtime.BinderImpl.unmarshal(Unknown Source)
359                                                at org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart.unmarshal(MainDocumentPart.java:321)
360                                         */
361                                }
362                               
363                        }
364                       
365                        return jaxbElement;
366                       
367                } catch (Exception e ) {
368                        e.printStackTrace();
369                        return null;
370                }
371    }
372
373    public org.docx4j.wml.Document unmarshal(org.w3c.dom.Element el) throws JAXBException {
374
375                try {
376
377                        binder = jc.createBinder();
378                        JaxbValidationEventHandler eventHandler = new JaxbValidationEventHandler();
379                        eventHandler.setContinue(false);
380                        binder.setEventHandler(eventHandler);
381                       
382                        try {
383                                jaxbElement =  (org.docx4j.wml.Document) binder.unmarshal( el );
384                        } catch (UnmarshalException ue) {
385                                log.info("encountered unexpected content; pre-processing");
386                                try {
387                                        org.w3c.dom.Document doc;
388                                        if (el instanceof org.w3c.dom.Document) {
389                                                doc = (org.w3c.dom.Document) el;
390                                        } else {
391                                                // Hope for the best. Dodgy though; what if this is
392                                                // being used on something deep in the tree?
393                                                // TODO: revisit
394                                                doc = el.getOwnerDocument();
395                                        }
396                                        eventHandler.setContinue(true);
397                                        DOMResult result = new DOMResult();
398                                        Templates mcPreprocessorXslt = JaxbValidationEventHandler
399                                                        .getMcPreprocessor();
400                                        XmlUtils.transform(doc, mcPreprocessorXslt, null, result);
401                                        doc = (org.w3c.dom.Document) result.getNode();
402                                        jaxbElement = (org.docx4j.wml.Document) binder
403                                                        .unmarshal(doc);
404                                } catch (Exception e) {
405                                        throw new JAXBException("Preprocessing exception", e);
406                                }
407                        }
408                        return jaxbElement;
409                       
410                } catch (JAXBException e) {
411                        log.error(e);
412                        throw e;
413                }
414        }
415       
416   
417    /**
418     * Traverse the document, looking for fonts which have been applied, either
419     * directly, or via a style.
420     *
421     * @return
422     */
423    public Map fontsInUse() {
424       
425    // Setup
426       
427        Map<String, String> fontsDiscovered = new java.util.HashMap<String, String>();
428       
429        // Keep track of styles we encounter, so we can
430        // inspect these for fonts
431        Map<String, String> stylesInUse = new java.util.HashMap<String, String>();
432
433                org.docx4j.wml.Styles styles = null;
434                if (this.getStyleDefinitionsPart()!=null) {
435                        styles = (org.docx4j.wml.Styles)this.getStyleDefinitionsPart().getJaxbElement();                       
436                }
437                // It is convenient to have a HashMap of styles
438                Map<String, Style> stylesDefined = new java.util.HashMap<String, Style>();
439                if (styles!=null) {
440                     for (Iterator<Style> iter = styles.getStyle().iterator(); iter.hasNext();) {
441                            Style s = iter.next();
442                            stylesDefined.put(s.getStyleId(), s);
443                     }
444                }
445    // We need to know what fonts and styles are used in the document
446       
447                org.docx4j.wml.Document wmlDocumentEl = (org.docx4j.wml.Document)this.getJaxbElement();
448                Body body =  wmlDocumentEl.getBody();
449
450                List <Object> bodyChildren = body.getEGBlockLevelElts();
451               
452//              traverseMainDocumentRecursive(bodyChildren, fontsDiscovered, stylesInUse);
453                Finder finder = new Finder(fontsDiscovered, stylesInUse);
454                new TraversalUtil(bodyChildren, finder);
455
456        // Add default font
457                //String defaultFont = PropertyResolver.getDefaultFont(this.getStyleDefinitionsPart(), this.getThemePart());
458//              String defaultFont = getPropertyResolver().getDefaultFont();
459//              log.debug("fontsDiscovered.put:" + defaultFont);
460//              fontsDiscovered.put( defaultFont, defaultFont  );
461                fontsDiscovered.put( ((WordprocessingMLPackage)pack).getDefaultFont(), ((WordprocessingMLPackage)pack).getDefaultFont() );
462               
463        // Add fonts used in the styles we discovered
464                Iterator it = stylesInUse.entrySet().iterator();
465            while (it.hasNext()) {
466                Map.Entry pairs = (Map.Entry)it.next();
467                String styleName = (String)pairs.getKey();
468                log.debug("Inspecting style: " + styleName );
469            org.docx4j.wml.Style existingStyle = (org.docx4j.wml.Style)stylesDefined.get(styleName);
470            if (existingStyle!=null) {
471                String fontName = getPropertyResolver().getFontnameFromStyle(stylesDefined, this.getThemePart(), existingStyle);
472                if (fontName!=null) {
473                        log.debug(styleName + " uses font " + fontName);
474                        fontsDiscovered.put(fontName, fontName);
475                }
476            } else {
477                log.error("Couldn't find used style " + styleName + "in styles part!");
478            }
479            }
480           
481            // Fonts can also be used in the numbering part
482            // For now, treat any font mentioned in that part as in use.
483            // Ideally, we'd only register fonts used in numbering levels
484            // that were actually used in the document
485        if (getNumberingDefinitionsPart()!=null) {
486                Numbering numbering = getNumberingDefinitionsPart().getJaxbElement();
487            for (Numbering.AbstractNum abstractNumNode : numbering.getAbstractNum() ) {
488                for (Lvl lvl : abstractNumNode.getLvl() ) {
489                        if (lvl.getRPr()!=null
490                                        && lvl.getRPr().getRFonts()!=null ) {
491                                String fontName = lvl.getRPr().getRFonts().getAscii();                                 
492                                if (fontName!=null) {
493                                        fontsDiscovered.put(fontName, fontName);       
494                                        log.debug("Registered " + fontName + " for abstract list " + abstractNumNode.getAbstractNumId() + " lvl " + lvl.getIlvl() );
495                                }
496                        }
497                }
498            }                   
499        }           
500                       
501                return fontsDiscovered;
502    }
503   
504
505        /**
506         * Traverse the document, and return a map of all styles which are used
507         * directly in the document.  (IE this does not include styles on which
508         * others are just BasedOn).
509         * @return
510         */
511        public Map<String, String> getStylesInUse(){
512
513               
514                org.docx4j.wml.Document wmlDocumentEl = (org.docx4j.wml.Document)this.getJaxbElement();
515                Body body =  wmlDocumentEl.getBody();
516
517                List <Object> bodyChildren = body.getEGBlockLevelElts();
518               
519                Map<String, String> stylesInUse = new HashMap<String, String>();
520                Finder finder = new Finder(null, stylesInUse);
521                new TraversalUtil(bodyChildren, finder);
522
523                // Styles in headers, footers?
524                RelationshipsPart rp = this.getRelationshipsPart();
525                if (rp!=null) {
526                        for ( Relationship r : rp.getRelationships().getRelationship() ) {
527                                Part part = rp.getPart(r);
528                                if ( part instanceof FooterPart ) {
529                                       
530                                        Ftr ftr = ((FooterPart)part).getJaxbElement();
531                                        finder.walkJAXBElements(ftr);
532                                       
533                                } else if (part instanceof HeaderPart) {
534                                       
535                                        Hdr hdr = ((HeaderPart)part).getJaxbElement();
536                                        finder.walkJAXBElements(hdr);
537                                }
538                        }
539                }
540               
541                // Styles in endnotes, footnotes?
542                if (this.getEndNotesPart()!=null) {
543                        log.debug("Looking at endnotes");
544                        CTEndnotes endnotes= this.getEndNotesPart().getJaxbElement();
545                        finder.walkJAXBElements(endnotes);
546                }
547                if (this.getFootnotesPart()!=null) {
548                        log.debug("Looking at footnotes");
549                        CTFootnotes footnotes= this.getFootnotesPart().getJaxbElement();
550                        finder.walkJAXBElements(footnotes);
551                }
552               
553                // Comments
554                if (this.getCommentsPart()!=null) {
555                        log.debug("Looking at comments");                       
556                        Comments comments = this.getCommentsPart().getJaxbElement();
557                        finder.walkJAXBElements(comments);
558                }
559               
560                return stylesInUse;
561        }
562   
563       
564    /**
565     * Traverse looking for fonts and/or styles
566     *
567     */
568    static class Finder extends CallbackImpl {
569               
570        Map fontsDiscovered;
571        Map<String, String> stylesInUse;
572       
573        Finder(Map fontsDiscovered, Map<String, String> stylesInUse) {
574                this.fontsDiscovered = fontsDiscovered;
575                this.stylesInUse = stylesInUse;
576        }
577       
578        @Override
579                public List<Object> apply(Object o) {
580                       
581                        if (o instanceof org.docx4j.wml.P
582                                        && ((org.docx4j.wml.P)o).getPPr()!=null) {                                     
583                               
584                        org.docx4j.wml.PPr pPr =  ((org.docx4j.wml.P)o).getPPr();
585                                if (stylesInUse !=null && pPr.getPStyle() != null) {
586                                        // Note this paragraph style
587                                        log.debug("put style "
588                                                        + pPr.getPStyle().getVal());
589                                        stylesInUse.put(pPr.getPStyle().getVal(), 
590                                                        pPr.getPStyle().getVal());
591                                }
592                               
593                                if (pPr.getRPr()!=null) {
594                                       
595                                org.docx4j.wml.ParaRPr rPr =  pPr.getRPr();
596                               
597                                if (fontsDiscovered!=null && rPr.getRFonts()!=null) {
598                                        //      Note the font - just Ascii for now
599                                        //log.debug("put font " + rPr.getRFonts().getAscii());
600                                        fontsDiscovered.put(rPr.getRFonts().getAscii(), rPr.getRFonts().getAscii());
601                                }
602                                if (stylesInUse !=null && rPr.getRStyle()!=null) {
603                                        //      Note this run style
604                                        //log.debug("put style " + rPr.getRStyle().getVal() );
605                                        stylesInUse.put(rPr.getRStyle().getVal(), rPr.getRStyle().getVal());
606                                }
607                                }
608               
609                        } else if ( o instanceof org.docx4j.wml.R
610                                        && ((org.docx4j.wml.R)o).getRPr()!=null) {
611
612                        org.docx4j.wml.RPr rPr =  ((org.docx4j.wml.R)o).getRPr();
613                        if (fontsDiscovered!=null && rPr.getRFonts()!=null) {
614                                //      Note the font - just Ascii for now
615                                //log.debug("put font " + rPr.getRFonts().getAscii());
616                                fontsDiscovered.put(rPr.getRFonts().getAscii(), rPr.getRFonts().getAscii());
617                        }
618                        if (stylesInUse !=null && rPr.getRStyle()!=null) {
619                                //      Note this run style
620                                //log.debug("put style " + rPr.getRStyle().getVal() );
621                                stylesInUse.put(rPr.getRStyle().getVal(), rPr.getRStyle().getVal());
622                        }
623                               
624                        } else if (o instanceof org.docx4j.wml.R.Sym ) { 
625                               
626                                if ( fontsDiscovered !=null ) {
627                                        org.docx4j.wml.R.Sym sym = (org.docx4j.wml.R.Sym)o;
628                                        fontsDiscovered.put(sym.getFont(), sym.getFont());
629                                }
630                               
631                        } else if (o instanceof org.docx4j.wml.Tbl ) {
632                                // The table could have a table style;
633                                // Tables created in Word 2007 default to table style "TableGrid",
634                                // which is based on "TableNormal".
635                                org.docx4j.wml.Tbl tbl = (org.docx4j.wml.Tbl)o;
636                                if (stylesInUse !=null && tbl.getTblPr()!=null 
637                                                && tbl.getTblPr().getTblStyle()!=null) {
638                                        log.debug("Adding table style: " + tbl.getTblPr().getTblStyle().getVal() );
639                                        stylesInUse.put(tbl.getTblPr().getTblStyle().getVal(),
640                                                                        tbl.getTblPr().getTblStyle().getVal() );
641                                }
642                                // There is no such thing as a tr or a tc style,
643                                // so we don't need to look for them,
644                                // but since a tc can contain w:p or nested table,
645                                // we still need to recurse
646                               
647                        }
648                        return null;
649                }
650       
651        @Override
652                public boolean shouldTraverse(Object o) {
653               
654                if (o instanceof org.docx4j.wml.Br
655                                || o instanceof org.docx4j.wml.R.Tab
656                                || o instanceof org.docx4j.wml.R.LastRenderedPageBreak) {
657                        return false;
658                }
659                        return true;
660                }
661       
662        }       
663       
664       
665
666        /**
667         * Create a paragraph containing the string simpleText, styled
668         * using the specified style
669         * (up to user to ensure it is a paragraph style)
670         * and add it to the document.
671         *
672         * @param styleId
673         * @param text
674         * @return
675         */
676        public org.docx4j.wml.P addStyledParagraphOfText(String styleId, String text) {
677               
678                org.docx4j.wml.P p = createStyledParagraphOfText(styleId, text);
679                addObject(p);
680               
681                return p;
682
683        }
684
685        /**
686         * Create a paragraph containing the string simpleText, styled
687         * using the specified style (up to user to ensure it is a paragraph style)
688         * without adding it to the document.
689         *
690         * @param styleId
691         * @param text
692         * @return
693         */
694        public org.docx4j.wml.P createStyledParagraphOfText(String styleId, String text) {
695               
696                org.docx4j.wml.P p = createParagraphOfText(text);
697                                               
698                StyleDefinitionsPart styleDefinitionsPart
699                        = this.getStyleDefinitionsPart();
700
701                if (getPropertyResolver().activateStyle(styleId)) {
702                        // Style is available
703                        org.docx4j.wml.ObjectFactory factory = new org.docx4j.wml.ObjectFactory();                     
704                        org.docx4j.wml.PPr  pPr = factory.createPPr();
705                        p.setPPr(pPr);
706                        org.docx4j.wml.PPrBase.PStyle pStyle = factory.createPPrBasePStyle();
707                        pPr.setPStyle(pStyle);
708                        pStyle.setVal(styleId);
709                }               
710               
711                return p;
712
713        }
714       
715       
716        /**
717         * Create a paragraph containing the string simpleText,
718         * and add it to the document.  If passed null, the result
719         * is an empty P.
720         *
721         * @param simpleText
722         * @return
723         */
724        public org.docx4j.wml.P addParagraphOfText(String simpleText) {
725               
726                org.docx4j.wml.P  para = createParagraphOfText(simpleText);
727                addObject(para);
728               
729                return para;
730               
731        }
732
733        /**
734         * Create a paragraph containing the string simpleText,
735         * without adding it to the document.  If passed null, the result
736         * is an empty P.
737         *
738         * @param simpleText
739         * @return
740         */
741        public org.docx4j.wml.P createParagraphOfText(String simpleText) {
742               
743                // Create content
744
745                org.docx4j.wml.ObjectFactory factory = new org.docx4j.wml.ObjectFactory();
746                org.docx4j.wml.P  para = factory.createP();
747
748                if (simpleText!=null) {
749                        org.docx4j.wml.Text  t = factory.createText();
750                        t.setValue(simpleText);
751       
752                        org.docx4j.wml.R  run = factory.createR();
753                        run.getRunContent().add(t);             
754                       
755                        para.getParagraphContent().add(run);
756                }
757               
758                return para;
759               
760               
761        }
762       
763       
764        /**
765         * Add the object o to the document. 
766         *
767         * @param o
768         */
769        public void addObject(Object o) {
770               
771                this.getContent().add( o );
772               
773                // If this object contains paragraphs, make sure any style used
774                // is activated
775        Map stylesInUse = new java.util.HashMap();
776                Map fontsDiscovered = new java.util.HashMap(); // method requires this
777                List list = new java.util.ArrayList<Object>();
778                list.add(o);
779               
780//              traverseMainDocumentRecursive( list, fontsDiscovered, stylesInUse);
781                Finder finder = new Finder(fontsDiscovered, stylesInUse);
782                finder.walkJAXBElements(list);
783               
784                Iterator it = stylesInUse.entrySet().iterator();
785            while (it.hasNext()) {
786                Map.Entry pairs = (Map.Entry)it.next();
787                String styleName = (String)pairs.getKey();
788                log.debug("Inspecting style: " + styleName );
789               
790                if (styleDefinitionsPart==null) {
791                       
792                        log.warn("Style definitions part was null!");
793                       
794                } else if (getPropertyResolver().activateStyle(styleName)) {
795                        // Cool
796                } else {
797                        log.warn(styleName + " couldn't be activated!");
798                }
799               
800            }
801        }
802       
803        /**
804         * Create a paragraph from the xml string <w:p>...</w:p> ,
805         * and add it to the document.  You'll need to ensure the
806         * string contains namespace declarations (including for w:)
807         *
808         * @param simpleText
809         * @return
810         */
811        public org.docx4j.wml.P addParagraph(String pXml) throws JAXBException {
812               
813                org.docx4j.wml.P  para = (org.docx4j.wml.P)org.docx4j.XmlUtils.unmarshalString(pXml);
814                this.getContent().add( para );
815                return para;
816        }
817       
818        /**
819         * Create a Hyperlink object, which is suitable for adding to a w:p
820         * @param bookmarkName
821         * @param linkText
822         * @return
823         */
824        public static Hyperlink hyperlinkToBookmark(String bookmarkName, String linkText) {
825               
826                try {
827
828                       
829                        String hpl = "<w:hyperlink w:anchor=\"" + bookmarkName + "\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" " +
830            "w:history=\"1\" >" +
831            "<w:r>" +
832            "<w:rPr>" +
833            "<w:rStyle w:val=\"Hyperlink\" />" +  // TODO: enable this style in the document!
834            "</w:rPr>" +
835            "<w:t>" + linkText + "</w:t>" +
836            "</w:r>" +
837            "</w:hyperlink>";
838
839                        return (Hyperlink)XmlUtils.unmarshalString(hpl);
840                       
841                } catch (Exception e) {
842                        // Shouldn't happen
843                        e.printStackTrace();
844                        return null;
845                }
846                               
847        }
848       
849}
850
851       
Note: See TracBrowser for help on using the repository browser.