001package com.hfg.xml.msofficexml.docx.wordprocessingml.style; 002 003import com.hfg.css.*; 004import com.hfg.graphics.units.GfxSize; 005import com.hfg.graphics.units.GfxUnits; 006import com.hfg.graphics.units.Pixels; 007import com.hfg.graphics.units.Points; 008import com.hfg.html.attribute.HTMLColor; 009import com.hfg.graphics.ColorUtil; 010import com.hfg.util.StringUtil; 011import com.hfg.xml.XMLName; 012import com.hfg.xml.XMLTag; 013import com.hfg.xml.msofficexml.docx.DocxException; 014import com.hfg.xml.msofficexml.docx.wordprocessingml.WmlXML; 015 016import java.awt.Color; 017import java.util.ArrayList; 018import java.util.List; 019import java.util.regex.Matcher; 020import java.util.regex.Pattern; 021 022 023// Section 17.3.4 (pg. 351) of Ecma Office Open XML Part 1 024 025public class WmlTableCellBorder 026{ 027 private Position mPosition; 028 private WmlLineBorderStyle mStyle; 029 private Integer mSize; 030 private Integer mSpace; 031 private Color mColor; 032 private Boolean mAutoColor; 033 034 public static enum Position { 035 top, 036 bottom, 037 left, 038 right, 039 start, 040 end, 041 insideH, 042 insideV, 043 tl2br, // Top left to bottom right (diagonal) 044 tr2bl // Top right to bottom left (diagonal) 045 } 046 047 private static final Pattern sizePattern = Pattern.compile("(\\d+)(px)?"); 048 049 //--------------------------------------------------------------------------- 050 public WmlTableCellBorder() 051 { 052 init(); 053 } 054 055 //--------------------------------------------------------------------------- 056 public WmlTableCellBorder(CSSDeclaration inCSSDeclaration) 057 { 058 this(); 059 060 if (inCSSDeclaration.getProperty().equals(CSSProperty.border)) 061 { 062 // Don't set a value for position. It will be expanded into top, bottom, left, right 063 } 064 else if (inCSSDeclaration.getProperty().equals(CSSProperty.border_top)) 065 { 066 setPosition(Position.top); 067 } 068 else if (inCSSDeclaration.getProperty().equals(CSSProperty.border_bottom)) 069 { 070 setPosition(Position.bottom); 071 } 072 else if (inCSSDeclaration.getProperty().equals(CSSProperty.border_left)) 073 { 074 setPosition(Position.left); 075 } 076 else if (inCSSDeclaration.getProperty().equals(CSSProperty.border_right)) 077 { 078 setPosition(Position.right); 079 } 080 else 081 { 082 throw new CSSException("The CSS declaration " + StringUtil.singleQuote(inCSSDeclaration.toString()) 083 + " cannot be used to construct a " + this.getClass().getSimpleName() + " object!"); 084 } 085 086 // Ex: '1px solid blue' 087 088 boolean findSize = true; 089 boolean findStyle = true; 090 boolean findColor = true; 091 092 String[] valuePieces = inCSSDeclaration.getValue().split("\\s+"); 093 for (int i = 0; i < valuePieces.length; i++) 094 { 095 String token = valuePieces[i]; 096 097 if (findSize) 098 { 099 findSize = false; // Size should always come first 100 Matcher m = sizePattern.matcher(token); 101 if (m.matches()) 102 { 103 setSize(new Pixels(Integer.parseInt(m.group(1)))); 104 continue; 105 } 106 } 107 108 if (findStyle) 109 { 110 findStyle = false; 111 CSSBorderStyle style = CSSBorderStyle.valueOf(token); 112 if (style != null) 113 { 114 WmlLineBorderStyle wmlStyle = WmlLineBorderStyle.valueOf(style); 115 if (wmlStyle != null) 116 { 117 setStyle(wmlStyle); 118 } 119 continue; 120 } 121 122 } 123 124 if (findColor) 125 { 126 findColor = false; 127 if (token.equals("transparent")) 128 { 129 // I would set this to null but that seems to result in a black border 130 setColor(Color.WHITE); 131 } 132 else 133 { 134 HTMLColor color = HTMLColor.valueOf(token); 135 if (color != null) 136 { 137 setColor(color); 138 } 139 } 140 } 141 } 142 } 143 144 //--------------------------------------------------------------------------- 145 private void init() 146 { 147 // Set some default values 148 setStyle(WmlLineBorderStyle.single); 149 setSize(new Points(1)); 150 setColor(Color.black); 151 } 152 153 //--------------------------------------------------------------------------- 154 /** 155 * If a position is not specified via this method, a complete border set (top, end, bottom, and start) will be generated. 156 * @param inValue the border position 157 */ 158 public WmlTableCellBorder setPosition(Position inValue) 159 { 160 mPosition = inValue; 161 return this; 162 } 163 164 //--------------------------------------------------------------------------- 165 /** 166 * Specifies the width of the border in eighths of a point, with a minimum value 167 * of 1/4 of a point and a maximum value of twelve points. 168 * @param inValue width of the border 169 */ 170 public WmlTableCellBorder setSize(GfxSize inValue) 171 { 172 int eighthOfAPointValue = (int) (inValue.to(GfxUnits.points) * 8); 173 if (eighthOfAPointValue < 2) 174 { 175 throw new DocxException("The border size must be between 1/4 to 12 points!"); 176 } 177 178 mSize = eighthOfAPointValue; 179 return this; 180 } 181 182 //--------------------------------------------------------------------------- 183 public WmlTableCellBorder setStyle(WmlLineBorderStyle inValue) 184 { 185 mStyle = inValue; 186 return this; 187 } 188 189 //--------------------------------------------------------------------------- 190 public WmlTableCellBorder setColor(Color inValue) 191 { 192 mColor = inValue; 193 return this; 194 } 195 196 //--------------------------------------------------------------------------- 197 public WmlTableCellBorder setAutoColor(boolean inValue) 198 { 199 mAutoColor = inValue; 200 return this; 201 } 202 203 //--------------------------------------------------------------------------- 204 /** 205 * The padding (in points) between the text and the border. 206 * @param inValue padding (in points) 207 */ 208 public WmlTableCellBorder setSpace(int inValue) 209 { 210 mSpace = inValue; 211 return this; 212 } 213 214 //--------------------------------------------------------------------------- 215 public List<XMLTag> toXMLTags() 216 { 217 List<XMLTag> tags = new ArrayList<XMLTag>(4); 218 219 List<Position> positions = new ArrayList<Position>(4); 220 if (mPosition != null) 221 { 222 positions.add(mPosition); 223 } 224 else 225 { 226 positions.add(Position.top); 227 // The spec says to use start & end but those seem to cause errors 228// positions.add(Position.start); 229// positions.add(Position.end); 230 positions.add(Position.left); 231 positions.add(Position.right); 232 positions.add(Position.bottom); 233 } 234 235 for (Position position : positions) 236 { 237 XMLTag tag = new XMLTag(new XMLName(position.name(), WmlXML.WORDPROCESSINGML_NAMESPACE)); 238 239 if (mSize != null) 240 { 241 tag.setAttribute(WmlXML.SIZE_ATT, mSize); 242 } 243 244 if (mStyle != null) 245 { 246 tag.setAttribute(WmlXML.VALUE_ATT, mStyle.name()); 247 } 248 249 if (mSpace != null) 250 { 251 tag.setAttribute(WmlXML.SPACE_ATT, mSpace); 252 } 253 254 if (mColor != null) 255 { 256 tag.setAttribute(WmlXML.COLOR_ATT, ColorUtil.colorToHex(mColor)); 257 } 258 else if (mAutoColor != null 259 && mAutoColor) 260 { 261 tag.setAttribute(WmlXML.COLOR_ATT, "auto"); 262 } 263 264 tags.add(tag); 265 } 266 267 return tags; 268 } 269}