You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

StylesTable.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.xssf.model;
  16. import java.io.IOException;
  17. import java.io.InputStream;
  18. import java.io.OutputStream;
  19. import java.util.*;
  20. import java.util.Map.Entry;
  21. import org.apache.poi.ss.usermodel.FontFamily;
  22. import org.apache.poi.ss.usermodel.FontScheme;
  23. import org.apache.poi.ss.usermodel.BuiltinFormats;
  24. import org.apache.poi.xssf.usermodel.XSSFCellStyle;
  25. import org.apache.poi.xssf.usermodel.XSSFFont;
  26. import org.apache.poi.xssf.usermodel.extensions.XSSFCellBorder;
  27. import org.apache.poi.xssf.usermodel.extensions.XSSFCellFill;
  28. import org.apache.poi.POIXMLDocumentPart;
  29. import org.apache.xmlbeans.XmlException;
  30. import org.apache.xmlbeans.XmlOptions;
  31. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorder;
  32. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorders;
  33. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellStyleXfs;
  34. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellXfs;
  35. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDxf;
  36. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDxfs;
  37. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFill;
  38. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFills;
  39. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFont;
  40. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFonts;
  41. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTNumFmt;
  42. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTNumFmts;
  43. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTStylesheet;
  44. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXf;
  45. import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPatternType;
  46. import org.openxmlformats.schemas.spreadsheetml.x2006.main.StyleSheetDocument;
  47. import org.apache.poi.openxml4j.opc.PackagePart;
  48. import org.apache.poi.openxml4j.opc.PackageRelationship;
  49. /**
  50. * Table of styles shared across all sheets in a workbook.
  51. *
  52. * @author ugo
  53. */
  54. public class StylesTable extends POIXMLDocumentPart {
  55. private final Map<Integer, String> numberFormats = new LinkedHashMap<Integer,String>();
  56. private final List<XSSFFont> fonts = new ArrayList<XSSFFont>();
  57. private final List<XSSFCellFill> fills = new ArrayList<XSSFCellFill>();
  58. private final List<XSSFCellBorder> borders = new ArrayList<XSSFCellBorder>();
  59. private final List<CTXf> styleXfs = new ArrayList<CTXf>();
  60. private final List<CTXf> xfs = new ArrayList<CTXf>();
  61. private final List<CTDxf> dxfs = new ArrayList<CTDxf>();
  62. /**
  63. * The first style id available for use as a custom style
  64. */
  65. public static final int FIRST_CUSTOM_STYLE_ID = BuiltinFormats.FIRST_USER_DEFINED_FORMAT_INDEX + 1;
  66. private StyleSheetDocument doc;
  67. private ThemesTable theme;
  68. /**
  69. * Create a new, empty StylesTable
  70. */
  71. public StylesTable() {
  72. super();
  73. doc = StyleSheetDocument.Factory.newInstance();
  74. doc.addNewStyleSheet();
  75. // Initialization required in order to make the document readable by MSExcel
  76. initialize();
  77. }
  78. public StylesTable(PackagePart part, PackageRelationship rel) throws IOException {
  79. super(part, rel);
  80. readFrom(part.getInputStream());
  81. }
  82. public ThemesTable getTheme() {
  83. return theme;
  84. }
  85. public void setTheme(ThemesTable theme) {
  86. this.theme = theme;
  87. }
  88. /**
  89. * Read this shared styles table from an XML file.
  90. *
  91. * @param is The input stream containing the XML document.
  92. * @throws IOException if an error occurs while reading.
  93. */
  94. protected void readFrom(InputStream is) throws IOException {
  95. try {
  96. doc = StyleSheetDocument.Factory.parse(is);
  97. // Grab all the different bits we care about
  98. if(doc.getStyleSheet().getNumFmts() != null)
  99. for (CTNumFmt nfmt : doc.getStyleSheet().getNumFmts().getNumFmtArray()) {
  100. numberFormats.put((int)nfmt.getNumFmtId(), nfmt.getFormatCode());
  101. }
  102. if(doc.getStyleSheet().getFonts() != null){
  103. int idx = 0;
  104. for (CTFont font : doc.getStyleSheet().getFonts().getFontArray()) {
  105. XSSFFont f = new XSSFFont(font, idx);
  106. fonts.add(f);
  107. idx++;
  108. }
  109. }
  110. if(doc.getStyleSheet().getFills() != null)
  111. for (CTFill fill : doc.getStyleSheet().getFills().getFillArray()) {
  112. fills.add(new XSSFCellFill(fill));
  113. }
  114. if(doc.getStyleSheet().getBorders() != null)
  115. for (CTBorder border : doc.getStyleSheet().getBorders().getBorderArray()) {
  116. borders.add(new XSSFCellBorder(border));
  117. }
  118. CTCellXfs cellXfs = doc.getStyleSheet().getCellXfs();
  119. if(cellXfs != null) xfs.addAll(Arrays.asList(cellXfs.getXfArray()));
  120. CTCellStyleXfs cellStyleXfs = doc.getStyleSheet().getCellStyleXfs();
  121. if(cellStyleXfs != null) styleXfs.addAll(Arrays.asList(cellStyleXfs.getXfArray()));
  122. CTDxfs styleDxfs = doc.getStyleSheet().getDxfs();
  123. if(styleDxfs != null) dxfs.addAll(Arrays.asList(styleDxfs.getDxfArray()));
  124. } catch (XmlException e) {
  125. throw new IOException(e.getLocalizedMessage());
  126. }
  127. }
  128. // ===========================================================
  129. // Start of style related getters and setters
  130. // ===========================================================
  131. public String getNumberFormatAt(int idx) {
  132. return numberFormats.get(idx);
  133. }
  134. public int putNumberFormat(String fmt) {
  135. if (numberFormats.containsValue(fmt)) {
  136. // Find the key, and return that
  137. for(Integer key : numberFormats.keySet() ) {
  138. if(numberFormats.get(key).equals(fmt)) {
  139. return key;
  140. }
  141. }
  142. throw new IllegalStateException("Found the format, but couldn't figure out where - should never happen!");
  143. }
  144. // Find a spare key, and add that
  145. int newKey = FIRST_CUSTOM_STYLE_ID;
  146. while(numberFormats.containsKey(newKey)) {
  147. newKey++;
  148. }
  149. numberFormats.put(newKey, fmt);
  150. return newKey;
  151. }
  152. public XSSFFont getFontAt(int idx) {
  153. return fonts.get(idx);
  154. }
  155. public int putFont(XSSFFont font) {
  156. int idx = fonts.indexOf(font);
  157. if (idx != -1) {
  158. return idx;
  159. }
  160. fonts.add(font);
  161. return fonts.size() - 1;
  162. }
  163. public XSSFCellStyle getStyleAt(int idx) {
  164. int styleXfId = 0;
  165. // 0 is the empty default
  166. if(xfs.get(idx).getXfId() > 0) {
  167. styleXfId = (int) xfs.get(idx).getXfId();
  168. }
  169. return new XSSFCellStyle(idx, styleXfId, this, theme);
  170. }
  171. public int putStyle(XSSFCellStyle style) {
  172. CTXf mainXF = style.getCoreXf();
  173. if(! xfs.contains(mainXF)) {
  174. xfs.add(mainXF);
  175. }
  176. return xfs.indexOf(mainXF);
  177. }
  178. public XSSFCellBorder getBorderAt(int idx) {
  179. return borders.get(idx);
  180. }
  181. public int putBorder(XSSFCellBorder border) {
  182. int idx = borders.indexOf(border);
  183. if (idx != -1) {
  184. return idx;
  185. }
  186. borders.add(border);
  187. return borders.size() - 1;
  188. }
  189. public XSSFCellFill getFillAt(int idx) {
  190. return fills.get(idx);
  191. }
  192. public List<XSSFCellBorder> getBorders(){
  193. return borders;
  194. }
  195. public List<XSSFCellFill> getFills(){
  196. return fills;
  197. }
  198. public List<XSSFFont> getFonts(){
  199. return fonts;
  200. }
  201. public Map<Integer, String> getNumberFormats(){
  202. return numberFormats;
  203. }
  204. public int putFill(XSSFCellFill fill) {
  205. int idx = fills.indexOf(fill);
  206. if (idx != -1) {
  207. return idx;
  208. }
  209. fills.add(fill);
  210. return fills.size() - 1;
  211. }
  212. public CTXf getCellXfAt(int idx) {
  213. return xfs.get(idx);
  214. }
  215. public int putCellXf(CTXf cellXf) {
  216. xfs.add(cellXf);
  217. return xfs.size();
  218. }
  219. public CTXf getCellStyleXfAt(int idx) {
  220. return styleXfs.get(idx);
  221. }
  222. public int putCellStyleXf(CTXf cellStyleXf) {
  223. styleXfs.add(cellStyleXf);
  224. return styleXfs.size();
  225. }
  226. /**
  227. * get the size of cell styles
  228. */
  229. public int getNumCellStyles(){
  230. return styleXfs.size();
  231. }
  232. /**
  233. * For unit testing only
  234. */
  235. public int _getNumberFormatSize() {
  236. return numberFormats.size();
  237. }
  238. /**
  239. * For unit testing only
  240. */
  241. public int _getXfsSize() {
  242. return xfs.size();
  243. }
  244. /**
  245. * For unit testing only
  246. */
  247. public int _getStyleXfsSize() {
  248. return styleXfs.size();
  249. }
  250. /**
  251. * For unit testing only!
  252. */
  253. public CTStylesheet getCTStylesheet() {
  254. return doc.getStyleSheet();
  255. }
  256. /**
  257. * Write this table out as XML.
  258. *
  259. * @param out The stream to write to.
  260. * @throws IOException if an error occurs while writing.
  261. */
  262. public void writeTo(OutputStream out) throws IOException {
  263. XmlOptions options = new XmlOptions(DEFAULT_XML_OPTIONS);
  264. // Work on the current one
  265. // Need to do this, as we don't handle
  266. // all the possible entries yet
  267. // Formats
  268. CTNumFmts formats = CTNumFmts.Factory.newInstance();
  269. formats.setCount(numberFormats.size());
  270. for (Entry<Integer, String> fmt : numberFormats.entrySet()) {
  271. CTNumFmt ctFmt = formats.addNewNumFmt();
  272. ctFmt.setNumFmtId(fmt.getKey());
  273. ctFmt.setFormatCode(fmt.getValue());
  274. }
  275. doc.getStyleSheet().setNumFmts(formats);
  276. int idx;
  277. // Fonts
  278. CTFonts ctFonts = CTFonts.Factory.newInstance();
  279. ctFonts.setCount(fonts.size());
  280. CTFont[] ctfnt = new CTFont[fonts.size()];
  281. idx = 0;
  282. for(XSSFFont f : fonts) ctfnt[idx++] = f.getCTFont();
  283. ctFonts.setFontArray(ctfnt);
  284. doc.getStyleSheet().setFonts(ctFonts);
  285. // Fills
  286. CTFills ctFills = CTFills.Factory.newInstance();
  287. ctFills.setCount(fills.size());
  288. CTFill[] ctf = new CTFill[fills.size()];
  289. idx = 0;
  290. for(XSSFCellFill f : fills) ctf[idx++] = f.getCTFill();
  291. ctFills.setFillArray(ctf);
  292. doc.getStyleSheet().setFills(ctFills);
  293. // Borders
  294. CTBorders ctBorders = CTBorders.Factory.newInstance();
  295. ctBorders.setCount(borders.size());
  296. CTBorder[] ctb = new CTBorder[borders.size()];
  297. idx = 0;
  298. for(XSSFCellBorder b : borders) ctb[idx++] = b.getCTBorder();
  299. ctBorders.setBorderArray(ctb);
  300. doc.getStyleSheet().setBorders(ctBorders);
  301. // Xfs
  302. if(xfs.size() > 0) {
  303. CTCellXfs ctXfs = CTCellXfs.Factory.newInstance();
  304. ctXfs.setCount(xfs.size());
  305. ctXfs.setXfArray(
  306. xfs.toArray(new CTXf[xfs.size()])
  307. );
  308. doc.getStyleSheet().setCellXfs(ctXfs);
  309. }
  310. // Style xfs
  311. if(styleXfs.size() > 0) {
  312. CTCellStyleXfs ctSXfs = CTCellStyleXfs.Factory.newInstance();
  313. ctSXfs.setCount(styleXfs.size());
  314. ctSXfs.setXfArray(
  315. styleXfs.toArray(new CTXf[styleXfs.size()])
  316. );
  317. doc.getStyleSheet().setCellStyleXfs(ctSXfs);
  318. }
  319. // Style dxfs
  320. if(dxfs.size() > 0) {
  321. CTDxfs ctDxfs = CTDxfs.Factory.newInstance();
  322. ctDxfs.setCount(dxfs.size());
  323. ctDxfs.setDxfArray(dxfs.toArray(new CTDxf[dxfs.size()])
  324. );
  325. doc.getStyleSheet().setDxfs(ctDxfs);
  326. }
  327. // Save
  328. doc.save(out, options);
  329. }
  330. @Override
  331. protected void commit() throws IOException {
  332. PackagePart part = getPackagePart();
  333. OutputStream out = part.getOutputStream();
  334. writeTo(out);
  335. out.close();
  336. }
  337. private void initialize() {
  338. //CTFont ctFont = createDefaultFont();
  339. XSSFFont xssfFont = createDefaultFont();
  340. fonts.add(xssfFont);
  341. CTFill[] ctFill = createDefaultFills();
  342. fills.add(new XSSFCellFill(ctFill[0]));
  343. fills.add(new XSSFCellFill(ctFill[1]));
  344. CTBorder ctBorder = createDefaultBorder();
  345. borders.add(new XSSFCellBorder(ctBorder));
  346. CTXf styleXf = createDefaultXf();
  347. styleXfs.add(styleXf);
  348. CTXf xf = createDefaultXf();
  349. xf.setXfId(0);
  350. xfs.add(xf);
  351. }
  352. private static CTXf createDefaultXf() {
  353. CTXf ctXf = CTXf.Factory.newInstance();
  354. ctXf.setNumFmtId(0);
  355. ctXf.setFontId(0);
  356. ctXf.setFillId(0);
  357. ctXf.setBorderId(0);
  358. return ctXf;
  359. }
  360. private static CTBorder createDefaultBorder() {
  361. CTBorder ctBorder = CTBorder.Factory.newInstance();
  362. ctBorder.addNewBottom();
  363. ctBorder.addNewTop();
  364. ctBorder.addNewLeft();
  365. ctBorder.addNewRight();
  366. ctBorder.addNewDiagonal();
  367. return ctBorder;
  368. }
  369. private static CTFill[] createDefaultFills() {
  370. CTFill[] ctFill = new CTFill[]{CTFill.Factory.newInstance(),CTFill.Factory.newInstance()};
  371. ctFill[0].addNewPatternFill().setPatternType(STPatternType.NONE);
  372. ctFill[1].addNewPatternFill().setPatternType(STPatternType.DARK_GRAY);
  373. return ctFill;
  374. }
  375. private static XSSFFont createDefaultFont() {
  376. CTFont ctFont = CTFont.Factory.newInstance();
  377. XSSFFont xssfFont=new XSSFFont(ctFont, 0);
  378. xssfFont.setFontHeightInPoints(XSSFFont.DEFAULT_FONT_SIZE);
  379. xssfFont.setColor(XSSFFont.DEFAULT_FONT_COLOR);//setTheme
  380. xssfFont.setFontName(XSSFFont.DEFAULT_FONT_NAME);
  381. xssfFont.setFamily(FontFamily.SWISS);
  382. xssfFont.setScheme(FontScheme.MINOR);
  383. return xssfFont;
  384. }
  385. protected CTDxf getDxf(int idx) {
  386. if (dxfs.size()==0) {
  387. return CTDxf.Factory.newInstance();
  388. }
  389. return dxfs.get(idx);
  390. }
  391. protected int putDxf(CTDxf dxf) {
  392. this.dxfs.add(dxf);
  393. return this.dxfs.size();
  394. }
  395. public XSSFCellStyle createCellStyle() {
  396. CTXf xf = CTXf.Factory.newInstance();
  397. xf.setNumFmtId(0);
  398. xf.setFontId(0);
  399. xf.setFillId(0);
  400. xf.setBorderId(0);
  401. xf.setXfId(0);
  402. int xfSize = styleXfs.size();
  403. int indexXf = putCellXf(xf);
  404. return new XSSFCellStyle(indexXf - 1, xfSize - 1, this, theme);
  405. }
  406. /**
  407. * Finds a font that matches the one with the supplied attributes
  408. */
  409. public XSSFFont findFont(short boldWeight, short color, short fontHeight, String name, boolean italic, boolean strikeout, short typeOffset, byte underline) {
  410. for (XSSFFont font : fonts) {
  411. if ( (font.getBoldweight() == boldWeight)
  412. && font.getColor() == color
  413. && font.getFontHeight() == fontHeight
  414. && font.getFontName().equals(name)
  415. && font.getItalic() == italic
  416. && font.getStrikeout() == strikeout
  417. && font.getTypeOffset() == typeOffset
  418. && font.getUnderline() == underline)
  419. {
  420. return font;
  421. }
  422. }
  423. return null;
  424. }
  425. }