source: trunk/docx4j/src/main/java/org/docx4j/model/PropertyResolver.java @ 1793

Revision 1793, 46.9 KB checked in by jharrop, 5 weeks ago (diff)

Where numId is implicit, fill it in from basedOn style.

Line 
1package org.docx4j.model;
2
3
4import java.math.BigInteger;
5import java.util.HashMap;
6import java.util.Iterator;
7import java.util.List;
8import java.util.Map;
9import java.util.Stack;
10
11import javax.xml.bind.JAXBException;
12
13import org.apache.log4j.Logger;
14import org.docx4j.XmlUtils;
15import org.docx4j.jaxb.Context;
16import org.docx4j.model.properties.Property;
17import org.docx4j.model.properties.PropertyFactory;
18import org.docx4j.model.properties.paragraph.AbstractParagraphProperty;
19import org.docx4j.model.properties.paragraph.Indent;
20import org.docx4j.model.properties.run.AbstractRunProperty;
21import org.docx4j.openpackaging.exceptions.Docx4JException;
22import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
23import org.docx4j.openpackaging.parts.ThemePart;
24import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
25import org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart;
26import org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart;
27import org.docx4j.wml.BooleanDefaultTrue;
28import org.docx4j.wml.CTTblCellMar;
29import org.docx4j.wml.CTTblPrBase;
30import org.docx4j.wml.CTTblStylePr;
31import org.docx4j.wml.DocDefaults;
32import org.docx4j.wml.PPr;
33import org.docx4j.wml.PPrBase.NumPr.NumId;
34import org.docx4j.wml.ParaRPr;
35import org.docx4j.wml.RPr;
36import org.docx4j.wml.Style;
37import org.docx4j.wml.TblBorders;
38import org.docx4j.wml.TblPr;
39import org.docx4j.wml.TcPr;
40import org.docx4j.wml.TrPr;
41
42/**
43 * This class works out the actual set of properties (paragraph or run)
44 * which apply, following the order specified in ECMA-376.
45 *
46 * From ECMA-376 > Part3 > 2 Introduction to WordprocessingML > 2.8 Styles > 2.8.10 Style Application
47 * at http://www.documentinteropinitiative.org/implnotes/ecma-376/P3-2.8.10.aspx
48 *
49 * (See also Part 4, 2.7.2 which is the normative bit...)
50
51        With the various flavors of styles available, multiple style types can be
52        applied to the same content within a file, which means that properties must
53        be applied in a specific deterministic order. As with inheritance, the
54        resulting formatting properties set by one type can be unchanged, removed,
55        or altered by following types.
56
57        The following table illustrates the order of application of these defaults,
58        and which properties are impacted by each:
59
60        This process can be described as follows:
61       
62        First, the document defaults are applied to all runs and paragraphs in
63        the document.
64       
65        Next, the table style properties are applied to each table in the document,
66        following the conditional formatting inclusions and exclusions specified
67        per table.
68       
69        Next, numbered item and paragraph properties are applied to each paragraph
70        formatted with a numbering style.
71       
72        Next, paragraph and run properties are
73        applied to each paragraph as defined by the paragraph style.
74       
75        Next, run properties are applied to each run with a specific character style
76        applied.
77       
78        Finally, we apply direct formatting (paragraph or run properties not from
79        styles).
80       
81        -----------
82       
83        Things which are unclear:
84       
85         - the role of w:link on a paragraph style (eg Heading1 links to "Heading1char"),
86           experimentation in Word 2007 suggests the w:link is not used at all
87           
88         - indeed, "Heading1char" is not used at all?
89         
90        -----------
91
92         docx4all does not use this; its org.docx4all.swing.text.StyleSheet
93         uses MutableAttributeSet's resolve function to climb the style hierarchy.
94           
95         This is most relevant to XSLFO, which unlike CSS, doesn't have a concept of
96         style. HTML NG2 uses CSS inheritance, and so doesn't need it.
97
98 * @author jharrop
99 *
100 */
101public class PropertyResolver {
102       
103        private static Logger log = Logger.getLogger(PropertyResolver.class);
104       
105        private DocDefaults docDefaults;       
106        private PPr documentDefaultPPr;
107        private RPr documentDefaultRPr;
108       
109        private StyleDefinitionsPart styleDefinitionsPart;
110       
111        private WordprocessingMLPackage wordMLPackage;
112       
113        /**
114         * All styles in the Style Definitions Part.
115         */
116        private org.docx4j.wml.Styles styles;
117
118        /**
119         * Map of all styles in the Style Definitions Part.
120         * Note, you need to manually keep this up to date
121         */
122        private java.util.Map<String, org.docx4j.wml.Style>  liveStyles = null;
123       
124       
125        private ThemePart themePart;
126        private NumberingDefinitionsPart numberingDefinitionsPart;
127
128
129        private java.util.Map<String, PPr>  resolvedStylePPrComponent = new HashMap<String, PPr>();
130
131        /**
132         * This map also contains the rPr component of a pPr
133         */
134        private java.util.Map<String, RPr>  resolvedStyleRPrComponent = new HashMap<String, RPr>();
135       
136        public PropertyResolver(WordprocessingMLPackage wordMLPackage) throws Docx4JException {
137               
138                this.wordMLPackage = wordMLPackage;
139               
140                MainDocumentPart mdp = wordMLPackage.getMainDocumentPart();
141               
142                styleDefinitionsPart = mdp.getStyleDefinitionsPart();
143                themePart = mdp.getThemePart();
144                numberingDefinitionsPart = mdp.getNumberingDefinitionsPart();
145                init();
146        }
147       
148//      public PropertyResolver(StyleDefinitionsPart styleDefinitionsPart,
149//                                                      ThemePart themePart,
150//                                                      NumberingDefinitionsPart numberingDefinitionsPart) throws Docx4JException {
151//             
152//              this.styleDefinitionsPart= styleDefinitionsPart;
153//              this.themePart = themePart;
154//              this.numberingDefinitionsPart = numberingDefinitionsPart;
155//              init();
156//      }
157       
158        String defaultParagraphStyleId;  // "Normal" in English, but ...
159        String defaultCharacterStyleId;
160       
161        private void init() throws Docx4JException {
162
163                // Make sure we have a styles definitions part
164                try {
165                        if (styleDefinitionsPart==null) {
166                                styleDefinitionsPart = new StyleDefinitionsPart();
167                                styleDefinitionsPart.unmarshalDefaultStyles();
168                               
169                                // For this case, should we provide a way
170                                // to add this new part to the package?
171                               
172                        }
173                } catch (Exception e) {
174                        throw new Docx4JException("Couldn't create default StyleDefinitionsPart", e);
175                }
176               
177                defaultParagraphStyleId = this.styleDefinitionsPart.getDefaultParagraphStyle().getStyleId();
178                defaultCharacterStyleId = this.styleDefinitionsPart.getDefaultCharacterStyle().getStyleId();
179
180                // Initialise styles
181                styles = (org.docx4j.wml.Styles)styleDefinitionsPart.getJaxbElement(); 
182                initialiseLiveStyles();         
183               
184                // Initialise docDefaults               
185                docDefaults = styles.getDocDefaults();
186
187                if (docDefaults == null) {
188                        // The only way this can happen is if the
189                        // styles definition part is missing the docDefaults element
190                        // (these are present in docs created from Word, and
191                        // in our default styles, so maybe the user created it using
192                        // some 3rd party program?)
193                        try {
194                                docDefaults = (DocDefaults) XmlUtils
195                                                .unmarshalString(StyleDefinitionsPart.docDefaultsString);
196                        } catch (JAXBException e) {
197                                throw new Docx4JException("Problem unmarshalling "
198                                                + StyleDefinitionsPart.docDefaultsString, e);
199                        }
200                }
201
202                // Setup documentDefaultPPr
203                if (docDefaults.getPPrDefault() == null) {
204                        try {
205                                documentDefaultPPr = (PPr) XmlUtils
206                                                .unmarshalString(StyleDefinitionsPart.pPrDefaultsString);
207                        } catch (JAXBException e) {
208                                throw new Docx4JException("Problem unmarshalling "
209                                                + StyleDefinitionsPart.pPrDefaultsString, e);
210                        }
211                } else {
212                        documentDefaultPPr = docDefaults.getPPrDefault().getPPr();
213                }
214
215                // Setup documentDefaultRPr
216                if (docDefaults.getRPrDefault() == null) {
217                        try {
218                                documentDefaultRPr = (RPr) XmlUtils
219                                                .unmarshalString(StyleDefinitionsPart.rPrDefaultsString);
220                        } catch (JAXBException e) {
221                                throw new Docx4JException("Problem unmarshalling "
222                                                + StyleDefinitionsPart.rPrDefaultsString, e);
223                        }
224                } else {
225                        documentDefaultRPr = docDefaults.getRPrDefault().getRPr();
226                }
227
228                addNormalToResolvedStylePPrComponent();
229                addDefaultParagraphFontToResolvedStyleRPrComponent();
230        }
231
232
233        private void addNormalToResolvedStylePPrComponent() {
234               
235                Stack<PPr> pPrStack = new Stack<PPr>();
236//              String styleId = "Normal";
237                String styleId = defaultParagraphStyleId;
238               
239                fillPPrStack(styleId, pPrStack);
240                pPrStack.push(documentDefaultPPr);
241                       
242                PPr effectivePPr = factory.createPPr();                 
243                // Now, apply the properties starting at the top of the stack
244                while (!pPrStack.empty() ) {
245                        PPr pPr = pPrStack.pop();
246                        applyPPr(pPr, effectivePPr);
247                }
248                resolvedStylePPrComponent.put(styleId, effectivePPr);           
249        }
250
251        private void addDefaultParagraphFontToResolvedStyleRPrComponent() {
252        Stack<RPr> rPrStack = new Stack<RPr>();
253
254                fillRPrStack(defaultParagraphStyleId, rPrStack);
255                        // Since default font size might be in there.
256               
257                fillRPrStack(defaultCharacterStyleId, rPrStack);
258                rPrStack.push(documentDefaultRPr);
259                       
260                RPr effectiveRPr = factory.createRPr();
261               
262                // Now, apply the properties starting at the top of the stack
263                while (!rPrStack.empty() ) {
264                        RPr rPr = rPrStack.pop();
265                        applyRPr(rPr, effectiveRPr);
266                }
267                resolvedStyleRPrComponent.put(defaultCharacterStyleId, effectiveRPr);           
268        }
269       
270        public Style getEffectiveTableStyle(TblPr tblPr) {
271                // OK to pass this a null tblPr.
272               
273                Stack<Style> tableStyleStack = new Stack<Style>();
274               
275                if (tblPr !=null && tblPr.getTblStyle()!=null) {
276                        String styleId = tblPr.getTblStyle().getVal();
277                        log.debug("Table style: " + styleId);
278                        fillTableStyleStack(styleId, tableStyleStack);
279                } else {
280                        log.debug("No table style specified");
281                }
282               
283                Style result;
284                if (tableStyleStack.size()>0 ) {
285                        result = XmlUtils.deepCopy(tableStyleStack.pop());
286                } else {
287                        result = Context.getWmlObjectFactory().createStyle();
288                        if (tblPr==null) {
289                                // Return empty style object
290                                return result;
291                        }                       
292                }
293                while (!tableStyleStack.empty() ) {
294                        Style thisLevel = tableStyleStack.pop();
295                        applyTableStyle(thisLevel, result);
296                }
297               
298                // Finally apply the tblPr we were passed
299                if (result.getTblPr()==null) {
300                        result.setTblPr(
301                                        Context.getWmlObjectFactory().createCTTblPrBase() );
302                }
303                applyTablePr(tblPr, result.getTblPr());
304               
305                return result;
306        }
307       
308        private void applyTableStyle(Style thisLevel, Style result) { 
309               
310                // TblPr
311                if (thisLevel.getTblPr()!=null) {
312                        log.debug("Applying tblPr..");
313                        if (result.getTblPr()==null) {
314                                result.setTblPr(
315                                                XmlUtils.deepCopy( thisLevel.getTblPr() ) );
316                        } else {
317                                applyTablePr(thisLevel.getTblPr(), result.getTblPr() );
318                        }
319                }
320               
321                // TblStylePr - STTblStyleOverrideType stuff
322                if (thisLevel.getTblStylePr()!=null) {
323                        log.debug("Applying tblStylePr.. TODO!");
324                        // Its a list, created automatically
325                        applyTableStylePr(thisLevel.getTblStylePr(), result.getTblStylePr() );
326                }
327               
328               
329                // TrPr - eg jc, trHeight, wAfter, tblCellSpacing
330                if (thisLevel.getTrPr()!=null) {
331                        log.debug("Applying trPr.. TODO!");
332                        if (result.getTrPr()==null) {
333                                result.setTrPr(
334                                                XmlUtils.deepCopy( thisLevel.getTrPr() ));
335                        } else {
336                                applyTrPr(thisLevel.getTrPr(), result.getTrPr() );
337                        }
338                }
339               
340                // TcPr - includes includes TcPrInner.TcBorders, CTShd, TcMar, CTVerticalJc
341                if (thisLevel.getTcPr()!=null) {
342                        log.debug("Applying tcPr.. TODO!");
343                        if (result.getTcPr()==null) {
344                                result.setTcPr(
345                                                XmlUtils.deepCopy( thisLevel.getTcPr() ));
346                        } else {
347                                applyTcPr(thisLevel.getTcPr(), result.getTcPr() );
348                        }
349                }
350               
351                // pPr
352                if (thisLevel.getPPr()!=null) {
353                        log.debug("Applying pPr..");
354                        if (result.getPPr()==null) {
355                                result.setPPr(
356                                                XmlUtils.deepCopy( thisLevel.getPPr() ));
357                        } else {
358                                applyPPr(thisLevel.getPPr(), result.getPPr() );
359                        }
360                }
361               
362                // rPr
363                if (thisLevel.getRPr()!=null) {
364                        log.debug("Applying rPr..");
365                        if (result.getRPr()==null) {
366                                result.setRPr(
367                                                XmlUtils.deepCopy( thisLevel.getRPr() ));
368                        } else {
369                                applyRPr(thisLevel.getRPr(), result.getRPr() );
370                        }
371                }
372        }
373
374        private void applyTablePr(CTTblPrBase thisLevel, CTTblPrBase result) {
375                /*
376                 * eg
377               <w:tblInd w:w="0" w:type="dxa"/>
378                <w:tblBorders>
379                    <w:top w:val="single" w:sz="4" w:space="0" w:color="000000" w:themeColor="text1"/>
380                    <w:left w:val="single" w:sz="4" w:space="0" w:color="000000" w:themeColor="text1"/>
381                    <w:bottom w:val="single" w:sz="4" w:space="0" w:color="000000" w:themeColor="text1"/>
382                    <w:right w:val="single" w:sz="4" w:space="0" w:color="000000" w:themeColor="text1"/>
383                    <w:insideH w:val="single" w:sz="4" w:space="0" w:color="000000" w:themeColor="text1"/>
384                    <w:insideV w:val="single" w:sz="4" w:space="0" w:color="000000" w:themeColor="text1"/>
385                </w:tblBorders>
386                <w:tblCellMar>
387                    <w:top w:w="0" w:type="dxa"/>
388                    <w:left w:w="108" w:type="dxa"/>
389                    <w:bottom w:w="0" w:type="dxa"/>
390                    <w:right w:w="108" w:type="dxa"/>
391                </w:tblCellMar>
392               
393                PLUS OTHERS, TODO
394                 */
395
396                // w:tblInd
397                if (thisLevel.getTblInd()!=null ) {
398                        if (result.getTblInd()==null) {
399                                result.setTblInd(
400                                        XmlUtils.deepCopy(thisLevel.getTblInd()) );
401                        } else {
402                                result.getTblInd().setW( 
403                                        // clone
404                                        BigInteger.valueOf(thisLevel.getTblInd().getW().intValue())
405                                );
406                                result.getTblInd().setType(
407                                        thisLevel.getTblInd().getType()
408                                );                             
409                        }                       
410                }
411
412                // w:tblBorders
413                if (thisLevel.getTblBorders()!=null) {
414                        if (result.getTblBorders()==null) {
415                                result.setTblBorders(
416                                                XmlUtils.deepCopy(thisLevel.getTblBorders() ));
417                        } else {
418                                TblBorders thisLevelBorders = thisLevel.getTblBorders();
419                                TblBorders resultBorders = result.getTblBorders();
420                               
421                                // child-by-child, copy if this level has a setting
422                               
423                                //top
424                                if (thisLevelBorders.getTop()!=null) {
425                                        resultBorders.setTop( XmlUtils.deepCopy(thisLevelBorders.getTop() ));
426                                }
427                                //bottom
428                                if (thisLevelBorders.getBottom()!=null) {
429                                        resultBorders.setBottom( XmlUtils.deepCopy(thisLevelBorders.getBottom() ));
430                                }
431                                //left
432                                if (thisLevelBorders.getLeft()!=null) {
433                                        resultBorders.setLeft( XmlUtils.deepCopy(thisLevelBorders.getLeft() ));
434                                }
435                                //right
436                                if (thisLevelBorders.getRight()!=null) {
437                                        resultBorders.setRight( XmlUtils.deepCopy(thisLevelBorders.getRight() ));
438                                }
439                                //insideH
440                                if (thisLevelBorders.getInsideH()!=null) {
441                                        resultBorders.setInsideH( XmlUtils.deepCopy(thisLevelBorders.getInsideH() ));
442                                }
443                                //insideV
444                                if (thisLevelBorders.getInsideV()!=null) {
445                                        resultBorders.setInsideV( XmlUtils.deepCopy(thisLevelBorders.getInsideV() ));
446                                }
447                        }
448                }
449               
450               
451                // w:tblCellMar
452                if (thisLevel.getTblCellMar()!=null) {
453                        if (result.getTblCellMar()==null) {
454                                result.setTblCellMar(
455                                                XmlUtils.deepCopy(thisLevel.getTblCellMar() ));
456                        } else {
457                                CTTblCellMar thisLevelCellMar = thisLevel.getTblCellMar();
458                                CTTblCellMar resultCellMar = result.getTblCellMar();
459                               
460                                // child-by-child, copy if this level has a setting
461                               
462                                //top
463                                if (thisLevelCellMar.getTop()!=null) {
464                                        resultCellMar.setTop( XmlUtils.deepCopy(thisLevelCellMar.getTop() ));
465                                }
466                                //bottom
467                                if (thisLevelCellMar.getBottom()!=null) {
468                                        resultCellMar.setBottom( XmlUtils.deepCopy(thisLevelCellMar.getBottom() ));
469                                }
470                                //left
471                                if (thisLevelCellMar.getLeft()!=null) {
472                                        resultCellMar.setLeft( XmlUtils.deepCopy(thisLevelCellMar.getLeft() ));
473                                }
474                                //right
475                                if (thisLevelCellMar.getRight()!=null) {
476                                        resultCellMar.setRight( XmlUtils.deepCopy(thisLevelCellMar.getRight() ));
477                                }
478                        }
479                }
480               
481                if (thisLevel.getTblCellSpacing()!=null ) {
482                        result.setTblCellSpacing(
483                                XmlUtils.deepCopy(thisLevel.getTblCellSpacing()) );
484                }
485        }
486       
487       
488        private void applyTableStylePr(List<CTTblStylePr> thisLevel, List<CTTblStylePr> result) {
489                // STTblStyleOverrideType
490               
491                // TODO
492        }
493        private void applyTrPr(TrPr thisLevel, TrPr result) {           
494               
495        }
496        private void applyTcPr(TcPr thisLevel, TcPr result) { 
497            // includes TcPrInner.TcBorders, CTShd, TcMar, CTVerticalJc
498                // TODO         
499        }
500       
501        /**
502         * Ascend the style hierarchy, capturing the table styles
503         * 
504         * @param stylename
505         * @param effectivePPr
506         */
507        private void fillTableStyleStack(String styleId, Stack<Style> tableStyleStack) {
508                // get the style
509                Style style = liveStyles.get(styleId);
510               
511                // add it to the stack
512                if (style==null) {
513                        // No such style!
514                        // For now, just log it..
515                        log.error("Style definition not found: " + styleId);
516                        return;
517                }
518               
519                tableStyleStack.push(style);
520                log.debug("Added " + styleId + " to table style stack");
521               
522                // if it is based on, recurse
523        if (style.getBasedOn()==null) {
524                        log.debug("Style " + styleId + " is a root style.");
525        } else if (style.getBasedOn().getVal()!=null) {
526                String basedOnStyleName = style.getBasedOn().getVal();                 
527                fillTableStyleStack( basedOnStyleName, tableStyleStack);
528        } else {
529                log.debug("No basedOn set for: " + style.getStyleId() );
530        }
531               
532        }
533       
534       
535        /**
536         * Follow the resolution rules to return the
537         * paragraph properties which actually apply,
538         * given this pPr element (on a w:p).
539         *
540         * Note 1:  the properties are not the definition
541         * of any style name returned.
542         * Note 2:  run properties are not resolved
543         * or returned by this method.
544         *
545         * What is returned is a live object.  If you
546         * want to change it, you should clone it first!
547         * 
548         * @param expressPPr
549         * @return
550         */
551        public PPr getEffectivePPr(PPr expressPPr) {
552               
553                PPr effectivePPr = null;
554                //      First, the document defaults are applied
555               
556                        // Done elsewhere
557                        // PPr effectivePPr = (PPr)XmlUtils.deepCopy(documentDefaultPPr);
558               
559                //      Next, the table style properties are applied to each table in the document,
560                //      following the conditional formatting inclusions and exclusions specified
561                //      per table.
562               
563                        // TODO - if the paragraph is in a table?
564                               
565                //      Next, numbered item and paragraph properties are applied to each paragraph
566                //      formatted with a *numbering *style**.
567               
568                        // TODO - who uses numbering styles (as opposed to numbering
569                        // via a paragraph style or direct formatting)?
570               
571                //  Next, paragraph and run properties are
572                //      applied to each paragraph as defined by the paragraph style.
573                PPr resolvedPPr = null;
574                String styleId;
575                if (expressPPr == null || expressPPr.getPStyle() == null ) {
576//                      styleId = "Normal";
577                        styleId = defaultParagraphStyleId;
578                       
579                } else {
580                        styleId = expressPPr.getPStyle().getVal();
581                }
582                resolvedPPr = getEffectivePPr(styleId);
583               
584                               
585                //      Next, run properties are applied to each run with a specific character style
586                //      applied.
587               
588                        // Not for pPr
589                               
590                //      Finally, we apply direct formatting (paragraph or run properties not from
591                //      styles).               
592                if (hasDirectPPrFormatting(expressPPr) ) {
593                        if (resolvedPPr==null) {
594                                log.warn("resolvedPPr was null. Look into this?");
595                                effectivePPr = Context.getWmlObjectFactory().createPPr();
596                        } else {
597                                effectivePPr = (PPr)XmlUtils.deepCopy(resolvedPPr);
598                        }
599                        applyPPr(expressPPr, effectivePPr);
600                        return effectivePPr;
601                } else {
602                        return resolvedPPr;
603                }
604               
605        }
606
607        /**
608         * Follow the resolution rules to return the
609         * paragraph properties which actually apply,
610         * given this paragraph style
611         *
612         * Note 1:  the properties are not the definition
613         * of any style name returned.
614         * Note 2:  run properties are not resolved
615         * or returned by this method.
616         *
617         * What is returned is a live object.  If you
618         * want to change it, you should clone it first!
619         * 
620         * @param expressPPr
621         * @return
622         */
623        public PPr getEffectivePPr(String styleId) {
624
625                PPr resolvedPPr = resolvedStylePPrComponent.get(styleId);
626               
627                if (resolvedPPr!=null) {
628                        return resolvedPPr;
629                }
630               
631                // Hmm, have to do the work
632                Style s = liveStyles.get(styleId);
633               
634                if (s==null) {
635                        log.error("Couldn't find style: " + styleId);
636                        return null;
637                }
638               
639                PPr expressPPr = s.getPPr();
640                if (expressPPr==null) {
641                        log.error("style: " + styleId + " has no PPr");
642                        String normalId = this.styleDefinitionsPart.getDefaultParagraphStyle().getStyleId();                   
643                        resolvedPPr = resolvedStylePPrComponent.get(normalId);
644                        return resolvedPPr;
645                }
646               
647                //  Next, paragraph and run properties are
648                //      applied to each paragraph as defined by the paragraph style.
649                Stack<PPr> pPrStack = new Stack<PPr>();
650                // Haven't done this one yet                           
651                fillPPrStack(styleId, pPrStack);
652                // Finally, on top
653                pPrStack.push(documentDefaultPPr);
654                                               
655                resolvedPPr = factory.createPPr();                     
656                // Now, apply the properties starting at the top of the stack
657                while (!pPrStack.empty() ) {
658                        PPr pPr = pPrStack.pop();
659                        applyPPr(pPr, resolvedPPr);
660                }
661                resolvedStylePPrComponent.put(styleId, resolvedPPr);
662                return resolvedPPr;
663        }
664       
665        /**
666         * @param expressRPr
667         * @param pPr -
668         * @return
669         */
670        public RPr getEffectiveRPr(RPr expressRPr, PPr pPr) {
671               
672                // NB Currently used in PDF viaXSLFO only
673               
674                log.debug("in getEffectiveRPr");
675               
676//              Idea is that you pass pPr if you are using this for XSL FO,
677//              since we need to take account of rPr in paragraph styles
678//              (but not the rPr in a pPr direct formatting, since
679//       that only applies to the paragraph mark).
680//               * For HTML/CSS, this would be null (since the pPr level rPr
681//               * is made into a separate style applied via a second value in
682//               * the class attribute).  But, in the CSS case, this
683                // function is not used - since the rPr is made into a style as well.
684               
685               
686                //      First, the document defaults are applied
687               
688                        RPr effectiveRPr = (RPr)XmlUtils.deepCopy(documentDefaultRPr);
689                       
690                        // Apply DefaultParagraphFont.  We only do it explicitly
691                        // here as per conditions, because if there is a run style,
692                        // walking the hierarchy will include this if it is needed
693                        if (expressRPr == null || expressRPr.getRStyle() == null ) {
694                                applyRPr(resolvedStyleRPrComponent.get(defaultCharacterStyleId), effectiveRPr);                                                         
695                        }
696               
697                //      Next, the table style properties are applied to each table in the document,
698                //      following the conditional formatting inclusions and exclusions specified
699                //      per table.
700               
701                        // TODO - if the paragraph is in a table?
702                               
703                //      Next, numbered item and paragraph properties are applied to each paragraph
704                //      formatted with a *numbering *style**.
705               
706//                       TODO - who uses numbering styles (as opposed to numbering
707                        // via a paragraph style or direct formatting)?
708               
709                //  Next, paragraph and run properties are
710                //      applied to each paragraph as defined by the paragraph style
711                // (this includes run properties defined in a paragraph style,
712                //  but not run properties directly included in a pPr in the
713                //  document (those only apply to a paragraph mark).
714                       
715                        if (pPr==null) {
716                                log.debug("pPr was null");
717                        } else {
718                                // At the pPr level, what rPr do we have?
719                                // .. ascend the paragraph style tree
720                                if (pPr.getPStyle()==null) {
721//                                      log.warn("No pstyle:");
722//                                      log.debug(XmlUtils.marshaltoString(pPr, true, true));
723                                } else {
724                                        log.debug("pstyle:" + pPr.getPStyle().getVal());
725                                        RPr pPrLevelRunStyle = getEffectiveRPr(pPr.getPStyle().getVal());
726                                        // .. and apply those
727                                        applyRPr(pPrLevelRunStyle, effectiveRPr);
728                                }
729                                // Check Paragraph rPr (our special hack of using ParaRPr
730                                // to format a fo:block)
731                                if ((expressRPr == null) && (pPr.getRPr() != null) && (hasDirectRPrFormatting(pPr.getRPr())) ) {                       
732                                        applyRPr(pPr.getRPr(), effectiveRPr);
733                                } 
734                        }
735                //      Next, run properties are applied to each run with a specific character style
736                //      applied.               
737                RPr resolvedRPr = null;
738                String runStyleId;
739                if (expressRPr != null && expressRPr.getRStyle() != null ) {
740                        runStyleId = expressRPr.getRStyle().getVal();
741                        resolvedRPr = getEffectiveRPr(runStyleId);
742                        applyRPr(resolvedRPr, effectiveRPr); 
743                }
744                               
745                //      Finally, we apply direct formatting (run properties not from
746                //      styles).               
747                if (hasDirectRPrFormatting(expressRPr) ) {                     
748                        //effectiveRPr = (RPr)XmlUtils.deepCopy(effectiveRPr);                 
749                        applyRPr(expressRPr, effectiveRPr);
750                } 
751                return effectiveRPr;
752               
753        }
754       
755        public RPr getEffectiveRPr(String styleId) {
756                // styleId passed in could be a run style
757                // or a *paragraph* style
758               
759                RPr resolvedRPr = resolvedStyleRPrComponent.get(styleId);
760               
761                if (resolvedRPr!=null) {
762                        return resolvedRPr;
763                }
764               
765                // Hmm, have to do the work
766                Style s = liveStyles.get(styleId);
767               
768                if (s==null) {
769                        log.error("Couldn't find style: " + styleId);
770                        return null;
771                }
772
773                // Comment out - this style might not have rPr,
774                // but an ancestor might!
775               
776//              RPr expressRPr = s.getRPr();
777//              if (expressRPr==null) {
778//                      log.error("style: " + runStyleId + " has no RPr");
779//                      resolvedRPr = resolvedStyleRPrComponent.get(defaultCharacterStyleId);
780//                      return resolvedRPr;
781//              }
782
783               
784                //      Next, run properties are applied to each run with a specific character style
785                //      applied.               
786                Stack<RPr> rPrStack = new Stack<RPr>();
787                // Haven't done this one yet                           
788                fillRPrStack(styleId, rPrStack);
789                // Finally, on top
790                rPrStack.push(documentDefaultRPr);
791                                               
792                resolvedRPr = factory.createRPr();                     
793                // Now, apply the properties starting at the top of the stack
794                while (!rPrStack.empty() ) {
795                        RPr rPr = rPrStack.pop();
796                        applyRPr(rPr, resolvedRPr);
797                }
798                resolvedStyleRPrComponent.put(styleId, resolvedRPr);
799                return resolvedRPr;
800        }
801       
802        org.docx4j.wml.ObjectFactory factory = new org.docx4j.wml.ObjectFactory();
803       
804        private BooleanDefaultTrue newBooleanDefaultTrue(boolean val) {
805               
806                BooleanDefaultTrue newBooleanDefaultTrue = factory.createBooleanDefaultTrue();
807                newBooleanDefaultTrue.setVal(Boolean.valueOf(val));
808                return newBooleanDefaultTrue;
809        }
810       
811        private boolean hasDirectPPrFormatting(PPr pPrToApply) {
812               
813                // NB, any rPr is intentionally ignored,
814                // since pPr/rPr is not applicable to anything
815                // except the paragraph mark
816               
817                if (pPrToApply==null) {
818                        return false;
819                }
820               
821                // Here is where we do the real work. 
822                // There are a lot of paragraph properties
823                // The below list is taken directly from PPrBase.
824               
825                //PPrBase.PStyle pStyle;
826               
827                        // Ignore
828               
829                //BooleanDefaultTrue keepNext;
830                if (pPrToApply.getKeepNext()!=null) {
831                        return true;           
832                }
833               
834       
835                //BooleanDefaultTrue keepLines;
836                if (pPrToApply.getKeepLines()!=null) {
837                        return true;           
838                }
839       
840                //BooleanDefaultTrue pageBreakBefore;
841                if (pPrToApply.getPageBreakBefore()!=null) {
842                        return true;           
843                }
844       
845                //CTFramePr framePr;
846                //BooleanDefaultTrue widowControl;
847                if (pPrToApply.getWidowControl()!=null) {
848                        return true;           
849                }
850       
851                //PPrBase.NumPr numPr;
852                //NumPr numPr;
853                if (pPrToApply.getNumPr()!=null) {
854                        return true;           
855                }
856       
857                //BooleanDefaultTrue suppressLineNumbers;
858                if (pPrToApply.getSuppressLineNumbers()!=null) {
859                        return true;           
860                }
861               
862                // PBdr pBdr;
863                if (pPrToApply.getPBdr()!=null) {
864                        return true;           
865                }
866       
867                //CTShd shd;
868                if (pPrToApply.getShd()!=null) {
869                        return true;           
870                }
871                       
872                //Tabs tabs;
873                if (pPrToApply.getTabs()!=null) {
874                        return true;           
875                }
876       
877                //BooleanDefaultTrue suppressAutoHyphens;
878                //BooleanDefaultTrue kinsoku;
879                //BooleanDefaultTrue wordWrap;
880                //BooleanDefaultTrue overflowPunct;
881                //BooleanDefaultTrue topLinePunct;
882                //BooleanDefaultTrue autoSpaceDE;
883                //BooleanDefaultTrue autoSpaceDN;
884                //BooleanDefaultTrue bidi;
885                //BooleanDefaultTrue adjustRightInd;
886                //BooleanDefaultTrue snapToGrid;
887                //PPrBase.Spacing spacing;
888                if (pPrToApply.getSpacing()!=null) {
889                        return true;           
890                }
891       
892                //PPrBase.Ind ind;
893                if (pPrToApply.getInd()!=null) {
894                        return true;           
895                }
896       
897                //BooleanDefaultTrue contextualSpacing;
898                //BooleanDefaultTrue mirrorIndents;
899                //BooleanDefaultTrue suppressOverlap;
900                //Jc jc;
901                if (pPrToApply.getJc()!=null) {
902                        return true;           
903                }
904       
905                //TextDirection textDirection;
906                //PPrBase.TextAlignment textAlignment;
907                if (pPrToApply.getTextAlignment()!=null ) {
908                        return true;           
909                }
910       
911                //CTTextboxTightWrap textboxTightWrap;
912                //PPrBase.OutlineLvl outlineLvl;
913                if (pPrToApply.getOutlineLvl()!=null ) {
914                        return true;           
915                }
916                //PPrBase.DivId divId;
917                //CTCnf cnfStyle;
918                return false;
919               
920        }
921       
922       
923        protected void applyPPr(PPr pPrToApply, PPr effectivePPr) {
924               
925                log.debug( "apply " + XmlUtils.marshaltoString(pPrToApply,  true, true)
926                        + "\n\r to " + XmlUtils.marshaltoString(effectivePPr,  true, true) );
927               
928                if (pPrToApply==null) {
929                        return;
930                }
931               
932        List<Property> properties = PropertyFactory.createProperties(wordMLPackage, pPrToApply); 
933        for( Property p :  properties ) {
934                        if (p!=null) {
935//                              log.debug("applying pPr " + p.getClass().getName() );
936                                ((AbstractParagraphProperty)p).set(effectivePPr);  // NB, this new method does not copy. TODO?
937                        }
938        }
939
940                log.debug( "result " + XmlUtils.marshaltoString(effectivePPr,  true, true) );
941       
942        }
943       
944        private boolean hasDirectRPrFormatting(RPr rPrToApply) {
945               
946                if (rPrToApply==null) {
947                        return false;
948                }
949               
950                // Here is where we do the real work. 
951                // There are a lot of run properties
952                // The below list is taken directly from RPr, and so
953                // is comprehensive.
954               
955                //RStyle rStyle;
956                //RFonts rFonts;
957                if (rPrToApply.getRFonts()!=null ) {
958                        return true;
959                }
960
961                //BooleanDefaultTrue b;
962                if (rPrToApply.getB()!=null) {
963                        return true;                   
964                }
965
966                //BooleanDefaultTrue bCs;
967                //BooleanDefaultTrue i;
968                if (rPrToApply.getI()!=null) {
969                        return true;                   
970                }
971
972                //BooleanDefaultTrue iCs;
973                //BooleanDefaultTrue caps;
974                if (rPrToApply.getCaps()!=null) {
975                        return true;                   
976                }
977
978                //BooleanDefaultTrue smallCaps;
979                if (rPrToApply.getSmallCaps()!=null) {
980                        return true;                   
981                }
982
983                //BooleanDefaultTrue strike;
984                if (rPrToApply.getStrike()!=null) {
985                        return true;                   
986                }
987                //BooleanDefaultTrue dstrike;
988                //BooleanDefaultTrue outline;
989                //BooleanDefaultTrue shadow;
990                //BooleanDefaultTrue emboss;
991                //BooleanDefaultTrue imprint;
992                //BooleanDefaultTrue noProof;
993                //BooleanDefaultTrue snapToGrid;
994                //BooleanDefaultTrue vanish;
995                //BooleanDefaultTrue webHidden;
996                //Color color;
997                if (rPrToApply.getColor()!=null ) {
998                        return true;                   
999                }
1000
1001                //CTSignedTwipsMeasure spacing;
1002                //CTTextScale w;
1003                //HpsMeasure kern;
1004                //CTSignedHpsMeasure position;
1005                //HpsMeasure sz;
1006                if (rPrToApply.getSz()!=null ) {
1007                        return true;                   
1008                }
1009
1010                //HpsMeasure szCs;
1011                //Highlight highlight;
1012                if (rPrToApply.getHighlight()!=null ) {
1013                        return true;                   
1014                }
1015                //U u;
1016                if (rPrToApply.getU()!=null ) {
1017                        return true;                   
1018                }
1019
1020                //CTTextEffect effect;
1021                //CTBorder bdr;
1022                if (rPrToApply.getBdr()!=null ) {
1023                        return true;                   
1024                }
1025                //CTShd shd;
1026                if (rPrToApply.getShd()!=null ) {
1027                        return true;                   
1028                }
1029                //CTFitText fitText;
1030                //CTVerticalAlignRun vertAlign;
1031                //BooleanDefaultTrue rtl;
1032                //BooleanDefaultTrue cs;
1033                //CTEm em;
1034                //CTLanguage lang;
1035                //CTEastAsianLayout eastAsianLayout;
1036                //BooleanDefaultTrue specVanish;
1037                //BooleanDefaultTrue oMath;
1038                //CTRPrChange rPrChange;
1039               
1040                // If we got here...
1041                return false;
1042        }
1043       
1044        private boolean hasDirectRPrFormatting(ParaRPr rPrToApply) {
1045               
1046                if (rPrToApply==null) {
1047                        return false;
1048                }
1049               
1050                // Here is where we do the real work. 
1051                // There are a lot of run properties
1052                // The below list is taken directly from RPr, and so
1053                // is comprehensive.
1054               
1055                //RStyle rStyle;
1056                //RFonts rFonts;
1057                if (rPrToApply.getRFonts()!=null ) {
1058                        return true;
1059                }
1060
1061                //BooleanDefaultTrue b;
1062                if (rPrToApply.getB()!=null) {
1063                        return true;                   
1064                }
1065
1066                //BooleanDefaultTrue bCs;
1067                //BooleanDefaultTrue i;
1068                if (rPrToApply.getI()!=null) {
1069                        return true;                   
1070                }
1071
1072                //BooleanDefaultTrue iCs;
1073                //BooleanDefaultTrue caps;
1074                if (rPrToApply.getCaps()!=null) {
1075                        return true;                   
1076                }
1077
1078                //BooleanDefaultTrue smallCaps;
1079                if (rPrToApply.getSmallCaps()!=null) {
1080                        return true;                   
1081                }
1082
1083                //BooleanDefaultTrue strike;
1084                if (rPrToApply.getStrike()!=null) {
1085                        return true;                   
1086                }
1087                //BooleanDefaultTrue dstrike;
1088                //BooleanDefaultTrue outline;
1089                //BooleanDefaultTrue shadow;
1090                //BooleanDefaultTrue emboss;
1091                //BooleanDefaultTrue imprint;
1092                //BooleanDefaultTrue noProof;
1093                //BooleanDefaultTrue snapToGrid;
1094                //BooleanDefaultTrue vanish;
1095                //BooleanDefaultTrue webHidden;
1096                //Color color;
1097                if (rPrToApply.getColor()!=null ) {
1098                        return true;                   
1099                }
1100
1101                //CTSignedTwipsMeasure spacing;
1102                //CTTextScale w;
1103                //HpsMeasure kern;
1104                //CTSignedHpsMeasure position;
1105                //HpsMeasure sz;
1106                if (rPrToApply.getSz()!=null ) {
1107                        return true;                   
1108                }
1109
1110                //HpsMeasure szCs;
1111                //Highlight highlight;
1112                //U u;
1113                if (rPrToApply.getU()!=null ) {
1114                        return true;                   
1115                }
1116
1117                //CTTextEffect effect;
1118                //CTBorder bdr;
1119                //CTShd shd;
1120                //CTFitText fitText;
1121                //CTVerticalAlignRun vertAlign;
1122                //BooleanDefaultTrue rtl;
1123                //BooleanDefaultTrue cs;
1124                //CTEm em;
1125                //CTLanguage lang;
1126                //CTEastAsianLayout eastAsianLayout;
1127                //BooleanDefaultTrue specVanish;
1128                //BooleanDefaultTrue oMath;
1129                //CTRPrChange rPrChange;
1130               
1131                // If we got here...
1132                return false;
1133        }
1134       
1135        protected void applyRPr(RPr rPrToApply, RPr effectiveRPr) {
1136               
1137                if (rPrToApply==null) {
1138                        return;
1139                }
1140               
1141        List<Property> properties = PropertyFactory.createProperties(null, rPrToApply); // wmlPackage null
1142       
1143        for( Property p :  properties ) {
1144                        if (p!=null) {                 
1145                                ((AbstractRunProperty)p).set(effectiveRPr);  // NB, this new method does not copy. TODO?
1146                        }
1147        }
1148               
1149        }       
1150       
1151        protected void applyRPr(ParaRPr rPrToApply, RPr effectiveRPr) {
1152               
1153                if (rPrToApply==null) {
1154                        return;
1155                }
1156               
1157        List<Property> properties = PropertyFactory.createProperties(null, rPrToApply); // wmlPackage null
1158       
1159        for( Property p :  properties ) {
1160                        if (p!=null) {                 
1161                                ((AbstractRunProperty)p).set(effectiveRPr);  // NB, this new method does not copy. TODO?
1162                        }
1163        }
1164        }       
1165       
1166       
1167        /**
1168         * Ascend the style hierarchy, capturing the pPr bit
1169         * 
1170         * @param stylename
1171         * @param effectivePPr
1172         */
1173        private void fillPPrStack(String styleId, Stack<PPr> pPrStack) {
1174                // The return value is the style on which styleId is based.
1175                // It is purely for the purposes of ascertainNumId.
1176               
1177                // get the style
1178                Style style = liveStyles.get(styleId);
1179               
1180                // add it to the stack
1181                if (style==null) {
1182                        // No such style!
1183                        // For now, just log it..
1184                        log.error("Style definition not found: " + styleId);
1185                        return;
1186                }
1187                pPrStack.push(style.getPPr());
1188                log.debug("Added " + styleId + " to pPr stack");
1189               
1190                // Some styles contain numPr, without specifying
1191                // their numId!  In this case you have to get it
1192                // from the numPr in their basedOn style.
1193                // To save numbering emulator from having to do
1194                // that work, we make the numId explicit here.
1195                boolean ascertainNumId = false;
1196                if (style.getPPr()!=null 
1197                                && style.getPPr().getNumPr()!=null
1198                                && style.getPPr().getNumPr().getNumId()==null) {
1199
1200                        ascertainNumId = true;                 
1201                        log.debug(styleId +" ascertainNumId: " + ascertainNumId);
1202                } else {
1203                        log.debug(styleId +" ascertainNumId: " + ascertainNumId);                       
1204                }
1205               
1206                // if it is based on, recurse
1207        if (style.getBasedOn()==null) {
1208                        log.debug("Style " + styleId + " is a root style.");
1209        } else if (style.getBasedOn().getVal()!=null) {
1210                String basedOnStyleName = style.getBasedOn().getVal();                 
1211                        log.debug("Style " + styleId + " is based on " + basedOnStyleName);
1212                fillPPrStack( basedOnStyleName, pPrStack);
1213                Style basedOnStyle = liveStyles.get(basedOnStyleName);
1214                if (ascertainNumId && basedOnStyle!=null) {
1215                        // This works via recursion                     
1216                        //log.debug( XmlUtils.marshaltoString(basedOnStyle, true, true));
1217                        if (basedOnStyle.getPPr()!=null 
1218                                        && basedOnStyle.getPPr().getNumPr()!=null
1219                                        && basedOnStyle.getPPr().getNumPr().getNumId()!=null) {
1220                                NumId numId = basedOnStyle.getPPr().getNumPr().getNumId();
1221                                // Attach it at this level - for this to work,
1222                                // you can't have a style in the basedOn hierarchy
1223                                // which doesn't have a numPr element, because
1224                                // in that case there is nowhere to hang the style
1225                                style.getPPr().getNumPr().setNumId(numId);
1226                                log.info("Injected numId " + numId);
1227                        }
1228                }
1229        } else {
1230                log.debug("No basedOn set for: " + style.getStyleId() );
1231        }
1232               
1233        }
1234
1235        /**
1236         * Ascend the style hierarchy, capturing the rPr bit
1237         * 
1238         * @param stylename
1239         * @param effectivePPr
1240         */
1241        private void fillRPrStack(String styleId, Stack<RPr> rPrStack) {
1242               
1243                // get the style
1244                Style style = liveStyles.get(styleId);
1245               
1246                // add it to the stack
1247                if (style==null) {
1248                        // No such style!
1249                        // For now, just log it..
1250                        log.error("Style definition not found: " + styleId);
1251                        return;
1252                }
1253                rPrStack.push(style.getRPr());
1254                log.debug("Added " + styleId + " to pPr stack");
1255               
1256                // if it is based on, recurse
1257        if (style.getBasedOn()==null) {
1258                        log.debug("Style " + styleId + " is a root style.");
1259        } else if (style.getBasedOn().getVal()!=null) {
1260                String basedOnStyleName = style.getBasedOn().getVal();                 
1261                fillRPrStack( basedOnStyleName, rPrStack);
1262        } else {
1263                log.debug("No basedOn set for: " + style.getStyleId() );
1264        }
1265               
1266        }
1267       
1268       
1269    private void initialiseLiveStyles() {
1270       
1271        log.debug("initialiseLiveStyles()");
1272                liveStyles = new java.util.HashMap<String, org.docx4j.wml.Style>();
1273               
1274                for ( org.docx4j.wml.Style s : styles.getStyle() ) {                           
1275                        liveStyles.put(s.getStyleId(), s);     
1276//                      log.debug("live style: " + s.getStyleId() );
1277                }
1278       
1279    }
1280               
1281
1282       
1283        /**
1284         * Returns default document font, by attempting to look at styles/docDefaults/rPrDefault/rPr/rFonts.
1285         *
1286         * @return default document font.
1287         */
1288        public String getDefaultFont() {
1289               
1290                // First look at the defaults
1291                // 3 look at styles/rPrDefault
1292                // 3.1 if there is an rFonts element, do what it says (it may refer you to the theme part,
1293                //     in which case if there is no theme part, default to "internally stored settings"
1294                //         (there is no normal.dot; see http://support.microsoft.com/kb/924460/en-us )
1295                //         in this case Calibri and Cambria)
1296                // 3.2 if there is no rFonts element, default to Times New Roman.
1297               
1298                org.docx4j.wml.RFonts rFonts = documentDefaultRPr.getRFonts();
1299                if (rFonts==null) {
1300                        log.info("No styles/docDefaults/rPrDefault/rPr/rFonts - default to Times New Roman");
1301                        // Yes, Times New Roman is still buried in Word 2007
1302                        return "Times New Roman";                                               
1303                } else {                                               
1304                        // Usual case
1305                        if (rFonts.getAsciiTheme()!=null ) {
1306                                // for example minorHAnsi, which I think translates to minorFont/latin
1307                                if (rFonts.getAsciiTheme().equals(org.docx4j.wml.STTheme.MINOR_H_ANSI)) {
1308                                        if (themePart!=null) {
1309                                                org.docx4j.dml.BaseStyles.FontScheme fontScheme = themePart.getFontScheme();
1310                                                if (fontScheme.getMinorFont()!=null
1311                                                                && fontScheme.getMinorFont().getLatin()!=null) {
1312                                                                                                                                       
1313                                                        org.docx4j.dml.TextFont textFont = fontScheme.getMinorFont().getLatin();
1314                                                        log.debug("minorFont/latin font is " + textFont.getTypeface() );
1315                                                        return textFont.getTypeface(); 
1316                                                } else {
1317                                                        // No minorFont/latin in theme part - default to Calibri
1318                                                        log.info("No minorFont/latin in theme part - default to Calibri");                                                             
1319                                                        return "Calibri"; 
1320                                                }
1321                                        } else {
1322                                                // No theme part - default to Calibri
1323                                                log.info("No theme part - default to Calibri");
1324                                                return "Calibri"; 
1325                                        }
1326                                } else {
1327                                        // TODO
1328                                        log.error("Don't know how to handle: "
1329                                                        + rFonts.getAsciiTheme());
1330                                        return null;
1331                                }
1332                        } else if (rFonts.getAscii()!=null ) {
1333                                log.info("rPrDefault/rFonts referenced " + rFonts.getAscii());                                                         
1334                                return rFonts.getAscii();                                                       
1335                        } else {
1336                                // TODO
1337                                log.error("Neither ascii or asciTheme.  What to do? ");
1338                                return null;
1339                        }                                               
1340                } 
1341        }
1342       
1343       
1344    public boolean activateStyle( String styleId  ) {
1345       
1346        if (liveStyles.get(styleId)!=null) {
1347                // Its already live - nothing to do
1348                return true;                   
1349        }
1350       
1351        java.util.Map<String, org.docx4j.wml.Style> knownStyles
1352                = styleDefinitionsPart.getKnownStyles();
1353       
1354        org.docx4j.wml.Style s = knownStyles.get(styleId);
1355       
1356        if (s==null) {
1357                log.error("Unknown style: " + styleId);
1358                return false;
1359        }
1360               
1361        return activateStyle(s, false); 
1362                // false -> don't replace an existing live style with a template
1363       
1364    }
1365   
1366    public boolean activateStyle(org.docx4j.wml.Style s) {
1367
1368        return activateStyle(s, true);
1369       
1370    }
1371
1372    private boolean activateStyle(org.docx4j.wml.Style s, boolean replace) {
1373       
1374        if (liveStyles.get(s.getStyleId())!=null) {
1375                // Its already live
1376               
1377                if (!replace) {                         
1378                        return false;
1379                }
1380               
1381                // Remove existing entry
1382                        styles.getStyle().remove( 
1383                                        liveStyles.get(s.getStyleId()) );                               
1384        }
1385       
1386        // Add it
1387        // .. to the JAXB object
1388        styles.getStyle().add(s);
1389        // .. here
1390        liveStyles.put(s.getStyleId(), s);
1391       
1392        // Now, recursively check that what it is based on is present
1393        boolean result1;
1394        if (s.getBasedOn()!=null) {
1395                String basedOn = s.getBasedOn().getVal();
1396                result1 = activateStyle( basedOn );
1397               
1398        } else if ( s.getStyleId().equals(defaultParagraphStyleId)
1399                        || s.getStyleId().equals(defaultCharacterStyleId) )
1400        {
1401                // stop condition
1402                result1 = true;
1403        } else {
1404               
1405                log.error("Expected " + s.getStyleId() + " to have <w:basedOn ??");
1406                // Not properly activated
1407                result1 = false;
1408        }
1409       
1410        // Also add the linked style, if any
1411        // .. Word might expect it to be there
1412        boolean result2 = true;
1413        if (s.getLink()!=null) {
1414               
1415                org.docx4j.wml.Style.Link link = s.getLink();
1416                result2 = activateStyle(link.getVal());
1417               
1418        }
1419       
1420        return (result1 & result2);
1421               
1422    }
1423   
1424    public org.docx4j.wml.Style getStyle(String styleId) {
1425       
1426        return liveStyles.get(styleId);
1427    }
1428       
1429// TODO - what follows is old code.  It is however necessary to be able to
1430// get all fonts. 
1431// 1.  can the above code be modified to just get a single property (ie fonts)?
1432// 2.  we only really want to climb the hierarchy once per style, so
1433//     introduce an effectivePr map?
1434   
1435    /**
1436     * Determine the font used in this style, using the inheritance rules.
1437     *
1438     * @return the font name, or null if there is no rFonts element in any style
1439     * in the style inheritance hierarchy (ie
1440     * this method does not look up styles/docDefaults/rPrDefault/rPr/rFonts
1441     * or from there, the theme part - see getDefaultFont to do that).
1442     *
1443     * @see getDefaultFont
1444     */ 
1445    public String getFontnameFromStyle(org.docx4j.wml.Style style) {
1446       
1447        return getFontnameFromStyle(styleDefinitionsPart, themePart, style); 
1448       
1449    }
1450       
1451    /**
1452     * Determine the font used in this style, using the inheritance rules.
1453     *
1454     * @return the font name, or null if there is no rFonts element in any style
1455     * in the style inheritance hierarchy (ie
1456     * this method does not look up styles/docDefaults/rPrDefault/rPr/rFonts
1457     * or from there, the theme part - see getDefaultFont to do that).
1458     *
1459     * @see getDefaultFont
1460     */ 
1461    public static String getFontnameFromStyle(StyleDefinitionsPart styleDefinitionsPart, ThemePart themePart,  org.docx4j.wml.Style style) {
1462
1463                org.docx4j.wml.Styles styles = (org.docx4j.wml.Styles)styleDefinitionsPart.getJaxbElement();
1464//              org.docx4j.wml.Styles styles = (org.docx4j.wml.Styles)this.getStyleDefinitionsPart().getJaxbElement();
1465
1466                // It is convenient to have a HashMap of styles
1467                Map stylesDefined = new java.util.HashMap();
1468             for (Iterator iter = styles.getStyle().iterator(); iter.hasNext();) {
1469                    org.docx4j.wml.Style s = (org.docx4j.wml.Style)iter.next();
1470                    stylesDefined.put(s.getStyleId(), s);
1471             }
1472             
1473            return getFontnameFromStyle(stylesDefined, themePart, style);
1474    }
1475    /**
1476     *
1477     * @return the font name, or null if there is no rFonts element in any style
1478     * in the style inheritance hierarchy (ie
1479     * this method does not look up styles/docDefaults/rPrDefault/rPr/rFonts
1480     * or from there, the theme part).
1481     */
1482    public static String getFontnameFromStyle(Map stylesDefined, ThemePart themePart, org.docx4j.wml.Style style) {
1483       
1484                /*
1485                a paragraph style does not inherit anything from its linked character style.
1486
1487                A linked character style seems to be just a Word 2007 user interface
1488                hint.  ie if you select some characters in a paragraph and select to
1489                apply "Heading 1", what you are actually doing is applying "Heading 1
1490                char".  This is determined by looking at the definition of the
1491                "Heading 1" style to see what its linked style is.
1492               
1493                (Interestingly, in Word 2007, if you right click to modify something
1494                 which is Heading 1 char, it modifies both the Heading 1 style and the
1495                 Heading 1 char style!.  Haven't looked to see what happens to Heading 1 char
1496                 style if you right click to modify a Heading 1 par.)
1497
1498                 The algorithm Word 2007 seems to use is:
1499                    look at the specified style:
1500                        1 does it have its own rPr which contains rFonts?
1501                        2 if not, what does this styles basedOn style say? (Ignore
1502                any linked char style)
1503                                3 look at styles/rPrDefault
1504                                3.1 if there is an rFonts element, do what it says (it may refer you to the theme part,
1505                                    in which case if there is no theme part, default to "internally stored settings"
1506                                        (there is no normal.dot; see http://support.microsoft.com/kb/924460/en-us )
1507                                        in this case Calibri and Cambria)
1508                                3.2 if there is no rFonts element, default to Times New Roman.
1509               
1510               
1511                For efficiency reasons, we don't do 3 in this method.
1512                 */
1513       
1514
1515        // 1 does it have its own rPr which contains rFonts?
1516        org.docx4j.wml.RPr rPr = style.getRPr();
1517        if (rPr!=null && rPr.getRFonts()!=null) {
1518                if (rPr.getRFonts().getAscii()!=null) {
1519                        return rPr.getRFonts().getAscii();
1520                } else if (rPr.getRFonts().getAsciiTheme()!=null 
1521                                        && themePart != null) {
1522                        log.debug("Encountered rFonts/AsciiTheme: " + rPr.getRFonts().getAsciiTheme() );
1523                       
1524                                org.docx4j.dml.Theme theme = (org.docx4j.dml.Theme)themePart.getJaxbElement();
1525                                org.docx4j.dml.BaseStyles.FontScheme fontScheme = themePart.getFontScheme();
1526                                if (rPr.getRFonts().getAsciiTheme().equals(org.docx4j.wml.STTheme.MINOR_H_ANSI)) {
1527                                        if (fontScheme != null && fontScheme.getMinorFont().getLatin() != null) {
1528                                                fontScheme = theme.getThemeElements().getFontScheme();
1529                                                org.docx4j.dml.TextFont textFont = fontScheme.getMinorFont().getLatin();
1530                                                log.info("minorFont/latin font is " + textFont.getTypeface());
1531                                                return (textFont.getTypeface());
1532                                        } else {
1533                                                // No minorFont/latin in theme part - default to Calibri
1534                                                log.info("No minorFont/latin in theme part - default to Calibri");
1535                                                return ("Calibri");
1536                                        }
1537                                } else if (rPr.getRFonts().getAsciiTheme().equals(org.docx4j.wml.STTheme.MAJOR_H_ANSI)) {
1538                                        if (fontScheme != null && fontScheme.getMajorFont().getLatin() != null) {
1539                                                fontScheme = theme.getThemeElements().getFontScheme();
1540                                                org.docx4j.dml.TextFont textFont = fontScheme.getMajorFont().getLatin();
1541                                                log.debug("majorFont/latin font is " + textFont.getTypeface());
1542                                                return (textFont.getTypeface());
1543                                        } else {
1544                                                // No minorFont/latin in theme part - default to Cambria
1545                                                log.info("No majorFont/latin in theme part - default to Cambria");
1546                                                return ("Cambria");
1547                                        }
1548                                } else {
1549                                        log.error("Don't know how to handle: "
1550                                                        + rPr.getRFonts().getAsciiTheme());
1551                                }
1552                }
1553        }
1554                       
1555        // 2 if not, what does this styles basedOn style say? (recursive)
1556       
1557        if (style.getBasedOn()!=null && style.getBasedOn().getVal()!=null) {
1558                String basedOnStyleName = style.getBasedOn().getVal();                 
1559                //log.debug("recursing into basedOn:" + basedOnStyleName);
1560            org.docx4j.wml.Style candidateStyle = (org.docx4j.wml.Style)stylesDefined.get(basedOnStyleName);
1561            if (candidateStyle != null && candidateStyle.getStyleId().equals(basedOnStyleName)) {
1562                return getFontnameFromStyle(stylesDefined, themePart, candidateStyle);
1563            }
1564             // If we get here the style is missing!
1565                log.error("couldn't find basedOn:" + basedOnStyleName);             
1566             return null;
1567        } else {
1568                //log.debug("No basedOn set for: " + style.getStyleId() );
1569                return null;
1570        }
1571       
1572    }
1573
1574       
1575}
Note: See TracBrowser for help on using the repository browser.