From 0cdc064b0ce891d60ed2b9690dd243c5de50d38b Mon Sep 17 00:00:00 2001 From: Greg Woolsey Date: Fri, 12 May 2017 18:01:32 +0000 Subject: [PATCH] git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1795002 13f79535-47bb-0310-9956-ffa450edef68 --- .classpath | 2 +- .../usermodel/ConditionalFormattingRule.java | 2 +- .../usermodel/DifferentialStyleProvider.java | 32 + .../org/apache/poi/ss/usermodel/Table.java | 6 +- .../apache/poi/ss/usermodel/TableStyle.java | 23 + .../poi/ss/usermodel/TableStyleInfo.java | 40 + .../poi/ss/usermodel/TableStyleType.java | 49 + .../apache/poi/xssf/model/StylesTable.java | 44 +- .../xssf/usermodel/XSSFBuiltinTableStyle.java | 411 + .../xssf/usermodel/XSSFDxfStyleProvider.java | 59 + .../apache/poi/xssf/usermodel/XSSFTable.java | 10 + .../poi/xssf/usermodel/XSSFTableStyle.java | 54 + .../xssf/usermodel/XSSFTableStyleInfo.java | 55 + .../poi/xssf/usermodel/TestTableStyles.java | 62 + .../poi/xssf/usermodel/presetTableStyles.xml | 18070 ++++++++++++++++ test-data/spreadsheet/tableStyle.xlsx | Bin 0 -> 10899 bytes test-data/spreadsheet/~$tableStyle.xlsx | Bin 0 -> 165 bytes 17 files changed, 18914 insertions(+), 5 deletions(-) create mode 100644 src/java/org/apache/poi/ss/usermodel/DifferentialStyleProvider.java create mode 100644 src/java/org/apache/poi/ss/usermodel/TableStyle.java create mode 100644 src/java/org/apache/poi/ss/usermodel/TableStyleInfo.java create mode 100644 src/java/org/apache/poi/ss/usermodel/TableStyleType.java create mode 100644 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFBuiltinTableStyle.java create mode 100644 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDxfStyleProvider.java create mode 100644 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyle.java create mode 100644 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyleInfo.java create mode 100644 src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestTableStyles.java create mode 100644 src/resources/ooxml/org/apache/poi/xssf/usermodel/presetTableStyles.xml create mode 100644 test-data/spreadsheet/tableStyle.xlsx create mode 100644 test-data/spreadsheet/~$tableStyle.xlsx 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 0000000000000000000000000000000000000000..e926287a1d85ec73ea998830cfd0adf135e02710 GIT binary patch literal 10899 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 literal 0 HcmV?d00001 diff --git a/test-data/spreadsheet/~$tableStyle.xlsx b/test-data/spreadsheet/~$tableStyle.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..6b2e003e923166931c3ae0ba69c0c1eaf400538e GIT binary patch literal 165 xcmd;aFG@{U2+z;YDNe0aAR6#6xHA+nq%x#4C@_RG