001package com.hfg.xml.msofficexml.docx.wordprocessingml;
002
003import java.awt.*;
004import java.util.HashMap;
005import java.util.Map;
006
007import com.hfg.css.CSSDeclaration;
008import com.hfg.graphics.units.GfxSize;
009import com.hfg.graphics.units.GfxUnits;
010import com.hfg.graphics.units.Points;
011import com.hfg.graphics.ColorUtil;
012import com.hfg.xml.XMLName;
013import com.hfg.xml.XMLTag;
014import com.hfg.xml.XMLizable;
015import com.hfg.xml.msofficexml.docx.Docx;
016import com.hfg.xml.msofficexml.docx.wordprocessingml.style.WmlHighlightColor;
017import com.hfg.xml.msofficexml.docx.wordprocessingml.style.WmlShading;
018import com.hfg.xml.msofficexml.docx.wordprocessingml.style.WmlSpacing;
019import com.hfg.xml.msofficexml.docx.wordprocessingml.style.WmlTextBorder;
020
021//------------------------------------------------------------------------------
022/**
023 Text run properties for Docx's Office Open XML documents.
024
025 @author J. Alex Taylor, hairyfatguy.com
026 */
027//------------------------------------------------------------------------------
028// com.hfg XML/HTML Coding Library
029//
030// This library is free software; you can redistribute it and/or
031// modify it under the terms of the GNU Lesser General Public
032// License as published by the Free Software Foundation; either
033// version 2.1 of the License, or (at your option) any later version.
034//
035// This library is distributed in the hope that it will be useful,
036// but WITHOUT ANY WARRANTY; without even the implied warranty of
037// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
038// Lesser General Public License for more details.
039//
040// You should have received a copy of the GNU Lesser General Public
041// License along with this library; if not, write to the Free Software
042// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
043//
044// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
045// jataylor@hairyfatguy.com
046//------------------------------------------------------------------------------
047//
048// Note: In section 2.4.1 it states <span style='font-style:italic'>"It should also be noted that a pPr element
049// may contain a set of run properties within a rPr element - these properties
050// are applied to the run which contains the glyph which represents the paragraph
051// mark and not the entire paragraph."</span> It seems very inefficient to structure things
052// this way but the format seems to force you to duplicate the run properties for every text run in a paragraph.
053
054public class WmlTextRunProperties extends WmlXMLTag
055{
056
057   private XMLTag     mStyle;
058   private XMLTag     mUnderlineTag;
059   private XMLTag     mColorTag;
060   private XMLTag     mRunFontsTag;
061   private XMLTag     mShadowTag;
062   private XMLTag     mHighlightTag;
063   private WmlShading mShadingTag;
064   private WmlSpacing mSpacingTag;
065   private XMLTag     mSmallCapsTag;
066   private XMLTag     mSizeTag;
067   private XMLTag     mCsSizeTag;
068   private XMLTag     mStrikeTag;
069   private XMLTag     mVanishTag;
070   private XMLTag     mVertAlignTag;
071   private WmlTextBorder mBorder;
072   private XMLTag     mNoProof;
073
074   //##########################################################################
075   // CONSTRUCTORS
076   //##########################################################################
077
078   //---------------------------------------------------------------------------
079   public WmlTextRunProperties(Docx inDocx)
080   {
081      super(WmlXML.RUN_PROPS, inDocx);
082   }
083
084
085   //##########################################################################
086   // PUBLIC METHODS
087   //##########################################################################
088
089   //---------------------------------------------------------------------------
090   // For now we will just remove the subtags being overridden
091   public WmlTextRunProperties combine(WmlTextRunProperties inPropertiesToOverlay)
092   {
093      if (inPropertiesToOverlay != null)
094      {
095         Map<String, XMLizable> subtagMap = new HashMap<>(15);
096         for (XMLizable subtag : getSubtags())
097         {
098            subtagMap.put(((XMLTag)subtag).getTagName(), subtag);
099         }
100
101         for (XMLizable subtag : inPropertiesToOverlay.getSubtags())
102         {
103            XMLizable existingSubtag = subtagMap.get(((XMLTag)subtag).getTagName());
104            if (existingSubtag != null)
105            {
106               removeSubtag(existingSubtag);
107            }
108
109            addSubtag(subtag);
110         }
111      }
112
113      return this;
114   }
115
116   //---------------------------------------------------------------------------
117   public WmlTextRunProperties setStyle(String inStyleId)
118   {
119      if (null == mStyle)
120      {
121         mStyle = new XMLTag(WmlXML.RUN_STYLE);
122         addSubtag(0, mStyle);
123      }
124
125      mStyle.setAttribute(WmlXML.VALUE_ATT, inStyleId);
126
127      return this;
128   }
129
130   //---------------------------------------------------------------------------
131   public WmlTextRunProperties setBold()
132   {
133      if (null == getOptionalSubtagByName(WmlXML.BOLD))
134      {
135         addSubtag(new XMLTag(WmlXML.BOLD));
136      }
137
138      return this;
139   }
140
141   //---------------------------------------------------------------------------
142   public WmlTextRunProperties setComplexScriptBold()
143   {
144      if (null == getOptionalSubtagByName(WmlXML.COMPLEX_SCRIPT_BOLD))
145      {
146         addSubtag(new XMLTag(WmlXML.COMPLEX_SCRIPT_BOLD));
147      }
148
149      return this;
150   }
151
152   //---------------------------------------------------------------------------
153   public WmlTextRunProperties setItalics()
154   {
155      if (null == getOptionalSubtagByName(WmlXML.ITALICS))
156      {
157         addSubtag(new XMLTag(WmlXML.ITALICS));
158      }
159
160      return this;
161   }
162
163   //---------------------------------------------------------------------------
164   public WmlTextRunProperties setComplexScriptItalics()
165   {
166      if (null == getOptionalSubtagByName(WmlXML.COMPLEX_SCRIPT_ITALICS))
167      {
168         addSubtag(new XMLTag(WmlXML.COMPLEX_SCRIPT_ITALICS));
169      }
170
171      return this;
172   }
173
174   //---------------------------------------------------------------------------
175   public WmlTextRunProperties setUnderline()
176   {
177      if (null == mUnderlineTag)
178      {
179         mUnderlineTag = new XMLTag(WmlXML.UNDERLINE);
180         addSubtag(mUnderlineTag);
181      }
182
183      mUnderlineTag.setAttribute(WmlXML.VALUE_ATT, "single");
184
185      return this;
186   }
187
188   //---------------------------------------------------------------------------
189   public WmlTextRunProperties setSuperscript()
190   {
191      if (null == mVertAlignTag)
192      {
193         mVertAlignTag = new XMLTag(WmlXML.VERT_ALIGN);
194         addSubtag(mVertAlignTag);
195      }
196
197      mVertAlignTag.setAttribute(WmlXML.VALUE_ATT, "superscript");
198
199      return this;
200   }
201
202   //---------------------------------------------------------------------------
203   public WmlTextRunProperties setSubscript()
204   {
205      if (null == mVertAlignTag)
206      {
207         mVertAlignTag = new XMLTag(WmlXML.VERT_ALIGN);
208         addSubtag(mVertAlignTag);
209      }
210
211      mVertAlignTag.setAttribute(WmlXML.VALUE_ATT, "subscript");
212
213      return this;
214   }
215
216   //---------------------------------------------------------------------------
217   public WmlTextRunProperties setShadow()
218   {
219      if (null == mShadowTag)
220      {
221         mShadowTag = new XMLTag(WmlXML.SHADOW);
222         addSubtag(mShadowTag);
223      }
224
225      mShadowTag.setAttribute(WmlXML.VALUE_ATT, "true");
226
227      return this;
228   }
229
230   //---------------------------------------------------------------------------
231   public WmlTextRunProperties setSmallCaps()
232   {
233      if (null == mSmallCapsTag)
234      {
235         mSmallCapsTag = new XMLTag(WmlXML.SMALL_CAPS);
236         addSubtag(mSmallCapsTag);
237      }
238
239      mSmallCapsTag.setAttribute(WmlXML.VALUE_ATT, "true");
240
241      return this;
242   }
243
244   //---------------------------------------------------------------------------
245   public WmlTextRunProperties setColor(Color inColor)
246   {
247      return setColor(WmlXML.VALUE_ATT, ColorUtil.colorToHex(inColor));
248   }
249
250   //---------------------------------------------------------------------------
251   public WmlTextRunProperties setAutoColor()
252   {
253      return setColor(WmlXML.VALUE_ATT, "auto");
254   }
255
256   //---------------------------------------------------------------------------
257   public WmlTextRunProperties setThemeColor(String inValue)
258   {
259      return setColor(WmlXML.THEME_COLOR_ATT, inValue);
260   }
261
262   //---------------------------------------------------------------------------
263   public WmlTextRunProperties setThemeShade(String inValue)
264   {
265      return setColor(WmlXML.THEME_SHADE_ATT, inValue);
266   }
267
268   //---------------------------------------------------------------------------
269   private WmlTextRunProperties setColor(XMLName inXMLAttrName, String inValue)
270   {
271      if (null == mColorTag)
272      {
273         // Check if it has been added via addSubtag()...
274         mColorTag = getOptionalSubtagByName(WmlXML.COLOR);
275         if (null == mColorTag)
276         {
277            mColorTag = new XMLTag(WmlXML.COLOR);
278            addSubtag(mColorTag);
279         }
280      }
281
282      mColorTag.setAttribute(inXMLAttrName, inValue);
283
284      return this;
285   }
286
287
288   //---------------------------------------------------------------------------
289   public WmlTextRunProperties setFont(Font inFont)
290   {
291      setFont(inFont.getName());
292      setSize(new Points(inFont.getSize()));
293
294      if (inFont.isBold())
295      {
296         setBold();
297      }
298
299      if (inFont.isItalic())
300      {
301         setItalics();
302      }
303
304      return this;
305   }
306
307   //---------------------------------------------------------------------------
308   public WmlTextRunProperties setFont(String inFont)
309   {
310      setFont(WmlXML.ASCII_ATT, inFont);
311      return setFont(WmlXML.HIGH_ANSI_ATT, inFont);
312   }
313
314   //---------------------------------------------------------------------------
315   public WmlTextRunProperties setAnsiThemeFont(String inValue)
316   {
317      return setFont(WmlXML.ANSI_THEME_ATT, inValue);
318   }
319
320   //---------------------------------------------------------------------------
321   public WmlTextRunProperties setAsciiThemeFont(String inValue)
322   {
323      return setFont(WmlXML.ASCII_THEME_ATT, inValue);
324   }
325
326   //---------------------------------------------------------------------------
327   public WmlTextRunProperties setComplexScriptThemeFont(String inValue)
328   {
329      return setFont(WmlXML.COMPLEX_SCRIPT_THEME_ATT, inValue);
330   }
331
332   //---------------------------------------------------------------------------
333   public WmlTextRunProperties setEastAsiaThemeFont(String inValue)
334   {
335      return setFont(WmlXML.EAST_ASIA_THEME_ATT, inValue);
336   }
337
338
339   //---------------------------------------------------------------------------
340   private WmlTextRunProperties setFont(XMLName inXMLAttrName, String inValue)
341   {
342      if (null == mRunFontsTag)
343      {
344         // Check if it has been added via addSubtag()...
345         mRunFontsTag = getOptionalSubtagByName(WmlXML.RUN_FONTS);
346         if (null == mRunFontsTag)
347         {
348            mRunFontsTag = new XMLTag(WmlXML.RUN_FONTS);
349            addSubtag(mRunFontsTag);
350         }
351      }
352
353      mRunFontsTag.setAttribute(inXMLAttrName, inValue);
354
355      return this;
356   }
357
358
359   //---------------------------------------------------------------------------
360   /**
361    * Specifies a positive measurement of font size.
362    * @param inValue the font size expressed in any units that extend GfxSize
363    * @return this text run properties object to enable method chaining
364    */
365   public WmlTextRunProperties setSize(GfxSize inValue)
366   {
367      if (null == mSizeTag)
368      {
369         mSizeTag = new XMLTag(WmlXML.SIZE);
370         addSubtag(mSizeTag);
371      }
372
373      mSizeTag.setAttribute(WmlXML.VALUE_ATT, inValue.to(GfxUnits.half_points));
374
375      return this;
376   }
377
378   //---------------------------------------------------------------------------
379   /**
380    * Specifies a positive measurement of complex script font size.
381    * @param inValue the complex script font size expressed in any units that extend GfxSize
382    * @return this text run properties object to enable method chaining
383    */
384   public WmlTextRunProperties setComplexScriptSize(GfxSize inValue)
385   {
386      if (null == mCsSizeTag)
387      {
388         mCsSizeTag = new XMLTag(WmlXML.COMPLEX_SCRIPT_FONT_SIZE);
389         addSubtag(mCsSizeTag);
390      }
391
392      mSizeTag.setAttribute(WmlXML.VALUE_ATT, inValue.to(GfxUnits.half_points));
393
394      return this;
395   }
396
397   //---------------------------------------------------------------------------
398   public WmlTextRunProperties setStrikeThrough()
399   {
400      if (null == mStrikeTag)
401      {
402         mStrikeTag = new XMLTag(WmlXML.STRIKE);
403         addSubtag(mStrikeTag);
404      }
405
406      mStrikeTag.setAttribute(WmlXML.VALUE_ATT, "true");
407
408      return this;
409   }
410
411   //---------------------------------------------------------------------------
412   /**
413    Specifies a highlighting color to be applied as background behind the contents:
414    &lt;w:highlight w:val="red"/&gt; This element will supersede any background shading
415    specified in shd.
416    * @param inColor Only the colors specified by this enum are allowed
417    * @return this text run properties object (facilitates chaining)
418    */
419   public WmlTextRunProperties setHighlight(WmlHighlightColor inColor)
420   {
421      if (null == inColor)
422      {
423         // Clear the highlight tag if present
424
425         // Check it it has been added via addSubtag()...
426         XMLTag highlightTag = getOptionalSubtagByName(WmlXML.HIGHLIGHT);
427         if (highlightTag != null)
428         {
429            removeSubtag(highlightTag);
430            mHighlightTag = null;
431         }
432      }
433      else
434      {
435         if (null == mHighlightTag)
436         {
437            // Check it it has been added via addSubtag()...
438            mHighlightTag = getOptionalSubtagByName(WmlXML.HIGHLIGHT);
439            if (null == mHighlightTag)
440            {
441               mHighlightTag = new XMLTag(WmlXML.HIGHLIGHT);
442               addSubtag(mHighlightTag);
443            }
444         }
445
446         mHighlightTag.setAttribute(WmlXML.VALUE_ATT, inColor.name());
447      }
448
449      return this;
450   }
451
452
453   //---------------------------------------------------------------------------
454   public WmlTextRunProperties disableSpellingAndGrammarChecks()
455   {
456      if (null == mNoProof)
457      {
458         // Check if it has been added via addSubtag()...
459         mNoProof = getOptionalSubtagByName(WmlXML.NO_PROOF);
460         if (null == mNoProof)
461         {
462            mNoProof = new XMLTag(WmlXML.NO_PROOF);
463            addSubtag(mNoProof);
464         }
465      }
466
467      return this;
468   }
469
470   //---------------------------------------------------------------------------
471   public WmlTextRunProperties vanish()
472   {
473      if (null == mVanishTag)
474      {
475         // Check if it has been added via addSubtag()...
476         mVanishTag = getOptionalSubtagByName(WmlXML.VANISH);
477         if (null == mVanishTag)
478         {
479            mVanishTag = new XMLTag(WmlXML.VANISH);
480            addSubtag(mVanishTag);
481         }
482      }
483
484      return this;
485   }
486
487
488   //---------------------------------------------------------------------------
489   public WmlTextBorder getBorder()
490   {
491      if (null == mBorder)
492      {
493         // Check if it has been added via addSubtag()...
494         mBorder = getOptionalSubtagByName(WmlXML.BORDER);
495         if (null == mBorder)
496         {
497            mBorder = new WmlTextBorder(getParentDoc());
498            addSubtag(mBorder);
499         }
500      }
501
502      return mBorder;
503   }
504
505
506   //---------------------------------------------------------------------------
507   public WmlTextBorder getBorder(CSSDeclaration inCSSDeclaration)
508   {
509      if (null == mBorder)
510      {
511         // Check if it has been added via addSubtag()...
512         mBorder = getOptionalSubtagByName(WmlXML.BORDER);
513         if (null == mBorder)
514         {
515            mBorder = new WmlTextBorder(getParentDoc(), inCSSDeclaration);
516            addSubtag(mBorder);
517         }
518      }
519
520      return mBorder;
521   }
522
523   //---------------------------------------------------------------------------
524   public WmlShading getShading()
525   {
526      if (null == mShadingTag)
527      {
528         // Check if it has been added via addSubtag()...
529         mShadingTag = getOptionalSubtagByName(WmlXML.SHADING);
530         if (null == mShadingTag)
531         {
532            mShadingTag = new WmlShading();
533            addSubtag(mShadingTag);
534         }
535      }
536
537      return mShadingTag;
538   }
539
540   //---------------------------------------------------------------------------
541   public WmlSpacing getSpacing()
542   {
543      if (null == mSpacingTag)
544      {
545         // Check if it has been added via addSubtag()...
546         mSpacingTag = getOptionalSubtagByName(WmlXML.SPACING);
547         if (null == mSpacingTag)
548         {
549            mSpacingTag = new WmlSpacing();
550            addSubtag(mSpacingTag);
551         }
552      }
553
554      return mSpacingTag;
555   }
556}