001package com.hfg.xml.msofficexml.docx.drawingml.line; 002 003 004import java.awt.*; 005import java.io.Writer; 006import java.util.*; 007import java.util.List; 008 009import com.hfg.graphics.units.GfxSize; 010import com.hfg.graphics.units.GfxUnits; 011import com.hfg.util.CompareUtil; 012import com.hfg.xml.XMLNamespace; 013import com.hfg.xml.XMLNamespaceSet; 014import com.hfg.xml.XMLNode; 015import com.hfg.xml.XMLTag; 016import com.hfg.xml.msofficexml.OfficeOpenXMLTag; 017import com.hfg.xml.msofficexml.OfficeOpenXmlDocument; 018import com.hfg.xml.msofficexml.docx.drawingml.DmlXML; 019import com.hfg.xml.msofficexml.docx.drawingml.fill.DmlFill; 020import com.hfg.xml.msofficexml.docx.drawingml.fill.DmlSolidFill; 021import com.hfg.xml.msofficexml.docx.drawingml.line.dash.DmlLineDash; 022import com.hfg.xml.msofficexml.docx.drawingml.line.dash.DmlPresetLineDash; 023import com.hfg.xml.msofficexml.docx.drawingml.line.dash.DmlPresetLineDashType; 024import com.hfg.xml.msofficexml.docx.drawingml.line.join.DmlLineJoin; 025 026//------------------------------------------------------------------------------ 027/** 028 Represents a line (<a:ln>) tag in drawing markup language (DML) from Office Open XML. 029 030 @author J. Alex Taylor, hairyfatguy.com 031 */ 032//------------------------------------------------------------------------------ 033// com.hfg XML/HTML Coding Library 034// 035// This library is free software; you can redistribute it and/or 036// modify it under the terms of the GNU Lesser General Public 037// License as published by the Free Software Foundation; either 038// version 2.1 of the License, or (at your option) any later version. 039// 040// This library is distributed in the hope that it will be useful, 041// but WITHOUT ANY WARRANTY; without even the implied warranty of 042// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 043// Lesser General Public License for more details. 044// 045// You should have received a copy of the GNU Lesser General Public 046// License along with this library; if not, write to the Free Software 047// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 048// 049// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 050// jataylor@hairyfatguy.com 051//------------------------------------------------------------------------------ 052/* 053Sequence [1..1] 054 055 1. Choice [0..1] 056 a:noFill No Fill 057 a:solidFill Solid Fill 058 a:gradFill Gradient Fill 059 a:pattFill 060 2. Choice [0..1] 061 a:prstDash Preset Dash 062 a:custDash Custom Dash 063 3. Choice [0..1] 064 a:round Round Line Join 065 a:bevel Line Join Bevel 066 a:miter Miter Line Join 067 4. a:headEnd [0..1] Line Head/End Style 068 5. a:tailEnd [0..1] Tail line end style 069 6. a:extLst [0..1] 070 */ 071 072public class DmlLine extends OfficeOpenXMLTag 073{ 074 private XMLTag mFillTag; 075 private XMLTag mLineJoinTag; 076 private DmlLineHeadEnd mLineHeadEnd; 077 private DmlLineTailEnd mLineTailEnd; 078 private DmlLineDash mLineDash; 079 080 private static final SubtagComparator SUBTAG_SEQUENCE_COMPARATOR = new DmlLine(null).new SubtagComparator(); 081 082 // TODO: Is subtag sequencing still necessary now that we use the xsd? 083 private static List<Set<String>> sSubtagSequence = new ArrayList<>(6); 084 085 static 086 { 087 Set<String> hashSet = new HashSet<>(4); 088 hashSet.add(DmlXML.NO_FILL.getLocalName()); 089 hashSet.add(DmlXML.SOLID_FILL.getLocalName()); 090 hashSet.add(DmlXML.GRADIENT_FILL.getLocalName()); 091 hashSet.add(DmlXML.PATTERN_FILL.getLocalName()); 092 sSubtagSequence.add(hashSet); 093 094 hashSet = new HashSet<>(2); 095 hashSet.add(DmlXML.PRESET_DASH.getLocalName()); 096 hashSet.add(DmlXML.CUSTOM_DASH.getLocalName()); 097 sSubtagSequence.add(hashSet); 098 099 hashSet = new HashSet<>(3); 100 hashSet.add(DmlXML.ROUND.getLocalName()); 101 hashSet.add(DmlXML.BEVEL.getLocalName()); 102 hashSet.add(DmlXML.MITER.getLocalName()); 103 sSubtagSequence.add(hashSet); 104 105 hashSet = new HashSet<>(1); 106 hashSet.add(DmlXML.HEAD_END.getLocalName()); 107 sSubtagSequence.add(hashSet); 108 109 hashSet = new HashSet<>(1); 110 hashSet.add(DmlXML.TAIL_END.getLocalName()); 111 sSubtagSequence.add(hashSet); 112 113 hashSet = new HashSet<>(1); 114 hashSet.add(DmlXML.EXT_LIST.getLocalName()); 115 sSubtagSequence.add(hashSet); 116 } 117 118 //########################################################################### 119 // CONSTRUCTORS 120 //########################################################################### 121 122 //--------------------------------------------------------------------------- 123 public DmlLine(OfficeOpenXmlDocument inParentDoc) 124 { 125 super(DmlXML.LINE, inParentDoc); 126 init(); 127 } 128 129 //--------------------------------------------------------------------------- 130 private void init() 131 { 132 getHeadEnd(); 133 getTailEnd(); 134 } 135 136 //########################################################################### 137 // PUBLIC METHODS 138 //########################################################################### 139 140 //--------------------------------------------------------------------------- 141 /** 142 * Specifies the fill type that should be used for the shape. 143 */ 144 public DmlLine setWidth(GfxSize inValue) 145 { 146 setAttribute(DmlXML.WIDTH_ATT, inValue.toInt(GfxUnits.emus)); 147 return this; 148 } 149 150 //--------------------------------------------------------------------------- 151 /** 152 * Specifies the fill type that should be used for the shape. 153 */ 154 public DmlLine setFill(DmlFill inValue) 155 { 156 if (mFillTag != null) 157 { 158 removeSubtag(mFillTag); 159 } 160 161 mFillTag = inValue; 162 163 addSubtag(mFillTag); 164 165 return this; 166 } 167 168 //--------------------------------------------------------------------------- 169 /** 170 * Specifies the color for a solid fill to be used for the shape. 171 */ 172 public DmlLine setFill(Color inColor) 173 { 174 if (mFillTag != null) 175 { 176 removeSubtag(mFillTag); 177 } 178 179 mFillTag = new DmlSolidFill(inColor); 180 181 addSubtag(mFillTag); 182 183 return this; 184 } 185 186 //--------------------------------------------------------------------------- 187 /** 188 * Specifies the line end cap type. 189 */ 190 public DmlLine setCapType(DmlLineEndCapType inValue) 191 { 192 setAttribute(DmlXML.CAP_ATT, inValue); 193 194 return this; 195 } 196 197 //--------------------------------------------------------------------------- 198 /** 199 * Specifies the compound line type. 200 */ 201 public DmlLine setCompoundLineType(DmlCompoundLineType inValue) 202 { 203 setAttribute(DmlXML.COMPOUND_ATT, inValue); 204 205 return this; 206 } 207 208 //--------------------------------------------------------------------------- 209 /** 210 * Specifies the pen alignment type. 211 */ 212 public DmlLine setPenAlignmentType(DmlPenAlignmentType inValue) 213 { 214 setAttribute(DmlXML.ALIGN_ATT, inValue); 215 216 return this; 217 } 218 219 //--------------------------------------------------------------------------- 220 /** 221 * Specifies the dash type. 222 */ 223 public DmlLine setDash(DmlLineDash inValue) 224 { 225 if (mLineDash != null) 226 { 227 removeSubtag(mLineDash); 228 } 229 230 addSubtag(inValue); 231 232 return this; 233 } 234 235 //--------------------------------------------------------------------------- 236 /** 237 * Specifies the dash type. 238 */ 239 public DmlLine setDash(DmlPresetLineDashType inValue) 240 { 241 return setDash(new DmlPresetLineDash(getParentDoc(), inValue)); 242 } 243 244 //--------------------------------------------------------------------------- 245 /** 246 * Specifies the line join that should be used for the shape. 247 */ 248 public DmlLine setLineJoin(DmlLineJoin inValue) 249 { 250 if (mLineJoinTag != null) 251 { 252 removeSubtag(mLineJoinTag); 253 } 254 255 mLineJoinTag = inValue; 256 257 addSubtag(mLineJoinTag); 258 259 return this; 260 } 261 262 //--------------------------------------------------------------------------- 263 /** 264 * Returns the headEnd tag if one exists or else instantiates a new one. 265 * @return the headEnd tag for this line 266 */ 267 public DmlLineHeadEnd getHeadEnd() 268 { 269 if (null == mLineHeadEnd) 270 { 271 // Check if it has been added via addSubtag()... 272 mLineHeadEnd = getOptionalSubtagByName(DmlXML.LINE_HEAD_END); 273 if (null == mLineHeadEnd) 274 { 275 mLineHeadEnd = new DmlLineHeadEnd(); 276 addSubtag(mLineHeadEnd); 277 } 278 } 279 280 return mLineHeadEnd; 281 } 282 283 //--------------------------------------------------------------------------- 284 /** 285 * Returns the tailEnd tag if one exists or else instantiates a new one. 286 * @return the tailEnd tag for this line 287 */ 288 public DmlLineTailEnd getTailEnd() 289 { 290 if (null == mLineTailEnd) 291 { 292 // Check if it has been added via addSubtag()... 293 mLineTailEnd = getOptionalSubtagByName(DmlXML.LINE_TAIL_END); 294 if (null == mLineTailEnd) 295 { 296 mLineTailEnd = new DmlLineTailEnd(); 297 addSubtag(mLineTailEnd); 298 } 299 } 300 301 return mLineTailEnd; 302 } 303 304 //--------------------------------------------------------------------------- 305 @Override 306 protected void toXML(Writer inWriter, XMLNamespaceSet inDeclaredNamespaces) 307 { 308 // Before writing to XML, be sure the subtags are in the proper sequence. 309 Collections.sort(mContentAndSubtagList, SUBTAG_SEQUENCE_COMPARATOR); 310 311 312 super.toXML(inWriter, inDeclaredNamespaces); 313 } 314 315 private class SubtagComparator implements Comparator<XMLNode> 316 { 317 //------------------------------------------------------------------------ 318 public int compare(XMLNode inNode1, XMLNode inNode2) 319 { 320 return CompareUtil.compare(getSequenceIndex(inNode1.getTagName()), getSequenceIndex(inNode2.getTagName())); 321 } 322 323 //------------------------------------------------------------------------ 324 public int getSequenceIndex(String inTagName) 325 { 326 int index = -1; 327 for (int i = 0; i < sSubtagSequence.size(); i++) 328 { 329 if (sSubtagSequence.get(i).contains(inTagName)) 330 { 331 index = i; 332 break; 333 } 334 } 335 336 return index; 337 } 338 } 339}