diff --git a/.classpath b/.classpath index b860811a2..f700e8e5e 100644 --- a/.classpath +++ b/.classpath @@ -18,7 +18,7 @@ - + diff --git a/src/java/org/apache/poi/ss/usermodel/ConditionalFormattingRule.java b/src/java/org/apache/poi/ss/usermodel/ConditionalFormattingRule.java index 78b16ad87..bb8fec46f 100644 --- a/src/java/org/apache/poi/ss/usermodel/ConditionalFormattingRule.java +++ b/src/java/org/apache/poi/ss/usermodel/ConditionalFormattingRule.java @@ -22,7 +22,7 @@ package org.apache.poi.ss.usermodel; /** * Represents a description of a conditional formatting rule */ -public interface ConditionalFormattingRule { +public interface ConditionalFormattingRule extends DifferentialStyleProvider { /** * Create a new border formatting structure if it does not exist, * otherwise just return existing object. diff --git a/src/java/org/apache/poi/ss/usermodel/DifferentialStyleProvider.java b/src/java/org/apache/poi/ss/usermodel/DifferentialStyleProvider.java new file mode 100644 index 000000000..ab002e926 --- /dev/null +++ b/src/java/org/apache/poi/ss/usermodel/DifferentialStyleProvider.java @@ -0,0 +1,32 @@ +package org.apache.poi.ss.usermodel; + +/** + * Interface for classes providing differential style definitions, such as conditional format rules + * and table/pivot table styles. + * + * @since 3.17 beta 1 + */ +public interface DifferentialStyleProvider { + + /** + * @return - border formatting object if defined, null otherwise + */ + BorderFormatting getBorderFormatting(); + + /** + * @return - font formatting object if defined, null otherwise + */ + FontFormatting getFontFormatting(); + + /** + * + * @return number format defined for this rule, or null if the cell default should be used + */ + ExcelNumberFormat getNumberFormat(); + + /** + * @return - pattern formatting object if defined, null otherwise + */ + PatternFormatting getPatternFormatting(); + +} diff --git a/src/java/org/apache/poi/ss/usermodel/Table.java b/src/java/org/apache/poi/ss/usermodel/Table.java index 8a189d6bd..01e0394aa 100644 --- a/src/java/org/apache/poi/ss/usermodel/Table.java +++ b/src/java/org/apache/poi/ss/usermodel/Table.java @@ -77,5 +77,9 @@ public interface Table { */ boolean isHasTotalsRow(); - + /** + * @return TableStyleInfo for this instance + * @since 3.17 beta 1 + */ + TableStyleInfo getStyle(); } diff --git a/src/java/org/apache/poi/ss/usermodel/TableStyle.java b/src/java/org/apache/poi/ss/usermodel/TableStyle.java new file mode 100644 index 000000000..09092d447 --- /dev/null +++ b/src/java/org/apache/poi/ss/usermodel/TableStyle.java @@ -0,0 +1,23 @@ +package org.apache.poi.ss.usermodel; + +/** + * Data table style definition. Includes style elements for various table components. + * Any number of style elements may be represented, and any cell may be styled by + * multiple elements. The order of elements in {@link TableStyleType} defines precedence. + * + * @since 3.17 beta 1 + */ +public interface TableStyle { + + /** + * @return name (may be a builtin name) + */ + String getName(); + + /** + * + * @param type + * @return style definition for the given type, or null if not defined in this style. + */ + DifferentialStyleProvider getStyle(TableStyleType type); +} diff --git a/src/java/org/apache/poi/ss/usermodel/TableStyleInfo.java b/src/java/org/apache/poi/ss/usermodel/TableStyleInfo.java new file mode 100644 index 000000000..a4bfd1ff6 --- /dev/null +++ b/src/java/org/apache/poi/ss/usermodel/TableStyleInfo.java @@ -0,0 +1,40 @@ +package org.apache.poi.ss.usermodel; + +/** + * style information for a specific table instance, referencing the document style + * and indicating which optional portions of the style to apply. + * + * @since 3.17 beta 1 + */ +public interface TableStyleInfo { + + /** + * @return true if alternating column styles should be applied + */ + boolean isShowColumnStripes(); + + /** + * @return true if alternating row styles should be applied + */ + boolean isShowRowStripes(); + + /** + * @return true if the distinct first column style should be applied + */ + boolean isShowFirstColumn(); + + /** + * @return true if the distinct last column style should be applied + */ + boolean isShowLastColumn(); + + /** + * @return the name of the style (may reference a built-in style) + */ + String getName(); + + /** + * @return style definition + */ + TableStyle getStyle(); +} diff --git a/src/java/org/apache/poi/ss/usermodel/TableStyleType.java b/src/java/org/apache/poi/ss/usermodel/TableStyleType.java new file mode 100644 index 000000000..67810b76d --- /dev/null +++ b/src/java/org/apache/poi/ss/usermodel/TableStyleType.java @@ -0,0 +1,49 @@ +package org.apache.poi.ss.usermodel; + +/** + * Ordered list of table style elements, for both data tables and pivot tables. + * Some elements only apply to pivot tables, but any style definition can omit any number, + * so having them in one list should not be a problem. + *

+ * The order is the specification order of application, with later elements overriding previous + * ones, if style properties conflict. + *

+ * Processing could iterate bottom-up if looking for specific properties, and stop when the + * first style is found defining a value for that property. + *

+ * Enum names match the OOXML spec values exactly, so {@link #valueOf(String)} will work. + * + * @since 3.17 beta 1 + */ +public enum TableStyleType { + + wholeTable, + headerRow, + totalRow, + firstColumn, + lastColumn, + firstRowStripe, + secondRowStripe, + firstColumnStripe, + secondColumnStripe, + firstHeaderCell, + lastHeaderCell, + firstTotalCell, + lastTotalCell, + firstSubtotalColumn, + secondSubtotalColumn, + thirdSubtotalColumn, + firstSubtotalRow, + secondSubtotalRow, + thirdSubtotalRow, + blankRow, + firstColumnSubheading, + secondColumnSubheading, + thirdColumnSubheading, + firstRowSubheading, + secondRowSubheading, + thirdRowSubheading, + pageFieldLabels, + pageFieldValues, + ; +} diff --git a/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java b/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java index c192aed3d..901757582 100644 --- a/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java +++ b/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java @@ -25,6 +25,7 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -37,11 +38,14 @@ import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.usermodel.BuiltinFormats; import org.apache.poi.ss.usermodel.FontFamily; import org.apache.poi.ss.usermodel.FontScheme; +import org.apache.poi.ss.usermodel.TableStyle; import org.apache.poi.util.Internal; +import org.apache.poi.xssf.usermodel.XSSFBuiltinTableStyle; import org.apache.poi.xssf.usermodel.XSSFCellStyle; import org.apache.poi.xssf.usermodel.XSSFFactory; import org.apache.poi.xssf.usermodel.XSSFFont; import org.apache.poi.xssf.usermodel.XSSFRelation; +import org.apache.poi.xssf.usermodel.XSSFTableStyle; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.extensions.XSSFCellBorder; import org.apache.poi.xssf.usermodel.extensions.XSSFCellFill; @@ -59,6 +63,8 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFonts; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTNumFmt; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTNumFmts; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTStylesheet; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyle; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyles; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXf; import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPatternType; import org.openxmlformats.schemas.spreadsheetml.x2006.main.StyleSheetDocument; @@ -75,6 +81,7 @@ public class StylesTable extends POIXMLDocumentPart { private final List xfs = new ArrayList(); private final List dxfs = new ArrayList(); + private final Map tableStyles = new HashMap(); /** * The first style id available for use as a custom style @@ -189,7 +196,7 @@ public class StylesTable extends POIXMLDocumentPart { * @param is The input stream containing the XML document. * @throws IOException if an error occurs while reading. */ - protected void readFrom(InputStream is) throws IOException { + public void readFrom(InputStream is) throws IOException { try { doc = StyleSheetDocument.Factory.parse(is, DEFAULT_XML_OPTIONS); @@ -237,6 +244,13 @@ public class StylesTable extends POIXMLDocumentPart { CTDxfs styleDxfs = styleSheet.getDxfs(); if(styleDxfs != null) dxfs.addAll(Arrays.asList(styleDxfs.getDxfArray())); + CTTableStyles ctTableStyles = styleSheet.getTableStyles(); + if (ctTableStyles != null) { + for (CTTableStyle style : Arrays.asList(ctTableStyles.getTableStyleArray())) { + tableStyles.put(style.getName(), new XSSFTableStyle(styleDxfs, style)); + } + } + } catch (XmlException e) { throw new IOException(e.getLocalizedMessage()); } @@ -766,7 +780,33 @@ public class StylesTable extends POIXMLDocumentPart { this.dxfs.add(dxf); return this.dxfs.size(); } - + + /** + * NOTE: this only returns explicitly defined styles + * @param name of the table style + * @return defined style, or null if not explicitly defined + * + * @since 3.17 beta 1 + */ + public TableStyle getExplicitTableStyle(String name) { + return tableStyles.get(name); + } + + /** + * @param name of the table style + * @return defined style, either explicit or built-in, or null if not found + * + * @since 3.17 beta 1 + */ + public TableStyle getTableStyle(String name) { + if (name == null) return null; + try { + return XSSFBuiltinTableStyle.valueOf(name).getStyle(); + } catch (IllegalArgumentException e) { + return getExplicitTableStyle(name); + } + } + /** * Create a cell style in this style table. * Note - End users probably want to call {@link XSSFWorkbook#createCellStyle()} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFBuiltinTableStyle.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFBuiltinTableStyle.java new file mode 100644 index 000000000..f2df055fc --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFBuiltinTableStyle.java @@ -0,0 +1,411 @@ +package org.apache.poi.xssf.usermodel; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; + +import org.apache.poi.ss.usermodel.TableStyle; +import org.apache.poi.util.DocumentHelper; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.model.StylesTable; +import org.apache.xmlbeans.XmlOptions; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDxfs; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyle; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSSerializer; + +/** + * Table style names defined in the OOXML spec. + * The actual styling is defined in presetTableStyles.xml + */ +public enum XSSFBuiltinTableStyle { + /***/ + TableStyleDark1, + /***/ + TableStyleDark2, + /***/ + TableStyleDark3, + /***/ + TableStyleDark4, + /***/ + TableStyleDark5, + /***/ + TableStyleDark6, + /***/ + TableStyleDark7, + /***/ + TableStyleDark8, + /***/ + TableStyleDark9, + /***/ + TableStyleDark10, + /***/ + TableStyleDark11, + /***/ + TableStyleLight1, + /***/ + TableStyleLight2, + /***/ + TableStyleLight3, + /***/ + TableStyleLight4, + /***/ + TableStyleLight5, + /***/ + TableStyleLight6, + /***/ + TableStyleLight7, + /***/ + TableStyleLight8, + /***/ + TableStyleLight9, + /***/ + TableStyleLight10, + /***/ + TableStyleLight11, + /***/ + TableStyleLight12, + /***/ + TableStyleLight13, + /***/ + TableStyleLight14, + /***/ + TableStyleLight15, + /***/ + TableStyleLight16, + /***/ + TableStyleLight17, + /***/ + TableStyleLight18, + /***/ + TableStyleLight19, + /***/ + TableStyleLight20, + /***/ + TableStyleLight21, + /***/ + TableStyleMedium1, + /***/ + TableStyleMedium2, + /***/ + TableStyleMedium3, + /***/ + TableStyleMedium4, + /***/ + TableStyleMedium5, + /***/ + TableStyleMedium6, + /***/ + TableStyleMedium7, + /***/ + TableStyleMedium8, + /***/ + TableStyleMedium9, + /***/ + TableStyleMedium10, + /***/ + TableStyleMedium11, + /***/ + TableStyleMedium12, + /***/ + TableStyleMedium13, + /***/ + TableStyleMedium14, + /***/ + TableStyleMedium15, + /***/ + TableStyleMedium16, + /***/ + TableStyleMedium17, + /***/ + TableStyleMedium18, + /***/ + TableStyleMedium19, + /***/ + TableStyleMedium20, + /***/ + TableStyleMedium21, + /***/ + TableStyleMedium22, + /***/ + TableStyleMedium23, + /***/ + TableStyleMedium24, + /***/ + TableStyleMedium25, + /***/ + TableStyleMedium26, + /***/ + TableStyleMedium27, + /***/ + TableStyleMedium28, + /***/ + PivotStyleMedium1, + /***/ + PivotStyleMedium2, + /***/ + PivotStyleMedium3, + /***/ + PivotStyleMedium4, + /***/ + PivotStyleMedium5, + /***/ + PivotStyleMedium6, + /***/ + PivotStyleMedium7, + /***/ + PivotStyleMedium8, + /***/ + PivotStyleMedium9, + /***/ + PivotStyleMedium10, + /***/ + PivotStyleMedium11, + /***/ + PivotStyleMedium12, + /***/ + PivotStyleMedium13, + /***/ + PivotStyleMedium14, + /***/ + PivotStyleMedium15, + /***/ + PivotStyleMedium16, + /***/ + PivotStyleMedium17, + /***/ + PivotStyleMedium18, + /***/ + PivotStyleMedium19, + /***/ + PivotStyleMedium20, + /***/ + PivotStyleMedium21, + /***/ + PivotStyleMedium22, + /***/ + PivotStyleMedium23, + /***/ + PivotStyleMedium24, + /***/ + PivotStyleMedium25, + /***/ + PivotStyleMedium26, + /***/ + PivotStyleMedium27, + /***/ + PivotStyleMedium28, + /***/ + PivotStyleLight1, + /***/ + PivotStyleLight2, + /***/ + PivotStyleLight3, + /***/ + PivotStyleLight4, + /***/ + PivotStyleLight5, + /***/ + PivotStyleLight6, + /***/ + PivotStyleLight7, + /***/ + PivotStyleLight8, + /***/ + PivotStyleLight9, + /***/ + PivotStyleLight10, + /***/ + PivotStyleLight11, + /***/ + PivotStyleLight12, + /***/ + PivotStyleLight13, + /***/ + PivotStyleLight14, + /***/ + PivotStyleLight15, + /***/ + PivotStyleLight16, + /***/ + PivotStyleLight17, + /***/ + PivotStyleLight18, + /***/ + PivotStyleLight19, + /***/ + PivotStyleLight20, + /***/ + PivotStyleLight21, + /***/ + PivotStyleLight22, + /***/ + PivotStyleLight23, + /***/ + PivotStyleLight24, + /***/ + PivotStyleLight25, + /***/ + PivotStyleLight26, + /***/ + PivotStyleLight27, + /***/ + PivotStyleLight28, + /***/ + PivotStyleDark1, + /***/ + PivotStyleDark2, + /***/ + PivotStyleDark3, + /***/ + PivotStyleDark4, + /***/ + PivotStyleDark5, + /***/ + PivotStyleDark6, + /***/ + PivotStyleDark7, + /***/ + PivotStyleDark8, + /***/ + PivotStyleDark9, + /***/ + PivotStyleDark10, + /***/ + PivotStyleDark11, + /***/ + PivotStyleDark12, + /***/ + PivotStyleDark13, + /***/ + PivotStyleDark14, + /***/ + PivotStyleDark15, + /***/ + PivotStyleDark16, + /***/ + PivotStyleDark17, + /***/ + PivotStyleDark18, + /***/ + PivotStyleDark19, + /***/ + PivotStyleDark20, + /***/ + PivotStyleDark21, + /***/ + PivotStyleDark22, + /***/ + PivotStyleDark23, + /***/ + PivotStyleDark24, + /***/ + PivotStyleDark25, + /***/ + PivotStyleDark26, + /***/ + PivotStyleDark27, + /***/ + PivotStyleDark28, + ; + + private static final Map styleMap = new HashMap(60); + + private XSSFBuiltinTableStyle() { + } + + /** + * @return built-in {@link TableStyle} definition + */ + public TableStyle getStyle() { + init(); + return styleMap.get(this); + } + + /** + * NOTE: only checks by name, not definition. + * @param style + * @return true if the style represents a built-in style, false if it is null or a custom style + */ + public static boolean isBuiltinStyle(TableStyle style) { + if (style == null) return false; + try { + XSSFBuiltinTableStyle.valueOf(style.getName()); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + /** + * Only init once - thus the synchronized. Lazy, after creating instances, + * and only when a style is actually needed, to avoid overhead for uses + * that don't need the actual style definitions. + *

+ * Public so clients can initialize the map on startup rather than lazily + * during evaluation if desired. + */ + public static final synchronized void init() { + if (! styleMap.isEmpty()) return; + + /* + * initialize map. Every built-in has this format: + * + * + * ... + * ... + * + * + * ... + * + * + */ + try { + final InputStream is = XSSFBuiltinTableStyle.class.getResourceAsStream("presetTableStyles.xml"); + try { + final Document doc = DocumentHelper.readDocument(is); + final NodeList styleNodes = doc.getDocumentElement().getChildNodes(); + for (int i=0; i < styleNodes.getLength(); i++) { + final Node node = styleNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) continue; // only care about elements + final Element tag = (Element) node; + String styleName = tag.getTagName(); + Node dxfsNode = tag.getElementsByTagName("dxfs").item(0); + Node tableStyleNode = tag.getElementsByTagName("tableStyles").item(0); + + // hack because I can't figure out how to get XMLBeans to parse a sub-element in a standalone manner + // - build a fake styles.xml file with just this built-in + StylesTable styles = new StylesTable(); + styles.readFrom(new ByteArrayInputStream(styleXML(dxfsNode, tableStyleNode).getBytes(Charset.forName("UTF-8")))); + styleMap.put(XSSFBuiltinTableStyle.valueOf(styleName), styles.getExplicitTableStyle(styleName)); + } + } finally { + IOUtils.closeQuietly(is); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static String styleXML(Node dxfsNode, Node tableStyleNode) { + DOMImplementationLS lsImpl = (DOMImplementationLS)dxfsNode.getOwnerDocument().getImplementation().getFeature("LS", "3.0"); + LSSerializer lsSerializer = lsImpl.createLSSerializer(); + lsSerializer.getDomConfig().setParameter("xml-declaration", false); + StringBuilder sb = new StringBuilder(); + sb.append("\n") + .append("\n"); + sb.append(lsSerializer.writeToString(dxfsNode)); + sb.append(lsSerializer.writeToString(tableStyleNode)); + sb.append(""); + return sb.toString(); + } +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDxfStyleProvider.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDxfStyleProvider.java new file mode 100644 index 000000000..95c3ef3f4 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDxfStyleProvider.java @@ -0,0 +1,59 @@ +package org.apache.poi.xssf.usermodel; + +import org.apache.poi.ss.usermodel.BorderFormatting; +import org.apache.poi.ss.usermodel.DifferentialStyleProvider; +import org.apache.poi.ss.usermodel.ExcelNumberFormat; +import org.apache.poi.ss.usermodel.FontFormatting; +import org.apache.poi.ss.usermodel.PatternFormatting; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDxf; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTNumFmt; + +/** + * Style based on a dxf record - e.g. table style element or conditional formatting rule + */ +public class XSSFDxfStyleProvider implements DifferentialStyleProvider { + + private final BorderFormatting border; + private final FontFormatting font; + private final ExcelNumberFormat number; + private final PatternFormatting fill; + + /** + * @param dxf + */ + public XSSFDxfStyleProvider(CTDxf dxf) { + if (dxf == null) { + border = null; + font = null; + number = null; + fill = null; + } else { + border = dxf.isSetBorder() ? new XSSFBorderFormatting(dxf.getBorder()) : null; + font = dxf.isSetFont() ? new XSSFFontFormatting(dxf.getFont()) : null; + if (dxf.isSetNumFmt()) { + CTNumFmt numFmt = dxf.getNumFmt(); + number = new ExcelNumberFormat((int) numFmt.getNumFmtId(), numFmt.getFormatCode()); + } else { + number = null; + } + fill = dxf.isSetFill() ? new XSSFPatternFormatting(dxf.getFill()) : null; + } + } + + public BorderFormatting getBorderFormatting() { + return border; + } + + public FontFormatting getFontFormatting() { + return font; + } + + public ExcelNumberFormat getNumberFormat() { + return number; + } + + public PatternFormatting getPatternFormatting() { + return fill; + } + +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTable.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTable.java index 5bdd950de..f1a31b0f9 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTable.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTable.java @@ -31,6 +31,7 @@ import java.util.Locale; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.ss.usermodel.Table; +import org.apache.poi.ss.usermodel.TableStyleInfo; import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.Internal; import org.apache.poi.util.StringUtil; @@ -444,4 +445,13 @@ public class XSSFTable extends POIXMLDocumentPart implements Table { public int getEndRowIndex() { return getEndCellReference().getRow(); } + + /** + * + * @since 3.17 beta 1 + */ + public TableStyleInfo getStyle() { + if (! ctTable.isSetTableStyleInfo()) return null; + return new XSSFTableStyleInfo(((XSSFSheet) getParent()).getWorkbook().getStylesSource(), ctTable.getTableStyleInfo()); + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyle.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyle.java new file mode 100644 index 000000000..b7f709e03 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyle.java @@ -0,0 +1,54 @@ +package org.apache.poi.xssf.usermodel; + +import java.util.EnumMap; +import java.util.Map; + +import org.apache.poi.ss.usermodel.DifferentialStyleProvider; +import org.apache.poi.ss.usermodel.TableStyle; +import org.apache.poi.ss.usermodel.TableStyleType; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDxf; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDxfs; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyle; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyleElement; + +/** + * {@link TableStyle} implementation for styles defined in the OOXML styles.xml. + * Also used for built-in styles via dummy XML generated from presetTableStyles.xml. + */ +public class XSSFTableStyle implements TableStyle { + + private final String name; + private final Map elementMap = new EnumMap(TableStyleType.class); + + /** + * @param dxfs + * @param tableStyle + */ + public XSSFTableStyle(CTDxfs dxfs, CTTableStyle tableStyle) { + this.name = tableStyle.getName(); + for (CTTableStyleElement element : tableStyle.getTableStyleElementList()) { + TableStyleType type = TableStyleType.valueOf(element.getType().toString()); + DifferentialStyleProvider dstyle = null; + if (element.isSetDxfId()) { + int idx = (int) element.getDxfId() -1; + CTDxf dxf; + if (idx >= 0 && idx < dxfs.getCount()) { + dxf = dxfs.getDxfArray(idx); + } else { + dxf = null; + } + if (dxf != null) dstyle = new XSSFDxfStyleProvider(dxf); + } + elementMap.put(type, dstyle); + } + } + + public String getName() { + return name; + } + + public DifferentialStyleProvider getStyle(TableStyleType type) { + return elementMap.get(type); + } + +} \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyleInfo.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyleInfo.java new file mode 100644 index 000000000..325564c8e --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyleInfo.java @@ -0,0 +1,55 @@ +package org.apache.poi.xssf.usermodel; + +import org.apache.poi.ss.usermodel.TableStyle; +import org.apache.poi.ss.usermodel.TableStyleInfo; +import org.apache.poi.xssf.model.StylesTable; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyleInfo; + +/** + * Wrapper for the CT class, to cache values and add style lookup + */ +public class XSSFTableStyleInfo implements TableStyleInfo { + + private final boolean columnStripes; + private final boolean rowStripes; + private final boolean firstColumn; + private final boolean lastColumn; + private final TableStyle style; + + /** + * @param stylesTable + * @param tableStyleInfo + */ + public XSSFTableStyleInfo(StylesTable stylesTable, CTTableStyleInfo tableStyleInfo) { + this.columnStripes = tableStyleInfo.getShowColumnStripes(); + this.rowStripes = tableStyleInfo.getShowRowStripes(); + this.firstColumn = tableStyleInfo.getShowFirstColumn(); + this.lastColumn = tableStyleInfo.getShowLastColumn(); + this.style = stylesTable.getTableStyle(tableStyleInfo.getName()); + } + + public boolean isShowColumnStripes() { + return columnStripes; + } + + public boolean isShowRowStripes() { + return rowStripes; + } + + public boolean isShowFirstColumn() { + return firstColumn; + } + + public boolean isShowLastColumn() { + return lastColumn; + } + + public String getName() { + return style.getName(); + } + + public TableStyle getStyle() { + return style; + } + +} diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestTableStyles.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestTableStyles.java new file mode 100644 index 000000000..1ca90a990 --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestTableStyles.java @@ -0,0 +1,62 @@ +package org.apache.poi.xssf.usermodel; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.apache.poi.ss.usermodel.DifferentialStyleProvider; +import org.apache.poi.ss.usermodel.FontFormatting; +import org.apache.poi.ss.usermodel.PatternFormatting; +import org.apache.poi.ss.usermodel.Table; +import org.apache.poi.ss.usermodel.TableStyle; +import org.apache.poi.ss.usermodel.TableStyleInfo; +import org.apache.poi.ss.usermodel.TableStyleType; +import org.apache.poi.xssf.XSSFTestDataSamples; +import org.junit.Test; + +/** + * Test built-in table styles + */ +public class TestTableStyles { + + /** + * Test that a built-in style is initialized properly + */ + @Test + public void testBuiltinStyleInit() { + TableStyle style = XSSFBuiltinTableStyle.TableStyleMedium2.getStyle(); + assertNotNull("no style found for Medium2", style); + assertNull("Should not have style info for blankRow", style.getStyle(TableStyleType.blankRow)); + DifferentialStyleProvider headerRow = style.getStyle(TableStyleType.headerRow); + assertNotNull("no header row style", headerRow); + FontFormatting font = headerRow.getFontFormatting(); + assertNotNull("No header row font formatting", font); + assertTrue("header row not bold", font.isBold()); + PatternFormatting fill = headerRow.getPatternFormatting(); + assertNotNull("No header fill", fill); + assertEquals("wrong header fill", 4, ((XSSFColor) fill.getFillBackgroundColorColor()).getTheme()); + } + + @Test + public void testCustomStyle() throws Exception { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("tableStyle.xlsx"); + + Table table = wb.getTable("Table1"); + assertNotNull("missing table", table); + + TableStyleInfo style = table.getStyle(); + assertNotNull("Missing table style info", style); + assertNotNull("Missing table style", style.getStyle()); + assertEquals("Wrong name", "TestTableStyle", style.getName()); + assertEquals("Wrong name", "TestTableStyle", style.getStyle().getName()); + + DifferentialStyleProvider firstColumn = style.getStyle().getStyle(TableStyleType.firstColumn); + assertNotNull("no first column style", firstColumn); + FontFormatting font = firstColumn.getFontFormatting(); + assertNotNull("no first col font", font); + assertTrue("wrong first col bold", font.isBold()); + + wb.close(); + } +} diff --git a/src/resources/ooxml/org/apache/poi/xssf/usermodel/presetTableStyles.xml b/src/resources/ooxml/org/apache/poi/xssf/usermodel/presetTableStyles.xml new file mode 100644 index 000000000..f83f2e33c --- /dev/null +++ b/src/resources/ooxml/org/apache/poi/xssf/usermodel/presetTableStyles.xml @@ -0,0 +1,18070 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test-data/spreadsheet/tableStyle.xlsx b/test-data/spreadsheet/tableStyle.xlsx new file mode 100644 index 000000000..e926287a1 Binary files /dev/null and b/test-data/spreadsheet/tableStyle.xlsx differ diff --git a/test-data/spreadsheet/~$tableStyle.xlsx b/test-data/spreadsheet/~$tableStyle.xlsx new file mode 100644 index 000000000..6b2e003e9 Binary files /dev/null and b/test-data/spreadsheet/~$tableStyle.xlsx differ