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 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  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 static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
  17. import java.io.IOException;
  18. import java.io.InputStream;
  19. import java.io.OutputStream;
  20. import java.util.ArrayList;
  21. import java.util.Arrays;
  22. import java.util.Collections;
  23. import java.util.HashMap;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Map.Entry;
  27. import java.util.SortedMap;
  28. import java.util.TreeMap;
  29. import org.apache.poi.POIXMLDocumentPart;
  30. import org.apache.poi.openxml4j.opc.PackagePart;
  31. import org.apache.poi.ss.SpreadsheetVersion;
  32. import org.apache.poi.ss.usermodel.BuiltinFormats;
  33. import org.apache.poi.ss.usermodel.FontFamily;
  34. import org.apache.poi.ss.usermodel.FontScheme;
  35. import org.apache.poi.ss.usermodel.TableStyle;
  36. import org.apache.poi.util.Internal;
  37. import org.apache.poi.xssf.usermodel.XSSFBuiltinTableStyle;
  38. import org.apache.poi.xssf.usermodel.XSSFCellStyle;
  39. import org.apache.poi.xssf.usermodel.XSSFFactory;
  40. import org.apache.poi.xssf.usermodel.XSSFFont;
  41. import org.apache.poi.xssf.usermodel.XSSFRelation;
  42. import org.apache.poi.xssf.usermodel.XSSFTableStyle;
  43. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  44. import org.apache.poi.xssf.usermodel.extensions.XSSFCellBorder;
  45. import org.apache.poi.xssf.usermodel.extensions.XSSFCellFill;
  46. import org.apache.xmlbeans.XmlException;
  47. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorder;
  48. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorders;
  49. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellStyleXfs;
  50. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellXfs;
  51. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDxf;
  52. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDxfs;
  53. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFill;
  54. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFills;
  55. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFont;
  56. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFonts;
  57. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTNumFmt;
  58. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTNumFmts;
  59. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTStylesheet;
  60. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyle;
  61. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyles;
  62. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXf;
  63. import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPatternType;
  64. import org.openxmlformats.schemas.spreadsheetml.x2006.main.StyleSheetDocument;
  65. /**
  66. * Table of styles shared across all sheets in a workbook.
  67. */
  68. public class StylesTable extends POIXMLDocumentPart {
  69. private final SortedMap<Short, String> numberFormats = new TreeMap<Short,String>();
  70. private final List<XSSFFont> fonts = new ArrayList<XSSFFont>();
  71. private final List<XSSFCellFill> fills = new ArrayList<XSSFCellFill>();
  72. private final List<XSSFCellBorder> borders = new ArrayList<XSSFCellBorder>();
  73. private final List<CTXf> styleXfs = new ArrayList<CTXf>();
  74. private final List<CTXf> xfs = new ArrayList<CTXf>();
  75. private final List<CTDxf> dxfs = new ArrayList<CTDxf>();
  76. private final Map<String, TableStyle> tableStyles = new HashMap<String, TableStyle>();
  77. /**
  78. * The first style id available for use as a custom style
  79. */
  80. public static final int FIRST_CUSTOM_STYLE_ID = BuiltinFormats.FIRST_USER_DEFINED_FORMAT_INDEX + 1;
  81. // Is this right? Number formats (XSSFDataFormat) and cell styles (XSSFCellStyle) are different. What's up with the plus 1?
  82. private static final int MAXIMUM_STYLE_ID = SpreadsheetVersion.EXCEL2007.getMaxCellStyles();
  83. private static final short FIRST_USER_DEFINED_NUMBER_FORMAT_ID = BuiltinFormats.FIRST_USER_DEFINED_FORMAT_INDEX;
  84. /**
  85. * Depending on the version of Excel, the maximum number of number formats in a workbook is between 200 and 250
  86. * See https://support.office.com/en-us/article/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3
  87. * POI defaults this limit to 250, but can be increased or decreased on a per-StylesTable basis with
  88. * {@link #setMaxNumberOfDataFormats(int)} if needed.
  89. */
  90. private int MAXIMUM_NUMBER_OF_DATA_FORMATS = 250;
  91. /**
  92. * Changes the maximum number of data formats that may be in a style table
  93. *
  94. * @param num the upper limit on number of data formats in the styles table when adding new data formats
  95. * @throws IllegalArgumentException if <code>num</code> < 0
  96. * @throws IllegalStateException if <code>num</code> < current number of data formats in the style table.
  97. * Data formats must be explicitly removed before the limit can be decreased.
  98. */
  99. public void setMaxNumberOfDataFormats(int num) {
  100. if (num < getNumDataFormats()) {
  101. if (num < 0) {
  102. throw new IllegalArgumentException("Maximum Number of Data Formats must be greater than or equal to 0");
  103. } else {
  104. throw new IllegalStateException("Cannot set the maximum number of data formats less than the current quantity." +
  105. "Data formats must be explicitly removed (via StylesTable.removeNumberFormat) before the limit can be decreased.");
  106. }
  107. }
  108. MAXIMUM_NUMBER_OF_DATA_FORMATS = num;
  109. }
  110. /**
  111. * Get the upper limit on the number of data formats that has been set for the style table.
  112. * To get the current number of data formats in use, use {@link #getNumDataFormats()}.
  113. *
  114. * @return the maximum number of data formats allowed in the workbook
  115. */
  116. public int getMaxNumberOfDataFormats() {
  117. return MAXIMUM_NUMBER_OF_DATA_FORMATS;
  118. }
  119. private StyleSheetDocument doc;
  120. private XSSFWorkbook workbook;
  121. private ThemesTable theme;
  122. /**
  123. * Create a new, empty StylesTable
  124. */
  125. public StylesTable() {
  126. super();
  127. doc = StyleSheetDocument.Factory.newInstance();
  128. doc.addNewStyleSheet();
  129. // Initialization required in order to make the document readable by MSExcel
  130. initialize();
  131. }
  132. /**
  133. * @since POI 3.14-Beta1
  134. */
  135. public StylesTable(PackagePart part) throws IOException {
  136. super(part);
  137. readFrom(part.getInputStream());
  138. }
  139. public void setWorkbook(XSSFWorkbook wb) {
  140. this.workbook = wb;
  141. }
  142. /**
  143. * Get the current Workbook's theme table, or null if the
  144. * Workbook lacks any themes.
  145. * <p>Use {@link #ensureThemesTable()} to have a themes table
  146. * created if needed
  147. */
  148. public ThemesTable getTheme() {
  149. return theme;
  150. }
  151. public void setTheme(ThemesTable theme) {
  152. this.theme = theme;
  153. // Pass the themes table along to things which need to
  154. // know about it, but have already been created by now
  155. for(XSSFFont font : fonts) {
  156. font.setThemesTable(theme);
  157. }
  158. for(XSSFCellBorder border : borders) {
  159. border.setThemesTable(theme);
  160. }
  161. }
  162. /**
  163. * If there isn't currently a {@link ThemesTable} for the
  164. * current Workbook, then creates one and sets it up.
  165. * After this, calls to {@link #getTheme()} won't give null
  166. */
  167. public void ensureThemesTable() {
  168. if (theme != null) return;
  169. theme = (ThemesTable)workbook.createRelationship(XSSFRelation.THEME, XSSFFactory.getInstance());
  170. }
  171. /**
  172. * Read this shared styles table from an XML file.
  173. *
  174. * @param is The input stream containing the XML document.
  175. * @throws IOException if an error occurs while reading.
  176. */
  177. public void readFrom(InputStream is) throws IOException {
  178. try {
  179. doc = StyleSheetDocument.Factory.parse(is, DEFAULT_XML_OPTIONS);
  180. CTStylesheet styleSheet = doc.getStyleSheet();
  181. // Grab all the different bits we care about
  182. CTNumFmts ctfmts = styleSheet.getNumFmts();
  183. if( ctfmts != null){
  184. for (CTNumFmt nfmt : ctfmts.getNumFmtArray()) {
  185. short formatId = (short)nfmt.getNumFmtId();
  186. numberFormats.put(formatId, nfmt.getFormatCode());
  187. }
  188. }
  189. CTFonts ctfonts = styleSheet.getFonts();
  190. if(ctfonts != null){
  191. int idx = 0;
  192. for (CTFont font : ctfonts.getFontArray()) {
  193. // Create the font and save it. Themes Table supplied later
  194. XSSFFont f = new XSSFFont(font, idx);
  195. fonts.add(f);
  196. idx++;
  197. }
  198. }
  199. CTFills ctfills = styleSheet.getFills();
  200. if(ctfills != null){
  201. for (CTFill fill : ctfills.getFillArray()) {
  202. fills.add(new XSSFCellFill(fill));
  203. }
  204. }
  205. CTBorders ctborders = styleSheet.getBorders();
  206. if(ctborders != null) {
  207. for (CTBorder border : ctborders.getBorderArray()) {
  208. borders.add(new XSSFCellBorder(border));
  209. }
  210. }
  211. CTCellXfs cellXfs = styleSheet.getCellXfs();
  212. if(cellXfs != null) xfs.addAll(Arrays.asList(cellXfs.getXfArray()));
  213. CTCellStyleXfs cellStyleXfs = styleSheet.getCellStyleXfs();
  214. if(cellStyleXfs != null) styleXfs.addAll(Arrays.asList(cellStyleXfs.getXfArray()));
  215. CTDxfs styleDxfs = styleSheet.getDxfs();
  216. if(styleDxfs != null) dxfs.addAll(Arrays.asList(styleDxfs.getDxfArray()));
  217. CTTableStyles ctTableStyles = styleSheet.getTableStyles();
  218. if (ctTableStyles != null) {
  219. for (CTTableStyle style : Arrays.asList(ctTableStyles.getTableStyleArray())) {
  220. tableStyles.put(style.getName(), new XSSFTableStyle(styleDxfs, style));
  221. }
  222. }
  223. } catch (XmlException e) {
  224. throw new IOException(e.getLocalizedMessage());
  225. }
  226. }
  227. // ===========================================================
  228. // Start of style related getters and setters
  229. // ===========================================================
  230. /**
  231. * Get number format string given its id
  232. *
  233. * @param fmtId number format id
  234. * @return number format code
  235. */
  236. public String getNumberFormatAt(short fmtId) {
  237. return numberFormats.get(fmtId);
  238. }
  239. private short getNumberFormatId(String fmt) {
  240. // Find the key, and return that
  241. for (Entry<Short,String> numFmt : numberFormats.entrySet()) {
  242. if(numFmt.getValue().equals(fmt)) {
  243. return numFmt.getKey();
  244. }
  245. }
  246. throw new IllegalStateException("Number format not in style table: " + fmt);
  247. }
  248. /**
  249. * Puts <code>fmt</code> in the numberFormats map if the format is not
  250. * already in the the number format style table.
  251. * Does nothing if <code>fmt</code> is already in number format style table.
  252. *
  253. * @param fmt the number format to add to number format style table
  254. * @return the index of <code>fmt</code> in the number format style table
  255. * @throws IllegalStateException if adding the number format to the styles table
  256. * would exceed the {@link #MAXIMUM_NUMBER_OF_DATA_FORMATS} allowed.
  257. */
  258. public int putNumberFormat(String fmt) {
  259. // Check if number format already exists
  260. if (numberFormats.containsValue(fmt)) {
  261. try {
  262. return getNumberFormatId(fmt);
  263. } catch (final IllegalStateException e) {
  264. throw new IllegalStateException("Found the format, but couldn't figure out where - should never happen!");
  265. }
  266. }
  267. if (numberFormats.size() >= MAXIMUM_NUMBER_OF_DATA_FORMATS) {
  268. throw new IllegalStateException("The maximum number of Data Formats was exceeded. " +
  269. "You can define up to " + MAXIMUM_NUMBER_OF_DATA_FORMATS + " formats in a .xlsx Workbook.");
  270. }
  271. // Find a spare key, and add that
  272. final short formatIndex;
  273. if (numberFormats.isEmpty()) {
  274. formatIndex = FIRST_USER_DEFINED_NUMBER_FORMAT_ID;
  275. }
  276. else {
  277. // get next-available numberFormat index.
  278. // Assumption: gaps in number format ids are acceptable
  279. // to catch arithmetic overflow, nextKey's data type
  280. // must match numberFormat's key data type
  281. short nextKey = (short) (numberFormats.lastKey() + 1);
  282. if (nextKey < 0) {
  283. throw new IllegalStateException(
  284. "Cowardly avoiding creating a number format with a negative id." +
  285. "This is probably due to arithmetic overflow.");
  286. }
  287. formatIndex = (short) Math.max(nextKey, FIRST_USER_DEFINED_NUMBER_FORMAT_ID);
  288. }
  289. numberFormats.put(formatIndex, fmt);
  290. return formatIndex;
  291. }
  292. /**
  293. * Add a number format with a specific ID into the numberFormats map.
  294. * If a format with the same ID already exists, overwrite the format code
  295. * with <code>fmt</code>
  296. * This may be used to override built-in number formats.
  297. *
  298. * @param index the number format ID
  299. * @param fmt the number format code
  300. */
  301. public void putNumberFormat(short index, String fmt) {
  302. numberFormats.put(index, fmt);
  303. }
  304. /**
  305. * Remove a number format from the style table if it exists.
  306. * All cell styles with this number format will be modified to use the default number format.
  307. *
  308. * @param index the number format id to remove
  309. * @return true if the number format was removed
  310. */
  311. public boolean removeNumberFormat(short index) {
  312. String fmt = numberFormats.remove(index);
  313. boolean removed = (fmt != null);
  314. if (removed) {
  315. for (final CTXf style : xfs) {
  316. if (style.isSetNumFmtId() && style.getNumFmtId() == index) {
  317. style.unsetApplyNumberFormat();
  318. style.unsetNumFmtId();
  319. }
  320. }
  321. }
  322. return removed;
  323. }
  324. /**
  325. * Remove a number format from the style table if it exists
  326. * All cell styles with this number format will be modified to use the default number format
  327. *
  328. * @param fmt the number format to remove
  329. * @return true if the number format was removed
  330. */
  331. public boolean removeNumberFormat(String fmt) {
  332. short id = getNumberFormatId(fmt);
  333. return removeNumberFormat(id);
  334. }
  335. public XSSFFont getFontAt(int idx) {
  336. return fonts.get(idx);
  337. }
  338. /**
  339. * Records the given font in the font table.
  340. * Will re-use an existing font index if this
  341. * font matches another, EXCEPT if forced
  342. * registration is requested.
  343. * This allows people to create several fonts
  344. * then customise them later.
  345. * Note - End Users probably want to call
  346. * {@link XSSFFont#registerTo(StylesTable)}
  347. */
  348. public int putFont(XSSFFont font, boolean forceRegistration) {
  349. int idx = -1;
  350. if(!forceRegistration) {
  351. idx = fonts.indexOf(font);
  352. }
  353. if (idx != -1) {
  354. return idx;
  355. }
  356. idx = fonts.size();
  357. fonts.add(font);
  358. return idx;
  359. }
  360. public int putFont(XSSFFont font) {
  361. return putFont(font, false);
  362. }
  363. /**
  364. *
  365. * @param idx style index
  366. * @return XSSFCellStyle or null if idx is out of bounds for xfs array
  367. */
  368. public XSSFCellStyle getStyleAt(int idx) {
  369. int styleXfId = 0;
  370. if (idx < 0 || idx >= xfs.size()) {
  371. //BUG-60343
  372. return null;
  373. }
  374. // 0 is the empty default
  375. if(xfs.get(idx).getXfId() > 0) {
  376. styleXfId = (int) xfs.get(idx).getXfId();
  377. }
  378. return new XSSFCellStyle(idx, styleXfId, this, theme);
  379. }
  380. public int putStyle(XSSFCellStyle style) {
  381. CTXf mainXF = style.getCoreXf();
  382. if(! xfs.contains(mainXF)) {
  383. xfs.add(mainXF);
  384. }
  385. return xfs.indexOf(mainXF);
  386. }
  387. public XSSFCellBorder getBorderAt(int idx) {
  388. return borders.get(idx);
  389. }
  390. /**
  391. * Adds a border to the border style table if it isn't already in the style table
  392. * Does nothing if border is already in borders style table
  393. *
  394. * @param border border to add
  395. * @return the index of the added border
  396. */
  397. public int putBorder(XSSFCellBorder border) {
  398. int idx = borders.indexOf(border);
  399. if (idx != -1) {
  400. return idx;
  401. }
  402. borders.add(border);
  403. border.setThemesTable(theme);
  404. return borders.size() - 1;
  405. }
  406. public XSSFCellFill getFillAt(int idx) {
  407. return fills.get(idx);
  408. }
  409. public List<XSSFCellBorder> getBorders(){
  410. return Collections.unmodifiableList(borders);
  411. }
  412. public List<XSSFCellFill> getFills(){
  413. return Collections.unmodifiableList(fills);
  414. }
  415. public List<XSSFFont> getFonts(){
  416. return Collections.unmodifiableList(fonts);
  417. }
  418. public Map<Short, String> getNumberFormats(){
  419. return Collections.unmodifiableMap(numberFormats);
  420. }
  421. /**
  422. * Adds a fill to the fill style table if it isn't already in the style table
  423. * Does nothing if fill is already in fill style table
  424. *
  425. * @param fill fill to add
  426. * @return the index of the added fill
  427. */
  428. public int putFill(XSSFCellFill fill) {
  429. int idx = fills.indexOf(fill);
  430. if (idx != -1) {
  431. return idx;
  432. }
  433. fills.add(fill);
  434. return fills.size() - 1;
  435. }
  436. @Internal
  437. public CTXf getCellXfAt(int idx) {
  438. return xfs.get(idx);
  439. }
  440. /**
  441. * Adds a cell to the styles table.
  442. * Does not check for duplicates.
  443. *
  444. * @param cellXf the cell to add to the styles table
  445. * @return the added cell ID in the style table
  446. */
  447. @Internal
  448. public int putCellXf(CTXf cellXf) {
  449. xfs.add(cellXf);
  450. return xfs.size();
  451. }
  452. @Internal
  453. public void replaceCellXfAt(int idx, CTXf cellXf) {
  454. xfs.set(idx, cellXf);
  455. }
  456. @Internal
  457. public CTXf getCellStyleXfAt(int idx) {
  458. try {
  459. return styleXfs.get(idx);
  460. }
  461. catch (final IndexOutOfBoundsException e) {
  462. return null;
  463. }
  464. }
  465. /**
  466. * Adds a cell style to the styles table.
  467. * Does not check for duplicates.
  468. *
  469. * @param cellStyleXf the cell style to add to the styles table
  470. * @return the cell style ID in the style table
  471. */
  472. @Internal
  473. public int putCellStyleXf(CTXf cellStyleXf) {
  474. styleXfs.add(cellStyleXf);
  475. // TODO: check for duplicate
  476. return styleXfs.size();
  477. }
  478. @Internal
  479. protected void replaceCellStyleXfAt(int idx, CTXf cellStyleXf) {
  480. styleXfs.set(idx, cellStyleXf);
  481. }
  482. /**
  483. * get the size of cell styles
  484. */
  485. public int getNumCellStyles(){
  486. // Each cell style has a unique xfs entry
  487. // Several might share the same styleXfs entry
  488. return xfs.size();
  489. }
  490. /**
  491. * @return number of data formats in the styles table
  492. */
  493. public int getNumDataFormats() {
  494. return numberFormats.size();
  495. }
  496. /**
  497. * For unit testing only
  498. * @deprecated POI 3.14 beta 2. Use {@link #getNumDataFormats()} instead.
  499. */
  500. @Internal
  501. public int _getNumberFormatSize() {
  502. return getNumDataFormats();
  503. }
  504. /**
  505. * For unit testing only
  506. */
  507. @Internal
  508. /*package*/ int _getXfsSize() {
  509. return xfs.size();
  510. }
  511. /**
  512. * For unit testing only
  513. */
  514. @Internal
  515. public int _getStyleXfsSize() {
  516. return styleXfs.size();
  517. }
  518. /**
  519. * For unit testing only!
  520. */
  521. @Internal
  522. public CTStylesheet getCTStylesheet() {
  523. return doc.getStyleSheet();
  524. }
  525. @Internal
  526. public int _getDXfsSize() {
  527. return dxfs.size();
  528. }
  529. /**
  530. * Write this table out as XML.
  531. *
  532. * @param out The stream to write to.
  533. * @throws IOException if an error occurs while writing.
  534. */
  535. public void writeTo(OutputStream out) throws IOException {
  536. // Work on the current one
  537. // Need to do this, as we don't handle
  538. // all the possible entries yet
  539. CTStylesheet styleSheet = doc.getStyleSheet();
  540. // Formats
  541. CTNumFmts formats = CTNumFmts.Factory.newInstance();
  542. formats.setCount(numberFormats.size());
  543. for (final Entry<Short, String> entry : numberFormats.entrySet()) {
  544. CTNumFmt ctFmt = formats.addNewNumFmt();
  545. ctFmt.setNumFmtId(entry.getKey());
  546. ctFmt.setFormatCode(entry.getValue());
  547. }
  548. styleSheet.setNumFmts(formats);
  549. int idx;
  550. // Fonts
  551. CTFonts ctFonts = styleSheet.getFonts();
  552. if (ctFonts == null) {
  553. ctFonts = CTFonts.Factory.newInstance();
  554. }
  555. ctFonts.setCount(fonts.size());
  556. CTFont[] ctfnt = new CTFont[fonts.size()];
  557. idx = 0;
  558. for(XSSFFont f : fonts) ctfnt[idx++] = f.getCTFont();
  559. ctFonts.setFontArray(ctfnt);
  560. styleSheet.setFonts(ctFonts);
  561. // Fills
  562. CTFills ctFills = styleSheet.getFills();
  563. if (ctFills == null) {
  564. ctFills = CTFills.Factory.newInstance();
  565. }
  566. ctFills.setCount(fills.size());
  567. CTFill[] ctf = new CTFill[fills.size()];
  568. idx = 0;
  569. for(XSSFCellFill f : fills) ctf[idx++] = f.getCTFill();
  570. ctFills.setFillArray(ctf);
  571. styleSheet.setFills(ctFills);
  572. // Borders
  573. CTBorders ctBorders = styleSheet.getBorders();
  574. if (ctBorders == null) {
  575. ctBorders = CTBorders.Factory.newInstance();
  576. }
  577. ctBorders.setCount(borders.size());
  578. CTBorder[] ctb = new CTBorder[borders.size()];
  579. idx = 0;
  580. for(XSSFCellBorder b : borders) ctb[idx++] = b.getCTBorder();
  581. ctBorders.setBorderArray(ctb);
  582. styleSheet.setBorders(ctBorders);
  583. // Xfs
  584. if(xfs.size() > 0) {
  585. CTCellXfs ctXfs = styleSheet.getCellXfs();
  586. if (ctXfs == null) {
  587. ctXfs = CTCellXfs.Factory.newInstance();
  588. }
  589. ctXfs.setCount(xfs.size());
  590. ctXfs.setXfArray(
  591. xfs.toArray(new CTXf[xfs.size()])
  592. );
  593. styleSheet.setCellXfs(ctXfs);
  594. }
  595. // Style xfs
  596. if(styleXfs.size() > 0) {
  597. CTCellStyleXfs ctSXfs = styleSheet.getCellStyleXfs();
  598. if (ctSXfs == null) {
  599. ctSXfs = CTCellStyleXfs.Factory.newInstance();
  600. }
  601. ctSXfs.setCount(styleXfs.size());
  602. ctSXfs.setXfArray(
  603. styleXfs.toArray(new CTXf[styleXfs.size()])
  604. );
  605. styleSheet.setCellStyleXfs(ctSXfs);
  606. }
  607. // Style dxfs
  608. if(dxfs.size() > 0) {
  609. CTDxfs ctDxfs = styleSheet.getDxfs();
  610. if (ctDxfs == null) {
  611. ctDxfs = CTDxfs.Factory.newInstance();
  612. }
  613. ctDxfs.setCount(dxfs.size());
  614. ctDxfs.setDxfArray(dxfs.toArray(new CTDxf[dxfs.size()]));
  615. styleSheet.setDxfs(ctDxfs);
  616. }
  617. // Save
  618. doc.save(out, DEFAULT_XML_OPTIONS);
  619. }
  620. @Override
  621. protected void commit() throws IOException {
  622. PackagePart part = getPackagePart();
  623. OutputStream out = part.getOutputStream();
  624. writeTo(out);
  625. out.close();
  626. }
  627. private void initialize() {
  628. //CTFont ctFont = createDefaultFont();
  629. XSSFFont xssfFont = createDefaultFont();
  630. fonts.add(xssfFont);
  631. CTFill[] ctFill = createDefaultFills();
  632. fills.add(new XSSFCellFill(ctFill[0]));
  633. fills.add(new XSSFCellFill(ctFill[1]));
  634. CTBorder ctBorder = createDefaultBorder();
  635. borders.add(new XSSFCellBorder(ctBorder));
  636. CTXf styleXf = createDefaultXf();
  637. styleXfs.add(styleXf);
  638. CTXf xf = createDefaultXf();
  639. xf.setXfId(0);
  640. xfs.add(xf);
  641. }
  642. private static CTXf createDefaultXf() {
  643. CTXf ctXf = CTXf.Factory.newInstance();
  644. ctXf.setNumFmtId(0);
  645. ctXf.setFontId(0);
  646. ctXf.setFillId(0);
  647. ctXf.setBorderId(0);
  648. return ctXf;
  649. }
  650. private static CTBorder createDefaultBorder() {
  651. CTBorder ctBorder = CTBorder.Factory.newInstance();
  652. ctBorder.addNewBottom();
  653. ctBorder.addNewTop();
  654. ctBorder.addNewLeft();
  655. ctBorder.addNewRight();
  656. ctBorder.addNewDiagonal();
  657. return ctBorder;
  658. }
  659. private static CTFill[] createDefaultFills() {
  660. CTFill[] ctFill = new CTFill[]{CTFill.Factory.newInstance(),CTFill.Factory.newInstance()};
  661. ctFill[0].addNewPatternFill().setPatternType(STPatternType.NONE);
  662. ctFill[1].addNewPatternFill().setPatternType(STPatternType.DARK_GRAY);
  663. return ctFill;
  664. }
  665. private static XSSFFont createDefaultFont() {
  666. CTFont ctFont = CTFont.Factory.newInstance();
  667. XSSFFont xssfFont=new XSSFFont(ctFont, 0);
  668. xssfFont.setFontHeightInPoints(XSSFFont.DEFAULT_FONT_SIZE);
  669. xssfFont.setColor(XSSFFont.DEFAULT_FONT_COLOR);//setTheme
  670. xssfFont.setFontName(XSSFFont.DEFAULT_FONT_NAME);
  671. xssfFont.setFamily(FontFamily.SWISS);
  672. xssfFont.setScheme(FontScheme.MINOR);
  673. return xssfFont;
  674. }
  675. @Internal
  676. public CTDxf getDxfAt(int idx) {
  677. return dxfs.get(idx);
  678. }
  679. /**
  680. * Adds a Dxf to the style table
  681. * Does not check for duplicates.
  682. *
  683. * @param dxf the Dxf to add
  684. * @return added dxf ID in the style table
  685. */
  686. @Internal
  687. public int putDxf(CTDxf dxf) {
  688. this.dxfs.add(dxf);
  689. return this.dxfs.size();
  690. }
  691. /**
  692. * NOTE: this only returns explicitly defined styles
  693. * @param name of the table style
  694. * @return defined style, or null if not explicitly defined
  695. *
  696. * @since 3.17 beta 1
  697. */
  698. public TableStyle getExplicitTableStyle(String name) {
  699. return tableStyles.get(name);
  700. }
  701. /**
  702. * @param name of the table style
  703. * @return defined style, either explicit or built-in, or null if not found
  704. *
  705. * @since 3.17 beta 1
  706. */
  707. public TableStyle getTableStyle(String name) {
  708. if (name == null) return null;
  709. try {
  710. return XSSFBuiltinTableStyle.valueOf(name).getStyle();
  711. } catch (IllegalArgumentException e) {
  712. return getExplicitTableStyle(name);
  713. }
  714. }
  715. /**
  716. * Create a cell style in this style table.
  717. * Note - End users probably want to call {@link XSSFWorkbook#createCellStyle()}
  718. * rather than working with the styles table directly.
  719. * @throws IllegalStateException if the maximum number of cell styles has been reached.
  720. */
  721. public XSSFCellStyle createCellStyle() {
  722. if (getNumCellStyles() > MAXIMUM_STYLE_ID) {
  723. throw new IllegalStateException("The maximum number of Cell Styles was exceeded. " +
  724. "You can define up to " + MAXIMUM_STYLE_ID + " style in a .xlsx Workbook");
  725. }
  726. int xfSize = styleXfs.size();
  727. CTXf xf = CTXf.Factory.newInstance();
  728. xf.setNumFmtId(0);
  729. xf.setFontId(0);
  730. xf.setFillId(0);
  731. xf.setBorderId(0);
  732. xf.setXfId(0);
  733. int indexXf = putCellXf(xf);
  734. return new XSSFCellStyle(indexXf - 1, xfSize - 1, this, theme);
  735. }
  736. /**
  737. * Finds a font that matches the one with the supplied attributes
  738. * @deprecated POI 3.15 beta 2. Use {@link #findFont(boolean, short, short, String, boolean, boolean, short, byte)} instead.
  739. */
  740. public XSSFFont findFont(short boldWeight, short color, short fontHeight, String name, boolean italic, boolean strikeout, short typeOffset, byte underline) {
  741. for (XSSFFont font : fonts) {
  742. if ( (font.getBoldweight() == boldWeight)
  743. && font.getColor() == color
  744. && font.getFontHeight() == fontHeight
  745. && font.getFontName().equals(name)
  746. && font.getItalic() == italic
  747. && font.getStrikeout() == strikeout
  748. && font.getTypeOffset() == typeOffset
  749. && font.getUnderline() == underline)
  750. {
  751. return font;
  752. }
  753. }
  754. return null;
  755. }
  756. /**
  757. * Finds a font that matches the one with the supplied attributes
  758. */
  759. public XSSFFont findFont(boolean bold, short color, short fontHeight, String name, boolean italic, boolean strikeout, short typeOffset, byte underline) {
  760. for (XSSFFont font : fonts) {
  761. if ( (font.getBold() == bold)
  762. && font.getColor() == color
  763. && font.getFontHeight() == fontHeight
  764. && font.getFontName().equals(name)
  765. && font.getItalic() == italic
  766. && font.getStrikeout() == strikeout
  767. && font.getTypeOffset() == typeOffset
  768. && font.getUnderline() == underline)
  769. {
  770. return font;
  771. }
  772. }
  773. return null;
  774. }
  775. }