Improving Coverage for Excel2003 XML Reader (#1557)

* Improving Coverage for Excel2003 XML Reader

Reader/Xml is now 100% covered.

File templates/Excel2003XMLTest.xml, used in some tests, is *not*
readable by a current version of Excel. I have substituted a new file
excel2003.xml to be used in its place. I have not deleted the original
in case someone in future (possibly me) wants to see what it needs to
make it usable.

There are minimal code changes.
- Unused protected functions pixel2WidthUnits and widthUnits2Pixel
  are deleted.
- One regex looking to convert hex characters is changed from a-z to a-f,
  and made case insensitive.
- No calculation performed for "error" cell (previously calculation
  was attempted and threw exception).
- Empty relative row/cell is now handled correctly.
- Style applied to empty cell when appropriate.
- Support added for textRotation.
- Support added for border styles.
- Support added for diagonal borders.
- Support added for superscript and subscript.
- Support added for fill patterns.

In theory, encodings other than UTF-8 were supported.
In fact, I was unable to get SecurityScanner to pass *any* xml which is
not UTF-8. Eliminating the assumption that strings might not be UTF-8
allowed much of the code to be greatly simplified.
After that, I added some code that would permit the use of
some ASCII-compatible encodings (there is a test of ISO-8859-1).
It would be more difficult to handle other encodings (such as UTF-16).
I am not convinced that even the ISO-8859 effort is worth it,
but am willing to investigate either expanding or eliminating
non-UTF8 support.

I added a number of tests, creating an Xml directory, and moving
XmlTest to that directory.

Pull Request had problems reading old invalid sample in the code
coverage phase, not in any of the other test phases, and not in
the code coverage phase on my local machine.
As it turns out, aside from being invalid, the sample
is much larger than any of the other samples. Tests have been
adjusted accordingly.

* Smaller Test File

Should eliminate need to avoid test during xml coverage.

* Break Up Style Test into Multiple Tests

Per suggestion from Mark Baker.

* Integrate AddressHelper Change

The introduction of AddressHelper introduced a conflict which needed to
be resolved. I wanted to test it locally before resolving. This required
me to add (unchanged) AddressHelper to my local copy. I hope this is
an okay manner of resolving the conflict.

* Weird Travis Error

XmlOddTest works just fine on my local machine, but Travis failed it.
Even worse, the lines which Travis flags don't even make any sense
(one was the empty line between two methods!).
This test is not essential to the rest of the change. I am removing
it from the package, and will attempt to re-add it when I have a chance
to sync up my fork with the main project.
This commit is contained in:
oleibman 2020-10-11 04:26:56 -07:00 committed by GitHub
parent 7545c411f9
commit 1741766a9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1645 additions and 128 deletions

View File

@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory;
require __DIR__ . '/../Header.php'; require __DIR__ . '/../Header.php';
$filename = __DIR__ . '/../templates/Excel2003XMLTest.xml'; $filename = __DIR__ . '/../templates/excel2003.xml';
$callStartTime = microtime(true); $callStartTime = microtime(true);
$spreadsheet = IOFactory::load($filename); $spreadsheet = IOFactory::load($filename);
$helper->logRead('Xml', $filename, $callStartTime); $helper->logRead('Xml', $filename, $callStartTime);

View File

@ -20,6 +20,19 @@
<ProtectStructure>False</ProtectStructure> <ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows> <ProtectWindows>False</ProtectWindows>
</ExcelWorkbook> </ExcelWorkbook>
<Styles>
<Style ss:ID="ce9">
<Alignment ss:Vertical="Bottom" ss:Rotate="0"/>
<Borders/>
<Font ss:Color="#000000" ss:FontName="Arial1" ss:Size="11"/>
<NumberFormat ss:Format="Short Date"/>
</Style>
<Style ss:ID="ce32">
<Alignment ss:Horizontal="Center" ss:Vertical="Center" ss:WrapText="1" ss:Rotate="0"/>
<Borders/>
<Font ss:Color="#000000" ss:FontName="Calibri" ss:Size="11"/>
</Style>
</Styles>
<ss:Worksheet ss:Name="Sample Data"> <ss:Worksheet ss:Name="Sample Data">
<Table> <Table>
<Column ss:Width="96.4913"/> <Column ss:Width="96.4913"/>
@ -28,7 +41,7 @@
<Column ss:Span="6" ss:Width="48.3874"/> <Column ss:Span="6" ss:Width="48.3874"/>
<Column ss:Index="12" ss:Width="50.2583"/> <Column ss:Index="12" ss:Width="50.2583"/>
<Column ss:Span="1011" ss:Width="48.3874"/> <Column ss:Span="1011" ss:Width="48.3874"/>
<Row ss:AutoFitHeight="0" ss:Height="14.9953"> <Row ss:Index="8" ss:AutoFitHeight="0" ss:Height="14.9953">
<Cell> <Cell>
<ss:Data xmlns="http://www.w3.org/TR/REC-html40" ss:Type="String">Test String 1</ss:Data> <ss:Data xmlns="http://www.w3.org/TR/REC-html40" ss:Type="String">Test String 1</ss:Data>
<Comment> <Comment>
@ -38,7 +51,16 @@
</Comment> </Comment>
</Cell> </Cell>
<Cell> <Cell>
<Data ss:Type="Number">1</Data> <Data ss:StyleID="ce32" ss:Type="Number">1</Data>
</Cell>
<Cell ss:Formula="of:=12/1">
<Data ss:Type="Number">12</Data>
</Cell>
<Cell ss:Formula="of:=[.C8]-[.B8]">
<Data ss:Type="Number">11</Data>
</Cell>
<Cell ss:StyleID="ce9">
<Data ss:Type="DateTime">1960-12-19T00:00:00.000</Data>
</Cell> </Cell>
</Row> </Row>
</Table> </Table>

View File

@ -0,0 +1,944 @@
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<Title>Xml2003 Workbook</Title>
<Subject>Test Gnumeric Workbook Subject</Subject>
<Author>Mark Baker</Author>
<Keywords>PHPExcel Xml Reader Test Keywords</Keywords>
<Description>Some comments about the PHPExcel Gnumeric Reader</Description>
<LastAuthor>Owen Leibman</LastAuthor>
<Created>2010-09-02T20:48:39Z</Created>
<LastSaved>2010-09-02T20:48:39Z</LastSaved>
<Category>PHPExcel Xml Reader Test Category</Category>
<Manager>Maarten Balliauw</Manager>
<Company>PHPExcel</Company>
<HyperlinkBase>https://github.com/PHPOffice/PhpSpreadsheet</HyperlinkBase>
<Version>16.00</Version>
</DocumentProperties>
<CustomDocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<my_API_Token dt:dt="string">AbCd1234</my_API_Token>
<my_API_Token_Expiry dt:dt="dateTime.tz">2019-01-31T07:00:00Z</my_API_Token_Expiry>
<my_API_Boolean dt:dt="boolean">1</my_API_Boolean>
<my_API_Int dt:dt="string">3</my_API_Int>
<myאInt dt:dt="string">2</myאInt>
<my_API_Float dt:dt="float">3.14159</my_API_Float>
</CustomDocumentProperties>
<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
<AllowPNG/>
</OfficeDocumentSettings>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>8964</WindowHeight>
<WindowWidth>23040</WindowWidth>
<WindowTopX>32767</WindowTopX>
<WindowTopY>32767</WindowTopY>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
<Borders/>
<Font ss:FontName="Calibri" ss:Size="11" ss:Color="#000000"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s62" ss:Name="Hyperlink">
<Font ss:FontName="Calibri" ss:Size="11" ss:Color="#0066CC"
ss:Underline="Single"/>
</Style>
<Style ss:ID="m1867777004784">
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="3"
ss:Color="#00B050"/>
<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="3"
ss:Color="#0070C0"/>
<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="3"
ss:Color="#FFFF00"/>
<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="3"
ss:Color="#FF0000"/>
</Borders>
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000"/>
</Style>
<Style ss:ID="s63">
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#FF0000" ss:Bold="1"/>
</Style>
<Style ss:ID="s64">
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000"/>
</Style>
<Style ss:ID="s65">
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000" ss:Bold="1"
ss:Italic="1"/>
</Style>
<Style ss:ID="s66">
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000" ss:Bold="1"/>
</Style>
<Style ss:ID="s68">
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000" ss:Underline="Single"/>
</Style>
<Style ss:ID="s69">
<Interior ss:Color="#FF0000" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="s70">
<Font ss:FontName="Calibri" ss:Size="11" ss:Color="#FF0000"/>
</Style>
<Style ss:ID="s72">
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000" ss:Italic="1"/>
</Style>
<Style ss:ID="s73">
<Interior ss:Color="#FF9900" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="s74">
<Font ss:FontName="Calibri" ss:Size="11" ss:Color="#FF6600"/>
</Style>
<Style ss:ID="s75">
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="DashDotDot" ss:Weight="1"
ss:Color="#000000"/>
</Borders>
</Style>
<Style ss:ID="s76">
<Interior ss:Color="#FFFF00" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="s77">
<Font ss:FontName="Calibri" ss:Size="11" ss:Color="#FFFF00"/>
</Style>
<Style ss:ID="s78">
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="DashDot" ss:Weight="1"
ss:Color="#000000"/>
</Borders>
</Style>
<Style ss:ID="s79">
<Font ss:FontName="Calibri" ss:Size="11" ss:Color="#000000"
ss:Underline="Single"/>
</Style>
<Style ss:ID="s80">
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000"/>
<NumberFormat ss:Format="dd/mm/yyyy"/>
</Style>
<Style ss:ID="s81">
<Interior ss:Color="#008000" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="s82">
<Font ss:FontName="Calibri" ss:Size="11" ss:Color="#008000"/>
</Style>
<Style ss:ID="s83">
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"
ss:Color="#000000"/>
</Borders>
</Style>
<Style ss:ID="s84">
<Font ss:FontName="Calibri" ss:Size="11" ss:Color="#000000"
ss:Underline="Double"/>
</Style>
<Style ss:ID="s85">
<Interior ss:Color="#0000FF" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="s86">
<Font ss:FontName="Calibri" ss:Size="11" ss:Color="#0000FF"/>
</Style>
<Style ss:ID="s87">
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="DashDotDot" ss:Weight="2"
ss:Color="#000000"/>
</Borders>
</Style>
<Style ss:ID="s88">
<Font ss:FontName="Calibri" ss:Size="11" ss:Color="#000000" ss:StrikeThrough="1"/>
</Style>
<Style ss:ID="s89">
<Interior ss:Color="#993366" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="s90">
<Font ss:FontName="Calibri" ss:Size="11" ss:Color="#800080"/>
</Style>
<Style ss:ID="s91">
<Interior ss:Color="#FF99CC" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="s92">
<Font ss:FontName="Calibri" ss:Size="11" ss:Color="#FF00FF"/>
</Style>
<Style ss:ID="s93">
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="DashDot" ss:Weight="2"
ss:Color="#000000"/>
</Borders>
</Style>
<Style ss:ID="s94">
<Borders>
<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="2"
ss:Color="#000000"/>
</Borders>
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000"/>
</Style>
<Style ss:ID="s95">
<Interior ss:Color="#DDBC7D" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="s96">
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Dash" ss:Weight="2"
ss:Color="#000000"/>
</Borders>
</Style>
<Style ss:ID="s97">
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000"/>
<NumberFormat ss:Format="#\ ?0/??0"/>
</Style>
<Style ss:ID="s98">
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="2"
ss:Color="#000000"/>
</Borders>
</Style>
<Style ss:ID="s99">
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="2"
ss:Color="#000000"/>
</Borders>
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000"/>
</Style>
<Style ss:ID="s100">
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="3"
ss:Color="#000000"/>
</Borders>
</Style>
<Style ss:ID="s101">
<NumberFormat ss:Format="hh&quot;:&quot;mm&quot;:&quot;ss"/>
</Style>
<Style ss:ID="s102">
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Double" ss:Weight="3"
ss:Color="#000000"/>
</Borders>
</Style>
<Style ss:ID="s103">
<Borders>
<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="2"
ss:Color="#000000"/>
</Borders>
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000"/>
</Style>
<Style ss:ID="s104">
<NumberFormat ss:Format="d/m/yy\ hh&quot;:&quot;mm"/>
</Style>
<Style ss:ID="s105">
<Borders>
<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="2"
ss:Color="#000000"/>
</Borders>
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000"/>
</Style>
<Style ss:ID="s113">
<Borders>
<Border ss:Position="DiagonalLeft" ss:LineStyle="Double" ss:Weight="3"
ss:Color="#FF0000"/>
<Border ss:Position="DiagonalRight" ss:LineStyle="Double" ss:Weight="3"
ss:Color="#FF0000"/>
</Borders>
</Style>
<Style ss:ID="s114">
<Alignment ss:Vertical="Bottom" ss:WrapText="1"/>
</Style>
<Style ss:ID="s115">
<Alignment ss:Vertical="Bottom" ss:WrapText="1"/>
<Borders>
<Border ss:Position="DiagonalLeft" ss:LineStyle="Continuous" ss:Weight="1"
ss:Color="#000000"/>
</Borders>
</Style>
<Style ss:ID="s116">
<Borders>
<Border ss:Position="DiagonalRight" ss:LineStyle="Continuous" ss:Weight="1"
ss:Color="#000000"/>
</Borders>
</Style>
<Style ss:ID="s118">
<Alignment ss:Vertical="Bottom" ss:WrapText="1"/>
<Font ss:FontName="Calibri" ss:Size="11" ss:Color="#CCFFCC"/>
<Interior ss:Color="#FF0000" ss:Pattern="Gray0625" ss:PatternColor="#FFFF00"/>
</Style>
<Style ss:ID="s121">
<Font ss:FontName="Sans" ss:Color="#000000"/>
</Style>
<Style ss:ID="s123">
<Alignment ss:Vertical="Bottom" ss:Rotate="90"/>
<Font ss:FontName="Sans" ss:Color="#000000"/>
</Style>
<Style ss:ID="s125">
<Alignment ss:Vertical="Bottom" ss:Rotate="45"/>
<Font ss:FontName="Sans" ss:Color="#000000"/>
</Style>
<Style ss:ID="s127">
<Alignment ss:Vertical="Bottom" ss:Rotate="-90"/>
<Font ss:FontName="Sans" ss:Color="#000000"/>
</Style>
<Style ss:ID="s129">
<Alignment ss:Vertical="Bottom" ss:Rotate="-45"/>
<Font ss:FontName="Sans" ss:Color="#000000"/>
</Style>
<Style ss:ID="s130">
<Font ss:FontName="Sans" ss:Color="#000000" ss:Underline="Single"/>
</Style>
<Style ss:ID="s131">
<Font ss:FontName="Sans" ss:Color="#000000" ss:VerticalAlign="Subscript"/>
</Style>
<Style ss:ID="s132">
<Font ss:FontName="Sans" ss:Color="#000000" ss:Underline="Double"/>
</Style>
<Style ss:ID="s133">
<Font ss:FontName="Sans" ss:Color="#000000" ss:VerticalAlign="Superscript"/>
</Style>
<Style ss:ID="s134">
<Font ss:FontName="Sans" ss:Color="#000000" ss:Underline="SingleAccounting"/>
</Style>
<Style ss:ID="s135">
<Font ss:FontName="Sans" ss:Color="#000000" ss:Underline="DoubleAccounting"/>
</Style>
<Style ss:ID="s136">
<Alignment ss:Vertical="Bottom" ss:WrapText="1"/>
<Font ss:FontName="Sans" ss:Color="#000000"/>
</Style>
<Style ss:ID="s139">
<Font ss:FontName="Sans" ss:Color="#0000FF" ss:Underline="Single"/>
</Style>
<Style ss:ID="s140">
<Font ss:FontName="Sans" ss:Color="#000000"/>
<NumberFormat ss:Format="0"/>
</Style>
<Style ss:ID="s141">
<Alignment ss:Vertical="Bottom" ss:WrapText="1"/>
<Font ss:FontName="Arial1" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
</Style>
<Style ss:ID="s142">
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000"/>
<NumberFormat ss:Format="0.00;[Red]0.00"/>
</Style>
<Style ss:ID="s143">
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000"/>
<NumberFormat ss:Format="dd\-mmm\-yyyy"/>
</Style>
<Style ss:ID="s144">
<Font ss:FontName="Arial1" ss:Size="11" ss:Color="#000000"/>
<NumberFormat ss:Format="hh&quot;:&quot;mm\ AM/PM"/>
</Style>
<Style ss:ID="s145">
<Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Dot" ss:Weight="1"
ss:Color="#000000"/>
</Borders>
</Style>
<Style ss:ID="s146">
<Alignment ss:Horizontal="Right" ss:Vertical="Bottom"/>
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Dash" ss:Weight="1"
ss:Color="#000000"/>
</Borders>
</Style>
<Style ss:ID="s147">
<Alignment ss:Vertical="Top"/>
<Interior ss:Color="#00CCFF" ss:Pattern="HorzStripe" ss:PatternColor="#0000FF"/>
</Style>
</Styles>
<Names>
<NamedRange ss:Name="goodname" ss:RefersTo="='Sample Data'!R30C1"/>
<NamedRange ss:Name="MarksRange" ss:RefersTo="='Sample Data'!R1C2:R4C3"/>
</Names>
<Worksheet ss:Name="Sample Data">
<Table ss:ExpandedColumnCount="14" ss:ExpandedRowCount="32" x:FullColumns="1"
x:FullRows="1" ss:DefaultRowHeight="14.55">
<Column ss:AutoFitWidth="0" ss:Width="96.6"/>
<Column ss:AutoFitWidth="0" ss:Width="48.6" ss:Span="1"/>
<Column ss:Index="4" ss:AutoFitWidth="0" ss:Width="36"/>
<Column ss:AutoFitWidth="0" ss:Width="48.6" ss:Span="6"/>
<Column ss:Index="12" ss:AutoFitWidth="0" ss:Width="50.4"/>
<Column ss:AutoFitWidth="0" ss:Width="48.6" ss:Span="1"/>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s63"><Data ss:Type="String">Test String 1</Data><Comment
ss:Author="Mark"><ss:Data xmlns="http://www.w3.org/TR/REC-html40"><Font
html:Face="Tahoma" html:Size="9" html:Color="#000000">Test for a simple colour-formatted string</Font></ss:Data></Comment></Cell>
<Cell ss:StyleID="s64"><Data ss:Type="Number">1</Data><NamedCell
ss:Name="MarksRange"/></Cell>
<Cell ss:StyleID="s64"><Data ss:Type="Number">5</Data><NamedCell
ss:Name="MarksRange"/></Cell>
<Cell ss:Index="5" ss:StyleID="s65"><Data ss:Type="String">A</Data></Cell>
<Cell ss:StyleID="s66"><Data ss:Type="String">E</Data></Cell>
<Cell ss:Index="8" ss:Formula="=RC[-6]+RC[-5]"><Data ss:Type="Number">6</Data></Cell>
<Cell ss:Index="10" ss:Formula="=RC[-5]&amp;RC[-4]"><Data ss:Type="String">AE</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s65"><Data ss:Type="String">Test - String 2</Data></Cell>
<Cell><Data ss:Type="Number">2</Data><NamedCell ss:Name="MarksRange"/></Cell>
<Cell ss:StyleID="s64"><Data ss:Type="Number">6</Data><NamedCell
ss:Name="MarksRange"/></Cell>
<Cell ss:Index="5"><Data ss:Type="String">B</Data></Cell>
<Cell><Data ss:Type="String">F</Data></Cell>
<Cell ss:Index="8" ss:Formula="=RC[-6]+RC[-5]"><Data ss:Type="Number">8</Data></Cell>
<Cell ss:Index="10" ss:Formula="=RC[-5]&amp;RC[-4]"><Data ss:Type="String">BF</Data></Cell>
<Cell ss:Index="14" ss:StyleID="s145"><Data ss:Type="String">Dot</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s68"><Data ss:Type="String">Test #3</Data></Cell>
<Cell ss:StyleID="s64"><Data ss:Type="Number">3</Data><NamedCell
ss:Name="MarksRange"/></Cell>
<Cell ss:StyleID="s64"><Data ss:Type="Number">7</Data><NamedCell
ss:Name="MarksRange"/></Cell>
<Cell ss:Index="5" ss:StyleID="s66"><Data ss:Type="String">C</Data></Cell>
<Cell ss:StyleID="s65"><Data ss:Type="String">G</Data></Cell>
<Cell ss:Index="8" ss:Formula="=RC[-6]+RC[-5]"><Data ss:Type="Number">10</Data></Cell>
<Cell ss:Index="10" ss:Formula="=RC[-5]&amp;RC[-4]"><Data ss:Type="String">CG</Data></Cell>
<Cell ss:StyleID="s69"><Data ss:Type="String">Red</Data></Cell>
<Cell ss:StyleID="s70"><Data ss:Type="String">Red</Data></Cell>
<Cell ss:Index="14" ss:StyleID="s146"><Data ss:Type="String">Dash</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s64"><Data ss:Type="String">Test with (&quot;) in string</Data></Cell>
<Cell><Data ss:Type="Number">4</Data><NamedCell ss:Name="MarksRange"/></Cell>
<Cell ss:StyleID="s64"><Data ss:Type="Number">8</Data><NamedCell
ss:Name="MarksRange"/></Cell>
<Cell ss:Index="5" ss:StyleID="s72"><Data ss:Type="String">D</Data></Cell>
<Cell><Data ss:Type="String">H</Data></Cell>
<Cell ss:Index="8" ss:Formula="=RC[-6]+RC[-5]"><Data ss:Type="Number">12</Data></Cell>
<Cell ss:Index="10" ss:Formula="=RC[-5]&amp;RC[-4]"><Data ss:Type="String">DH</Data></Cell>
<Cell ss:StyleID="s73"><Data ss:Type="String">Orange</Data></Cell>
<Cell ss:StyleID="s74"><Data ss:Type="String">Orange</Data></Cell>
<Cell ss:Index="14" ss:StyleID="s75"><Data ss:Type="String">Dash/Dot/Dot</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:Index="8" ss:Formula="=SUM(R[-4]C[-6]:R[-1]C[-6])"><Data
ss:Type="Number">10</Data></Cell>
<Cell ss:Formula="=SUM(R[-4]C[-6]:R[-1]C[-6])"><Data ss:Type="Number">26</Data></Cell>
<Cell ss:Formula="=SUM(R[-4]C[-8]:R[-1]C[-7])"><Data ss:Type="Number">36</Data></Cell>
<Cell ss:StyleID="s76"><Data ss:Type="String">Yellow</Data></Cell>
<Cell ss:StyleID="s77"><Data ss:Type="String">Yellow</Data></Cell>
<Cell ss:Index="14" ss:StyleID="s78"><Data ss:Type="String">Dash/Dot</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s79"><Data ss:Type="String">Test #3</Data></Cell>
<Cell><Data ss:Type="Number">1.23</Data></Cell>
<Cell ss:Formula="=TRUE()"><Data ss:Type="Boolean">1</Data></Cell>
<Cell ss:StyleID="s80"><Data ss:Type="Boolean">1</Data></Cell>
<Cell ss:Index="8" ss:Formula="=SUM(R[-1]C+R[-2]C)"><Data ss:Type="Number">22</Data></Cell>
<Cell ss:Index="11" ss:StyleID="s81"><Data ss:Type="String">Green</Data></Cell>
<Cell ss:StyleID="s82"><Data ss:Type="String">Green</Data></Cell>
<Cell ss:Index="14" ss:StyleID="s83"><Data ss:Type="String">Thin Line</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s84"><Data ss:Type="String">Test #3</Data></Cell>
<Cell><Data ss:Type="Number">2.34</Data></Cell>
<Cell ss:Formula="=FALSE()"><Data ss:Type="Boolean">0</Data></Cell>
<Cell><Data ss:Type="Boolean">0</Data></Cell>
<Cell ss:Index="8" ss:Formula="=SUM(MarksRange)"><Data ss:Type="Number">36</Data></Cell>
<Cell ss:Index="11" ss:StyleID="s85"><Data ss:Type="String">Blue</Data></Cell>
<Cell ss:StyleID="s86"><Data ss:Type="String">Blue</Data></Cell>
<Cell ss:Index="14" ss:StyleID="s87"><Data ss:Type="String">Thick Dash/Dot/Dot</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s88"><Data ss:Type="String">Test #3</Data></Cell>
<Cell><Data ss:Type="Number">3.45</Data></Cell>
<Cell ss:Index="11" ss:StyleID="s89"><Data ss:Type="String">Purple</Data></Cell>
<Cell ss:StyleID="s90"><Data ss:Type="String">Purple</Data></Cell>
<Cell ss:Index="14" ss:StyleID="s87"><Data ss:Type="String">Variant Thick Dash/Dot/Dot</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="13.5">
<Cell ss:Index="11" ss:StyleID="s91"><Data ss:Type="String">Pink</Data></Cell>
<Cell ss:StyleID="s92"><Data ss:Type="String">Pink</Data></Cell>
<Cell ss:Index="14" ss:StyleID="s93"><Data ss:Type="String">Thick Dash/Dot</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s80"><Data ss:Type="DateTime">1960-12-19T00:00:00.000</Data></Cell>
<Cell ss:Index="3" ss:StyleID="s94"><Data ss:Type="String">TOP</Data></Cell>
<Cell ss:Index="7"><Data ss:Type="Number">0</Data></Cell>
<Cell ss:Index="11" ss:StyleID="s95"><Data ss:Type="String">Brown</Data></Cell>
<Cell ss:StyleID="s74"><Data ss:Type="String">Brown</Data></Cell>
<Cell ss:Index="14" ss:StyleID="s96"><Data ss:Type="String">Thick Dash</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s97"><Data ss:Type="Number">1.5</Data></Cell>
<Cell ss:Index="7" ss:Formula="=12/0"><Data ss:Type="Error">#DIV/0!</Data></Cell>
<Cell ss:Index="14" ss:StyleID="s98"><Data ss:Type="String">Thick Line</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="13.5">
<Cell ss:Index="3" ss:StyleID="s99"><Data ss:Type="String">BOTTOM</Data></Cell>
<Cell ss:Index="14" ss:StyleID="s100"><Data ss:Type="String">Extra Thick Line</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s101"><Data ss:Type="DateTime">1899-12-31T02:30:00.000</Data></Cell>
<Cell ss:Index="6"><Data ss:Type="String">Мойва сушеная</Data><Comment
ss:Author="Mark"><ss:Data xmlns="http://www.w3.org/TR/REC-html40"><Font
html:Face="Tahoma" html:Size="9" html:Color="#000000">Tests for UTF-8 content</Font></ss:Data></Comment></Cell>
<Cell ss:Index="14" ss:StyleID="s102"><Data ss:Type="String">Double Line</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:Index="3" ss:StyleID="s103"><Data ss:Type="String">LEFT</Data></Cell>
<Cell ss:Index="6"><Data ss:Type="String">Ärendetext</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s104"><Data ss:Type="DateTime">1960-12-19T01:30:00.000</Data></Cell>
<Cell ss:Index="6"><Data ss:Type="String">Højde</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:Index="3" ss:StyleID="s105"><Data ss:Type="String">RIGHT</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="13.5"/>
<Row ss:AutoFitHeight="0" ss:Height="27.75">
<Cell ss:Index="2" ss:MergeAcross="1" ss:MergeDown="1"
ss:StyleID="m1867777004784"><Data ss:Type="String">BOX</Data></Cell>
<Cell ss:Index="5" ss:StyleID="s113"/>
<Cell ss:Index="7" ss:StyleID="s114"><Data ss:Type="String">Test Column 1</Data></Cell>
<Cell ss:StyleID="s114"/>
<Cell ss:StyleID="s115"/>
<Cell ss:StyleID="s116"/>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="27.75">
<Cell ss:Index="7" ss:StyleID="s114"/>
<Cell ss:StyleID="s114"><Data ss:Type="String">Test Column 2</Data></Cell>
<Cell ss:StyleID="s114"/>
<Cell ss:Index="11" ss:StyleID="s147"><Data ss:Type="String">Patterned</Data></Cell>
<Cell ss:StyleID="s118"><Data ss:Type="String">Patterned 2</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="27.75">
<Cell ss:Index="7" ss:StyleID="s114"/>
<Cell ss:StyleID="s114"/>
<Cell ss:StyleID="s114"><Data ss:Type="String">Test Column 3</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s62" ss:HRef="https://github.com/PHPOffice/PhpSpreadsheet"><Data
ss:Type="String">PhpSpreadsheet</Data></Cell>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s121"><Data ss:Type="String">Underline None</Data></Cell>
<Cell ss:StyleID="s121"/>
<Cell ss:MergeAcross="1" ss:MergeDown="4" ss:StyleID="s123"><Data
ss:Type="String">Rotate 90</Data></Cell>
<Cell ss:MergeAcross="1" ss:MergeDown="4" ss:StyleID="s125"><Data
ss:Type="String">Rotate 45</Data></Cell>
<Cell ss:MergeAcross="1" ss:MergeDown="4" ss:StyleID="s127"><Data
ss:Type="String">Rotate -90</Data></Cell>
<Cell ss:MergeAcross="1" ss:MergeDown="4" ss:StyleID="s129"><Data
ss:Type="String">Rotate -45</Data></Cell>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s130"><Data ss:Type="String">Underline 1</Data></Cell>
<Cell ss:StyleID="s131"><Data ss:Type="String">Subscript</Data></Cell>
<Cell ss:Index="11" ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s132"><Data ss:Type="String">Underline 2</Data></Cell>
<Cell ss:StyleID="s133"><Data ss:Type="String">Superscript</Data></Cell>
<Cell ss:Index="11" ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s134"><Data ss:Type="String">Underline 3</Data></Cell>
<Cell ss:StyleID="s121"/>
<Cell ss:Index="11" ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s135"><Data ss:Type="String">Underline 4</Data></Cell>
<Cell ss:StyleID="s121"/>
<Cell ss:Index="11" ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:MergeAcross="2" ss:StyleID="s136"><Data ss:Type="String">I don't know if Gnumeric supports Rich Text in the same way as Excel, And this row should be autofit height with text wrap</Data></Cell>
<Cell ss:StyleID="s136"/>
<Cell ss:StyleID="s136"/>
<Cell ss:StyleID="s136"/>
<Cell ss:StyleID="s136"/>
<Cell ss:StyleID="s136"/>
<Cell ss:StyleID="s136"/>
<Cell ss:StyleID="s136"/>
<Cell ss:StyleID="s136"/>
<Cell ss:StyleID="s136"/>
<Cell ss:StyleID="s136"/>
<Cell ss:StyleID="s136"/>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell ss:MergeAcross="2" ss:StyleID="s139"><Data ss:Type="String">Blue with underline</Data></Cell>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15" ss:Hidden="1">
<Cell ss:StyleID="s140"><Data ss:Type="Number">5</Data><NamedCell
ss:Name="goodname"/></Cell>
<Cell ss:StyleID="s121" ss:Formula="=goodname"><Data ss:Type="Number">5</Data></Cell>
<Cell ss:StyleID="s121" ss:Formula="=@badname"><Data ss:Type="Error">#NAME?</Data></Cell>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
</Row>
<Row ss:AutoFitHeight="0">
<Cell ss:StyleID="s121"><Data ss:Type="String">Hidden row above</Data></Cell>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
</Row>
<Row ss:AutoFitHeight="0">
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
</Row>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.72"/>
<Footer x:Margin="0.72"/>
<PageMargins x:Bottom="1.2" x:Left="0.72" x:Right="0.72" x:Top="1.2"/>
</PageSetup>
<Unsynced/>
<Print>
<ValidPrinterInfo/>
<HorizontalResolution>600</HorizontalResolution>
<VerticalResolution>600</VerticalResolution>
</Print>
<Selected/>
<TopRowVisible>3</TopRowVisible>
<Panes>
<Pane>
<Number>3</Number>
<ActiveRow>18</ActiveRow>
<ActiveCol>10</ActiveCol>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
<AllowFormatCells/>
<AllowSizeCols/>
<AllowSizeRows/>
<AllowInsertCols/>
<AllowInsertRows/>
<AllowInsertHyperlinks/>
<AllowDeleteCols/>
<AllowDeleteRows/>
<AllowSort/>
<AllowFilter/>
<AllowUsePivotTables/>
</WorksheetOptions>
</Worksheet>
<Worksheet ss:Name="Report Data">
<Table ss:ExpandedColumnCount="11" ss:ExpandedRowCount="16" x:FullColumns="1"
x:FullRows="1" ss:DefaultRowHeight="14.55">
<Column ss:AutoFitWidth="0" ss:Width="56.4" ss:Span="2"/>
<Column ss:Index="4" ss:AutoFitWidth="0" ss:Width="78.599999999999994"/>
<Column ss:AutoFitWidth="0" ss:Width="58.2"/>
<Column ss:AutoFitWidth="0" ss:Width="27.6"/>
<Column ss:AutoFitWidth="0" ss:Width="78.599999999999994"/>
<Column ss:AutoFitWidth="0" ss:Width="68.399999999999991"/>
<Column ss:AutoFitWidth="0" ss:Width="48.6"/>
<Column ss:Hidden="1" ss:AutoFitWidth="0" ss:Width="48.6"/>
<Column ss:AutoFitWidth="0" ss:Width="48.6"/>
<Row ss:AutoFitHeight="0" ss:Height="31.5">
<Cell ss:StyleID="s141"/>
<Cell ss:StyleID="s141"/>
<Cell ss:StyleID="s141"/>
<Cell ss:StyleID="s141"/>
<Cell ss:StyleID="s141"/>
<Cell ss:Index="7" ss:StyleID="s141"/>
<Cell ss:StyleID="s141"/>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="31.5">
<Cell ss:StyleID="s141"><Data ss:Type="String">Heading 1</Data></Cell>
<Cell ss:StyleID="s141"><Data ss:Type="String">Heading 2</Data></Cell>
<Cell ss:StyleID="s141"><Data ss:Type="String">Third Heading</Data></Cell>
<Cell ss:StyleID="s141"><Data ss:Type="String">Date Heading</Data></Cell>
<Cell ss:StyleID="s141"><Data ss:Type="String">Time Heading</Data></Cell>
<Cell ss:Index="7" ss:StyleID="s141"><Data ss:Type="String">Adjusted Date</Data></Cell>
<Cell ss:StyleID="s141"><Data ss:Type="String">Adjusted Number</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell><Data ss:Type="String">ABC</Data></Cell>
<Cell><Data ss:Type="Number">1</Data></Cell>
<Cell ss:StyleID="s142"><Data ss:Type="Number">1.1100000000000001</Data></Cell>
<Cell ss:StyleID="s143"><Data ss:Type="DateTime">2001-01-01T00:00:00.000</Data></Cell>
<Cell ss:StyleID="s144"><Data ss:Type="DateTime">1899-12-31T01:00:00.000</Data></Cell>
<Cell ss:Index="7" ss:StyleID="s143" ss:Formula="=RC[-3]-RC[-5]"><Data
ss:Type="DateTime">2000-12-31T00:00:00.000</Data></Cell>
<Cell ss:StyleID="s142" ss:Formula="=RC[-6]*RC[-5]"><Data ss:Type="Number">1.1100000000000001</Data></Cell>
<Cell ss:Index="10"><Data ss:Type="String">A</Data></Cell>
<Cell ss:Formula="=RC[-9]&amp;RC[-1]"><Data ss:Type="String">1A</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell><Data ss:Type="String">BCD</Data></Cell>
<Cell><Data ss:Type="Number">2</Data></Cell>
<Cell ss:StyleID="s142"><Data ss:Type="Number">2.2200000000000002</Data></Cell>
<Cell ss:StyleID="s143"><Data ss:Type="DateTime">2002-02-02T00:00:00.000</Data></Cell>
<Cell ss:StyleID="s144"><Data ss:Type="DateTime">1899-12-31T02:00:00.000</Data></Cell>
<Cell ss:Index="7" ss:StyleID="s143" ss:Formula="=RC[-3]-RC[-5]"><Data
ss:Type="DateTime">2002-01-31T00:00:00.000</Data></Cell>
<Cell ss:StyleID="s142" ss:Formula="=RC[-6]*RC[-5]"><Data ss:Type="Number">4.4400000000000004</Data></Cell>
<Cell ss:Index="10"><Data ss:Type="String">B</Data></Cell>
<Cell ss:Formula="=RC[-9]&amp;RC[-1]"><Data ss:Type="String">2B</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell><Data ss:Type="String">CDE</Data></Cell>
<Cell><Data ss:Type="Number">3</Data></Cell>
<Cell ss:StyleID="s142"><Data ss:Type="Number">3.33</Data></Cell>
<Cell ss:StyleID="s143"><Data ss:Type="DateTime">2003-03-03T00:00:00.000</Data></Cell>
<Cell ss:StyleID="s144"><Data ss:Type="DateTime">1899-12-31T03:00:00.000</Data></Cell>
<Cell ss:Index="7" ss:StyleID="s143" ss:Formula="=RC[-3]-RC[-5]"><Data
ss:Type="DateTime">2003-02-28T00:00:00.000</Data></Cell>
<Cell ss:StyleID="s142" ss:Formula="=RC[-6]*RC[-5]"><Data ss:Type="Number">9.99</Data></Cell>
<Cell ss:Index="10"><Data ss:Type="String">C</Data></Cell>
<Cell ss:Formula="=RC[-9]&amp;RC[-1]"><Data ss:Type="String">3C</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell><Data ss:Type="String">DEF</Data></Cell>
<Cell><Data ss:Type="Number">4</Data></Cell>
<Cell ss:StyleID="s142"><Data ss:Type="Number">4.4400000000000004</Data></Cell>
<Cell ss:StyleID="s143"><Data ss:Type="DateTime">2004-04-03T23:00:00.000</Data></Cell>
<Cell ss:StyleID="s144"><Data ss:Type="DateTime">1899-12-31T04:00:00.000</Data></Cell>
<Cell ss:Index="7" ss:StyleID="s143" ss:Formula="=RC[-3]-RC[-5]"><Data
ss:Type="DateTime">2004-03-30T23:00:00.000</Data></Cell>
<Cell ss:StyleID="s142" ss:Formula="=RC[-6]*RC[-5]"><Data ss:Type="Number">17.760000000000002</Data></Cell>
<Cell ss:Index="10"><Data ss:Type="String">D</Data></Cell>
<Cell ss:Formula="=RC[-9]&amp;RC[-1]"><Data ss:Type="String">4D</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell><Data ss:Type="String">EFG</Data></Cell>
<Cell><Data ss:Type="Number">5</Data></Cell>
<Cell ss:StyleID="s142"><Data ss:Type="Number">5.55</Data></Cell>
<Cell ss:StyleID="s143"><Data ss:Type="DateTime">2005-05-04T23:00:00.000</Data></Cell>
<Cell ss:StyleID="s144"><Data ss:Type="DateTime">1899-12-31T05:00:00.000</Data></Cell>
<Cell ss:Index="7" ss:StyleID="s143" ss:Formula="=RC[-3]-RC[-5]"><Data
ss:Type="DateTime">2005-04-29T23:00:00.000</Data></Cell>
<Cell ss:StyleID="s142" ss:Formula="=RC[-6]*RC[-5]"><Data ss:Type="Number">27.75</Data></Cell>
<Cell ss:Index="10"><Data ss:Type="String">E</Data></Cell>
<Cell ss:Formula="=RC[-9]&amp;RC[-1]"><Data ss:Type="String">5E</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell><Data ss:Type="String">FGH</Data></Cell>
<Cell><Data ss:Type="Number">6</Data></Cell>
<Cell ss:StyleID="s142"><Data ss:Type="Number">6.66</Data></Cell>
<Cell ss:StyleID="s143"><Data ss:Type="DateTime">2006-06-05T23:00:00.000</Data></Cell>
<Cell ss:StyleID="s144"><Data ss:Type="DateTime">1899-12-31T06:00:00.000</Data></Cell>
<Cell ss:Index="7" ss:StyleID="s143" ss:Formula="=RC[-3]-RC[-5]"><Data
ss:Type="DateTime">2006-05-30T23:00:00.000</Data></Cell>
<Cell ss:StyleID="s142" ss:Formula="=RC[-6]*RC[-5]"><Data ss:Type="Number">39.96</Data></Cell>
<Cell ss:Index="10"><Data ss:Type="String">F</Data></Cell>
<Cell ss:Formula="=RC[-9]&amp;RC[-1]"><Data ss:Type="String">6F</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell><Data ss:Type="String">GHI</Data></Cell>
<Cell><Data ss:Type="Number">7</Data></Cell>
<Cell ss:StyleID="s142"><Data ss:Type="Number">7.77</Data></Cell>
<Cell ss:StyleID="s143"><Data ss:Type="DateTime">2007-07-06T23:00:00.000</Data></Cell>
<Cell ss:StyleID="s144"><Data ss:Type="DateTime">1899-12-31T07:00:00.000</Data></Cell>
<Cell ss:Index="7" ss:StyleID="s143" ss:Formula="=RC[-3]-RC[-5]"><Data
ss:Type="DateTime">2007-06-29T23:00:00.000</Data></Cell>
<Cell ss:StyleID="s142" ss:Formula="=RC[-6]*RC[-5]"><Data ss:Type="Number">54.39</Data></Cell>
<Cell ss:Index="10"><Data ss:Type="String">G</Data></Cell>
<Cell ss:Formula="=RC[-9]&amp;RC[-1]"><Data ss:Type="String">7G</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell><Data ss:Type="String">HIJ</Data></Cell>
<Cell><Data ss:Type="Number">8</Data></Cell>
<Cell ss:StyleID="s142"><Data ss:Type="Number">8.8800000000000008</Data></Cell>
<Cell ss:StyleID="s143"><Data ss:Type="DateTime">2008-08-07T23:00:00.000</Data></Cell>
<Cell ss:StyleID="s144"><Data ss:Type="DateTime">1899-12-31T08:00:00.000</Data></Cell>
<Cell ss:Index="7" ss:StyleID="s143" ss:Formula="=RC[-3]-RC[-5]"><Data
ss:Type="DateTime">2008-07-30T23:00:00.000</Data></Cell>
<Cell ss:StyleID="s142" ss:Formula="=RC[-6]*RC[-5]"><Data ss:Type="Number">71.040000000000006</Data></Cell>
<Cell ss:Index="10"><Data ss:Type="String">H</Data></Cell>
<Cell ss:Formula="=RC[-9]&amp;RC[-1]"><Data ss:Type="String">8H</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell><Data ss:Type="String">IJK</Data></Cell>
<Cell><Data ss:Type="Number">9</Data></Cell>
<Cell ss:StyleID="s142"><Data ss:Type="Number">9.99</Data></Cell>
<Cell ss:StyleID="s143"><Data ss:Type="DateTime">2009-09-08T23:00:00.000</Data></Cell>
<Cell ss:StyleID="s144"><Data ss:Type="DateTime">1899-12-31T09:00:00.000</Data></Cell>
<Cell ss:Index="7" ss:StyleID="s143" ss:Formula="=RC[-3]-RC[-5]"><Data
ss:Type="DateTime">2009-08-30T23:00:00.000</Data></Cell>
<Cell ss:StyleID="s142" ss:Formula="=RC[-6]*RC[-5]"><Data ss:Type="Number">89.91</Data></Cell>
<Cell ss:Index="10"><Data ss:Type="String">I</Data></Cell>
<Cell ss:Formula="=RC[-9]&amp;RC[-1]"><Data ss:Type="String">9I</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell><Data ss:Type="String">JKL</Data></Cell>
<Cell><Data ss:Type="Number">10</Data></Cell>
<Cell ss:StyleID="s142"><Data ss:Type="Number">11.1</Data></Cell>
<Cell ss:StyleID="s143"><Data ss:Type="DateTime">2010-10-09T23:00:00.000</Data></Cell>
<Cell ss:StyleID="s144"><Data ss:Type="DateTime">1899-12-31T10:00:00.000</Data></Cell>
<Cell ss:Index="7" ss:StyleID="s143" ss:Formula="=RC[-3]-RC[-5]"><Data
ss:Type="DateTime">2010-09-29T23:00:00.000</Data></Cell>
<Cell ss:StyleID="s142" ss:Formula="=RC[-6]*RC[-5]"><Data ss:Type="Number">111</Data></Cell>
<Cell ss:Index="10"><Data ss:Type="String">J</Data></Cell>
<Cell ss:Formula="=RC[-9]&amp;RC[-1]"><Data ss:Type="String">10J</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell><Data ss:Type="String">KLM</Data></Cell>
<Cell><Data ss:Type="Number">11</Data></Cell>
<Cell ss:StyleID="s142"><Data ss:Type="Number">12.21</Data></Cell>
<Cell ss:StyleID="s143"><Data ss:Type="DateTime">2011-11-11T00:00:00.000</Data></Cell>
<Cell ss:StyleID="s144"><Data ss:Type="DateTime">1899-12-31T11:00:00.000</Data></Cell>
<Cell ss:Index="7" ss:StyleID="s143" ss:Formula="=RC[-3]-RC[-5]"><Data
ss:Type="DateTime">2011-10-31T00:00:00.000</Data></Cell>
<Cell ss:StyleID="s142" ss:Formula="=RC[-6]*RC[-5]"><Data ss:Type="Number">134.31</Data></Cell>
<Cell ss:Index="10"><Data ss:Type="String">K</Data></Cell>
<Cell ss:Formula="=RC[-9]&amp;RC[-1]"><Data ss:Type="String">11K</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell><Data ss:Type="String">LMN</Data></Cell>
<Cell><Data ss:Type="Number">12</Data></Cell>
<Cell ss:StyleID="s142"><Data ss:Type="Number">13.32</Data></Cell>
<Cell ss:StyleID="s143"><Data ss:Type="DateTime">2012-12-12T00:00:00.000</Data></Cell>
<Cell ss:StyleID="s144"><Data ss:Type="DateTime">1900-01-12T00:00:00.000</Data></Cell>
<Cell ss:Index="7" ss:StyleID="s143" ss:Formula="=RC[-3]-RC[-5]"><Data
ss:Type="DateTime">2012-11-30T00:00:00.000</Data></Cell>
<Cell ss:StyleID="s142" ss:Formula="=RC[-6]*RC[-5]"><Data ss:Type="Number">159.84</Data></Cell>
<Cell ss:Index="10"><Data ss:Type="String">L</Data></Cell>
<Cell ss:Formula="=RC[-9]&amp;RC[-1]"><Data ss:Type="String">12L</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15">
<Cell><Data ss:Type="String">ZYX</Data></Cell>
<Cell><Data ss:Type="Number">-1</Data></Cell>
<Cell ss:StyleID="s142"><Data ss:Type="Number">-1.1100000000000001</Data></Cell>
<Cell ss:StyleID="s143"><Data ss:Type="DateTime">1999-12-01T00:00:00.000</Data></Cell>
<Cell ss:StyleID="s144"><Data ss:Type="DateTime">1899-12-31T23:00:00.000</Data></Cell>
<Cell ss:Index="7" ss:StyleID="s143" ss:Formula="=RC[-3]-RC[-5]"><Data
ss:Type="DateTime">1999-12-02T00:00:00.000</Data></Cell>
<Cell ss:StyleID="s142" ss:Formula="=RC[-6]*RC[-5]"><Data ss:Type="Number">1.1100000000000001</Data></Cell>
<Cell ss:Index="10"><Data ss:Type="String">M</Data></Cell>
<Cell ss:Formula="=RC[-9]&amp;RC[-1]"><Data ss:Type="String">-1M</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
<Cell ss:StyleID="s121"/>
</Row>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.72"/>
<Footer x:Margin="0.72"/>
<PageMargins x:Bottom="1.2" x:Left="0.72" x:Right="0.72" x:Top="1.2"/>
</PageSetup>
<Unsynced/>
<Print>
<ValidPrinterInfo/>
<NoOrientation/>
<HorizontalResolution>600</HorizontalResolution>
<VerticalResolution>600</VerticalResolution>
</Print>
<Panes>
<Pane>
<Number>3</Number>
<ActiveCol>8</ActiveCol>
<RangeSelection>R1C9:R15C11</RangeSelection>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
<AllowFormatCells/>
<AllowSizeCols/>
<AllowSizeRows/>
<AllowInsertCols/>
<AllowInsertRows/>
<AllowInsertHyperlinks/>
<AllowDeleteCols/>
<AllowDeleteRows/>
<AllowSort/>
<AllowFilter/>
<AllowUsePivotTables/>
</WorksheetOptions>
</Worksheet>
</Workbook>

View File

@ -17,6 +17,8 @@ use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Borders;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font; use PhpOffice\PhpSpreadsheet\Style\Font;
use SimpleXMLElement; use SimpleXMLElement;
@ -32,13 +34,6 @@ class Xml extends BaseReader
*/ */
protected $styles = []; protected $styles = [];
/**
* Character set used in the file.
*
* @var string
*/
protected $charSet = 'UTF-8';
/** /**
* Create a new Excel2003XML Reader instance. * Create a new Excel2003XML Reader instance.
*/ */
@ -48,6 +43,56 @@ class Xml extends BaseReader
$this->securityScanner = XmlScanner::getInstance($this); $this->securityScanner = XmlScanner::getInstance($this);
} }
private $fileContents = '';
private static $mappings = [
'borderStyle' => [
'1continuous' => Border::BORDER_THIN,
'1dash' => Border::BORDER_DASHED,
'1dashdot' => Border::BORDER_DASHDOT,
'1dashdotdot' => Border::BORDER_DASHDOTDOT,
'1dot' => Border::BORDER_DOTTED,
'1double' => Border::BORDER_DOUBLE,
'2continuous' => Border::BORDER_MEDIUM,
'2dash' => Border::BORDER_MEDIUMDASHED,
'2dashdot' => Border::BORDER_MEDIUMDASHDOT,
'2dashdotdot' => Border::BORDER_MEDIUMDASHDOTDOT,
'2dot' => Border::BORDER_DOTTED,
'2double' => Border::BORDER_DOUBLE,
'3continuous' => Border::BORDER_THICK,
'3dash' => Border::BORDER_MEDIUMDASHED,
'3dashdot' => Border::BORDER_MEDIUMDASHDOT,
'3dashdotdot' => Border::BORDER_MEDIUMDASHDOTDOT,
'3dot' => Border::BORDER_DOTTED,
'3double' => Border::BORDER_DOUBLE,
],
'fillType' => [
'solid' => Fill::FILL_SOLID,
'gray75' => Fill::FILL_PATTERN_DARKGRAY,
'gray50' => Fill::FILL_PATTERN_MEDIUMGRAY,
'gray25' => Fill::FILL_PATTERN_LIGHTGRAY,
'gray125' => Fill::FILL_PATTERN_GRAY125,
'gray0625' => Fill::FILL_PATTERN_GRAY0625,
'horzstripe' => Fill::FILL_PATTERN_DARKHORIZONTAL, // horizontal stripe
'vertstripe' => Fill::FILL_PATTERN_DARKVERTICAL, // vertical stripe
'reversediagstripe' => Fill::FILL_PATTERN_DARKUP, // reverse diagonal stripe
'diagstripe' => Fill::FILL_PATTERN_DARKDOWN, // diagonal stripe
'diagcross' => Fill::FILL_PATTERN_DARKGRID, // diagoanl crosshatch
'thickdiagcross' => Fill::FILL_PATTERN_DARKTRELLIS, // thick diagonal crosshatch
'thinhorzstripe' => Fill::FILL_PATTERN_LIGHTHORIZONTAL,
'thinvertstripe' => Fill::FILL_PATTERN_LIGHTVERTICAL,
'thinreversediagstripe' => Fill::FILL_PATTERN_LIGHTUP,
'thindiagstripe' => Fill::FILL_PATTERN_LIGHTDOWN,
'thinhorzcross' => Fill::FILL_PATTERN_LIGHTGRID, // thin horizontal crosshatch
'thindiagcross' => Fill::FILL_PATTERN_LIGHTTRELLIS, // thin diagonal crosshatch
],
];
public static function xmlMappings(): array
{
return self::$mappings;
}
/** /**
* Can the current IReader read the file? * Can the current IReader read the file?
* *
@ -73,13 +118,10 @@ class Xml extends BaseReader
]; ];
// Open file // Open file
$this->openFile($pFilename); $data = file_get_contents($pFilename);
$fileHandle = $this->fileHandle;
// Read sample data (first 2 KB will do) // Why?
$data = fread($fileHandle, 2048); //$data = str_replace("'", '"', $data); // fix headers with single quote
fclose($fileHandle);
$data = str_replace("'", '"', $data); // fix headers with single quote
$valid = true; $valid = true;
foreach ($signature as $match) { foreach ($signature as $match) {
@ -92,9 +134,14 @@ class Xml extends BaseReader
} }
// Retrieve charset encoding // Retrieve charset encoding
if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/um', $data, $matches)) { if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/m', $data, $matches)) {
$this->charSet = strtoupper($matches[1]); $charSet = strtoupper($matches[1]);
if (1 == preg_match('/^ISO-8859-\d[\dL]?$/i', $charSet)) {
$data = StringHelper::convertEncoding($data, 'UTF-8', $charSet);
$data = preg_replace('/(<?xml.*encoding=[\'"]).*?([\'"].*?>)/um', '$1' . 'UTF-8' . '$2', $data, 1);
}
} }
$this->fileContents = $data;
return $valid; return $valid;
} }
@ -110,13 +157,14 @@ class Xml extends BaseReader
{ {
try { try {
$xml = simplexml_load_string( $xml = simplexml_load_string(
$this->securityScanner->scan(file_get_contents($pFilename)), $this->securityScanner->scan($this->fileContents ?: file_get_contents($pFilename)),
'SimpleXMLElement', 'SimpleXMLElement',
Settings::getLibXmlLoaderOptions() Settings::getLibXmlLoaderOptions()
); );
} catch (\Exception $e) { } catch (\Exception $e) {
throw new Exception('Cannot load invalid XML file: ' . $pFilename, 0, $e); throw new Exception('Cannot load invalid XML file: ' . $pFilename, 0, $e);
} }
$this->fileContents = '';
return $xml; return $xml;
} }
@ -144,7 +192,7 @@ class Xml extends BaseReader
$xml_ss = $xml->children($namespaces['ss']); $xml_ss = $xml->children($namespaces['ss']);
foreach ($xml_ss->Worksheet as $worksheet) { foreach ($xml_ss->Worksheet as $worksheet) {
$worksheet_ss = $worksheet->attributes($namespaces['ss']); $worksheet_ss = $worksheet->attributes($namespaces['ss']);
$worksheetNames[] = self::convertStringEncoding((string) $worksheet_ss['Name'], $this->charSet); $worksheetNames[] = (string) $worksheet_ss['Name'];
} }
return $worksheetNames; return $worksheetNames;
@ -160,6 +208,9 @@ class Xml extends BaseReader
public function listWorksheetInfo($pFilename) public function listWorksheetInfo($pFilename)
{ {
File::assertFile($pFilename); File::assertFile($pFilename);
if (!$this->canRead($pFilename)) {
throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
}
$worksheetInfo = []; $worksheetInfo = [];
@ -179,10 +230,9 @@ class Xml extends BaseReader
$tmpInfo['totalRows'] = 0; $tmpInfo['totalRows'] = 0;
$tmpInfo['totalColumns'] = 0; $tmpInfo['totalColumns'] = 0;
$tmpInfo['worksheetName'] = "Worksheet_{$worksheetID}";
if (isset($worksheet_ss['Name'])) { if (isset($worksheet_ss['Name'])) {
$tmpInfo['worksheetName'] = (string) $worksheet_ss['Name']; $tmpInfo['worksheetName'] = (string) $worksheet_ss['Name'];
} else {
$tmpInfo['worksheetName'] = "Worksheet_{$worksheetID}";
} }
if (isset($worksheet->Table->Row)) { if (isset($worksheet->Table->Row)) {
@ -238,53 +288,23 @@ class Xml extends BaseReader
private static function identifyFixedStyleValue($styleList, &$styleAttributeValue) private static function identifyFixedStyleValue($styleList, &$styleAttributeValue)
{ {
$returnValue = false;
$styleAttributeValue = strtolower($styleAttributeValue); $styleAttributeValue = strtolower($styleAttributeValue);
foreach ($styleList as $style) { foreach ($styleList as $style) {
if ($styleAttributeValue == strtolower($style)) { if ($styleAttributeValue == strtolower($style)) {
$styleAttributeValue = $style; $styleAttributeValue = $style;
$returnValue = true;
return true; break;
} }
} }
return false; return $returnValue;
}
/**
* pixel units to excel width units(units of 1/256th of a character width).
*
* @param float $pxs
*
* @return float
*/
protected static function pixel2WidthUnits($pxs)
{
$UNIT_OFFSET_MAP = [0, 36, 73, 109, 146, 182, 219];
$widthUnits = 256 * ($pxs / 7);
$widthUnits += $UNIT_OFFSET_MAP[($pxs % 7)];
return $widthUnits;
}
/**
* excel width units(units of 1/256th of a character width) to pixel units.
*
* @param float $widthUnits
*
* @return float
*/
protected static function widthUnits2Pixel($widthUnits)
{
$pixels = ($widthUnits / 256) * 7;
$offsetWidthUnits = $widthUnits % 256;
return $pixels + round($offsetWidthUnits / (256 / 7));
} }
protected static function hex2str($hex) protected static function hex2str($hex)
{ {
return chr(hexdec($hex[1])); return mb_chr((int) hexdec($hex[1]), 'UTF-8');
} }
/** /**
@ -308,51 +328,52 @@ class Xml extends BaseReader
$docProps = $spreadsheet->getProperties(); $docProps = $spreadsheet->getProperties();
if (isset($xml->DocumentProperties[0])) { if (isset($xml->DocumentProperties[0])) {
foreach ($xml->DocumentProperties[0] as $propertyName => $propertyValue) { foreach ($xml->DocumentProperties[0] as $propertyName => $propertyValue) {
$stringValue = (string) $propertyValue;
switch ($propertyName) { switch ($propertyName) {
case 'Title': case 'Title':
$docProps->setTitle(self::convertStringEncoding($propertyValue, $this->charSet)); $docProps->setTitle($stringValue);
break; break;
case 'Subject': case 'Subject':
$docProps->setSubject(self::convertStringEncoding($propertyValue, $this->charSet)); $docProps->setSubject($stringValue);
break; break;
case 'Author': case 'Author':
$docProps->setCreator(self::convertStringEncoding($propertyValue, $this->charSet)); $docProps->setCreator($stringValue);
break; break;
case 'Created': case 'Created':
$creationDate = strtotime($propertyValue); $creationDate = strtotime($stringValue);
$docProps->setCreated($creationDate); $docProps->setCreated($creationDate);
break; break;
case 'LastAuthor': case 'LastAuthor':
$docProps->setLastModifiedBy(self::convertStringEncoding($propertyValue, $this->charSet)); $docProps->setLastModifiedBy($stringValue);
break; break;
case 'LastSaved': case 'LastSaved':
$lastSaveDate = strtotime($propertyValue); $lastSaveDate = strtotime($stringValue);
$docProps->setModified($lastSaveDate); $docProps->setModified($lastSaveDate);
break; break;
case 'Company': case 'Company':
$docProps->setCompany(self::convertStringEncoding($propertyValue, $this->charSet)); $docProps->setCompany($stringValue);
break; break;
case 'Category': case 'Category':
$docProps->setCategory(self::convertStringEncoding($propertyValue, $this->charSet)); $docProps->setCategory($stringValue);
break; break;
case 'Manager': case 'Manager':
$docProps->setManager(self::convertStringEncoding($propertyValue, $this->charSet)); $docProps->setManager($stringValue);
break; break;
case 'Keywords': case 'Keywords':
$docProps->setKeywords(self::convertStringEncoding($propertyValue, $this->charSet)); $docProps->setKeywords($stringValue);
break; break;
case 'Description': case 'Description':
$docProps->setDescription(self::convertStringEncoding($propertyValue, $this->charSet)); $docProps->setDescription($stringValue);
break; break;
} }
@ -361,7 +382,7 @@ class Xml extends BaseReader
if (isset($xml->CustomDocumentProperties)) { if (isset($xml->CustomDocumentProperties)) {
foreach ($xml->CustomDocumentProperties[0] as $propertyName => $propertyValue) { foreach ($xml->CustomDocumentProperties[0] as $propertyName => $propertyValue) {
$propertyAttributes = $propertyValue->attributes($namespaces['dt']); $propertyAttributes = $propertyValue->attributes($namespaces['dt']);
$propertyName = preg_replace_callback('/_x([0-9a-z]{4})_/', ['self', 'hex2str'], $propertyName); $propertyName = preg_replace_callback('/_x([0-9a-f]{4})_/i', ['self', 'hex2str'], $propertyName);
$propertyType = Properties::PROPERTY_TYPE_UNKNOWN; $propertyType = Properties::PROPERTY_TYPE_UNKNOWN;
switch ((string) $propertyAttributes) { switch ((string) $propertyAttributes) {
case 'string': case 'string':
@ -413,7 +434,7 @@ class Xml extends BaseReader
$spreadsheet->createSheet(); $spreadsheet->createSheet();
$spreadsheet->setActiveSheetIndex($worksheetID); $spreadsheet->setActiveSheetIndex($worksheetID);
if (isset($worksheet_ss['Name'])) { if (isset($worksheet_ss['Name'])) {
$worksheetName = self::convertStringEncoding((string) $worksheet_ss['Name'], $this->charSet); $worksheetName = (string) $worksheet_ss['Name'];
// Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in
// formula cells... during the load, all formulae should be correct, and we're simply bringing // formula cells... during the load, all formulae should be correct, and we're simply bringing
// the worksheet name in line with the formula, not the reverse // the worksheet name in line with the formula, not the reverse
@ -476,7 +497,7 @@ class Xml extends BaseReader
} }
if (isset($cell_ss['HRef'])) { if (isset($cell_ss['HRef'])) {
$spreadsheet->getActiveSheet()->getCell($cellRange)->getHyperlink()->setUrl($cell_ss['HRef']); $spreadsheet->getActiveSheet()->getCell($cellRange)->getHyperlink()->setUrl((string) $cell_ss['HRef']);
} }
if ((isset($cell_ss['MergeAcross'])) || (isset($cell_ss['MergeDown']))) { if ((isset($cell_ss['MergeAcross'])) || (isset($cell_ss['MergeDown']))) {
@ -493,14 +514,15 @@ class Xml extends BaseReader
$spreadsheet->getActiveSheet()->mergeCells($cellRange); $spreadsheet->getActiveSheet()->mergeCells($cellRange);
} }
$cellIsSet = $hasCalculatedValue = false; $hasCalculatedValue = false;
$cellDataFormula = ''; $cellDataFormula = '';
if (isset($cell_ss['Formula'])) { if (isset($cell_ss['Formula'])) {
$cellDataFormula = $cell_ss['Formula']; $cellDataFormula = $cell_ss['Formula'];
$hasCalculatedValue = true; $hasCalculatedValue = true;
} }
if (isset($cell->Data)) { if (isset($cell->Data)) {
$cellValue = $cellData = $cell->Data; $cellData = $cell->Data;
$cellValue = (string) $cellData;
$type = DataType::TYPE_NULL; $type = DataType::TYPE_NULL;
$cellData_ss = $cellData->attributes($namespaces['ss']); $cellData_ss = $cellData->attributes($namespaces['ss']);
if (isset($cellData_ss['Type'])) { if (isset($cellData_ss['Type'])) {
@ -516,7 +538,6 @@ class Xml extends BaseReader
const TYPE_ERROR = 'e'; const TYPE_ERROR = 'e';
*/ */
case 'String': case 'String':
$cellValue = self::convertStringEncoding($cellValue, $this->charSet);
$type = DataType::TYPE_STRING; $type = DataType::TYPE_STRING;
break; break;
@ -540,6 +561,7 @@ class Xml extends BaseReader
break; break;
case 'Error': case 'Error':
$type = DataType::TYPE_ERROR; $type = DataType::TYPE_ERROR;
$hasCalculatedValue = false;
break; break;
} }
@ -555,7 +577,7 @@ class Xml extends BaseReader
if ($hasCalculatedValue) { if ($hasCalculatedValue) {
$spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setCalculatedValue($cellValue); $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setCalculatedValue($cellValue);
} }
$cellIsSet = $rowHasData = true; $rowHasData = true;
} }
if (isset($cell->Comment)) { if (isset($cell->Comment)) {
@ -566,15 +588,15 @@ class Xml extends BaseReader
} }
$node = $cell->Comment->Data->asXML(); $node = $cell->Comment->Data->asXML();
$annotation = strip_tags($node); $annotation = strip_tags($node);
$spreadsheet->getActiveSheet()->getComment($columnID . $rowID)->setAuthor(self::convertStringEncoding($author, $this->charSet))->setText($this->parseRichText($annotation)); $spreadsheet->getActiveSheet()->getComment($columnID . $rowID)->setAuthor($author)->setText($this->parseRichText($annotation));
} }
if (($cellIsSet) && (isset($cell_ss['StyleID']))) { if (isset($cell_ss['StyleID'])) {
$style = (string) $cell_ss['StyleID']; $style = (string) $cell_ss['StyleID'];
if ((isset($this->styles[$style])) && (!empty($this->styles[$style]))) { if ((isset($this->styles[$style])) && (!empty($this->styles[$style]))) {
if (!$spreadsheet->getActiveSheet()->cellExists($columnID . $rowID)) { //if (!$spreadsheet->getActiveSheet()->cellExists($columnID . $rowID)) {
$spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValue(null); // $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValue(null);
} //}
$spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($this->styles[$style]); $spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($this->styles[$style]);
} }
} }
@ -622,20 +644,11 @@ class Xml extends BaseReader
return $spreadsheet; return $spreadsheet;
} }
protected static function convertStringEncoding($string, $charset)
{
if ($charset != 'UTF-8') {
return StringHelper::convertEncoding($string, 'UTF-8', $charset);
}
return $string;
}
protected function parseRichText($is) protected function parseRichText($is)
{ {
$value = new RichText(); $value = new RichText();
$value->createText(self::convertStringEncoding($is, $this->charSet)); $value->createText($is);
return $value; return $value;
} }
@ -716,29 +729,41 @@ class Xml extends BaseReader
case 'WrapText': case 'WrapText':
$this->styles[$styleID]['alignment']['wrapText'] = true; $this->styles[$styleID]['alignment']['wrapText'] = true;
break;
case 'Rotate':
$this->styles[$styleID]['alignment']['textRotation'] = $styleAttributeValue;
break; break;
} }
} }
} }
private static $borderPositions = ['top', 'left', 'bottom', 'right'];
/** /**
* @param $styleID * @param $styleID
*/ */
private function parseStyleBorders($styleID, SimpleXMLElement $styleData, array $namespaces): void private function parseStyleBorders($styleID, SimpleXMLElement $styleData, array $namespaces): void
{ {
$diagonalDirection = '';
$borderPosition = '';
foreach ($styleData->Border as $borderStyle) { foreach ($styleData->Border as $borderStyle) {
$borderAttributes = $borderStyle->attributes($namespaces['ss']); $borderAttributes = $borderStyle->attributes($namespaces['ss']);
$thisBorder = []; $thisBorder = [];
$style = (string) $borderAttributes->Weight;
$style .= strtolower((string) $borderAttributes->LineStyle);
$thisBorder['borderStyle'] = self::$mappings['borderStyle'][$style] ?? Border::BORDER_NONE;
foreach ($borderAttributes as $borderStyleKey => $borderStyleValue) { foreach ($borderAttributes as $borderStyleKey => $borderStyleValue) {
switch ($borderStyleKey) { switch ($borderStyleKey) {
case 'LineStyle':
$thisBorder['borderStyle'] = Border::BORDER_MEDIUM;
break;
case 'Weight':
break;
case 'Position': case 'Position':
$borderPosition = strtolower($borderStyleValue); $borderStyleValue = strtolower((string) $borderStyleValue);
if (in_array($borderStyleValue, self::$borderPositions)) {
$borderPosition = $borderStyleValue;
} elseif ($borderStyleValue == 'diagonalleft') {
$diagonalDirection = $diagonalDirection ? Borders::DIAGONAL_BOTH : Borders::DIAGONAL_DOWN;
} elseif ($borderStyleValue == 'diagonalright') {
$diagonalDirection = $diagonalDirection ? Borders::DIAGONAL_BOTH : Borders::DIAGONAL_UP;
}
break; break;
case 'Color': case 'Color':
@ -748,27 +773,45 @@ class Xml extends BaseReader
break; break;
} }
} }
if (!empty($thisBorder)) { if ($borderPosition) {
if (($borderPosition == 'left') || ($borderPosition == 'right') || ($borderPosition == 'top') || ($borderPosition == 'bottom')) { $this->styles[$styleID]['borders'][$borderPosition] = $thisBorder;
$this->styles[$styleID]['borders'][$borderPosition] = $thisBorder; } elseif ($diagonalDirection) {
} $this->styles[$styleID]['borders']['diagonalDirection'] = $diagonalDirection;
$this->styles[$styleID]['borders']['diagonal'] = $thisBorder;
} }
} }
} }
private static $underlineStyles = [
Font::UNDERLINE_NONE,
Font::UNDERLINE_DOUBLE,
Font::UNDERLINE_DOUBLEACCOUNTING,
Font::UNDERLINE_SINGLE,
Font::UNDERLINE_SINGLEACCOUNTING,
];
private function parseStyleFontUnderline(string $styleID, string $styleAttributeValue): void
{
if (self::identifyFixedStyleValue(self::$underlineStyles, $styleAttributeValue)) {
$this->styles[$styleID]['font']['underline'] = $styleAttributeValue;
}
}
private function parseStyleFontVerticalAlign(string $styleID, string $styleAttributeValue): void
{
if ($styleAttributeValue == 'Superscript') {
$this->styles[$styleID]['font']['superscript'] = true;
}
if ($styleAttributeValue == 'Subscript') {
$this->styles[$styleID]['font']['subscript'] = true;
}
}
/** /**
* @param $styleID * @param $styleID
*/ */
private function parseStyleFont($styleID, SimpleXMLElement $styleAttributes): void private function parseStyleFont(string $styleID, SimpleXMLElement $styleAttributes): void
{ {
$underlineStyles = [
Font::UNDERLINE_NONE,
Font::UNDERLINE_DOUBLE,
Font::UNDERLINE_DOUBLEACCOUNTING,
Font::UNDERLINE_SINGLE,
Font::UNDERLINE_SINGLEACCOUNTING,
];
foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) { foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
$styleAttributeValue = (string) $styleAttributeValue; $styleAttributeValue = (string) $styleAttributeValue;
switch ($styleAttributeKey) { switch ($styleAttributeKey) {
@ -793,9 +836,11 @@ class Xml extends BaseReader
break; break;
case 'Underline': case 'Underline':
if (self::identifyFixedStyleValue($underlineStyles, $styleAttributeValue)) { $this->parseStyleFontUnderline($styleID, $styleAttributeValue);
$this->styles[$styleID]['font']['underline'] = $styleAttributeValue;
} break;
case 'VerticalAlign':
$this->parseStyleFontVerticalAlign($styleID, $styleAttributeValue);
break; break;
} }
@ -810,11 +855,17 @@ class Xml extends BaseReader
foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) { foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
switch ($styleAttributeKey) { switch ($styleAttributeKey) {
case 'Color': case 'Color':
$this->styles[$styleID]['fill']['color']['rgb'] = substr($styleAttributeValue, 1); $this->styles[$styleID]['fill']['endColor']['rgb'] = substr($styleAttributeValue, 1);
$this->styles[$styleID]['fill']['startColor']['rgb'] = substr($styleAttributeValue, 1);
break;
case 'PatternColor':
$this->styles[$styleID]['fill']['startColor']['rgb'] = substr($styleAttributeValue, 1);
break; break;
case 'Pattern': case 'Pattern':
$this->styles[$styleID]['fill']['fillType'] = strtolower($styleAttributeValue); $lcStyleAttributeValue = strtolower((string) $styleAttributeValue);
$this->styles[$styleID]['fill']['fillType'] = self::$mappings['fillType'][$lcStyleAttributeValue] ?? Fill::FILL_NONE;
break; break;
} }

View File

@ -127,7 +127,9 @@ class IOFactoryTest extends TestCase
['samples/templates/30template.xls', 'Xls', Reader\Xls::class], ['samples/templates/30template.xls', 'Xls', Reader\Xls::class],
['samples/templates/OOCalcTest.ods', 'Ods', Reader\Ods::class], ['samples/templates/OOCalcTest.ods', 'Ods', Reader\Ods::class],
['samples/templates/SylkTest.slk', 'Slk', Reader\Slk::class], ['samples/templates/SylkTest.slk', 'Slk', Reader\Slk::class],
['samples/templates/Excel2003XMLTest.xml', 'Xml', Reader\Xml::class], ['samples/templates/excel2003.xml', 'Xml', Reader\Xml::class],
// Following not readable by Excel.
//['samples/templates/Excel2003XMLTest.xml', 'Xml', Reader\Xml::class],
['samples/templates/46readHtml.html', 'Html', Reader\Html::class], ['samples/templates/46readHtml.html', 'Html', Reader\Html::class],
]; ];
} }

View File

@ -105,7 +105,7 @@ class CsvTest extends TestCase
{ {
return [ return [
[false, 'tests/data/Reader/Ods/data.ods'], [false, 'tests/data/Reader/Ods/data.ods'],
[false, 'tests/data/Reader/Xml/WithoutStyle.xml'], [false, 'samples/templates/excel2003.xml'],
[true, 'tests/data/Reader/CSV/enclosure.csv'], [true, 'tests/data/Reader/CSV/enclosure.csv'],
[true, 'tests/data/Reader/CSV/semicolon_separated.csv'], [true, 'tests/data/Reader/CSV/semicolon_separated.csv'],
[true, 'tests/data/Reader/CSV/contains_html.csv'], [true, 'tests/data/Reader/CSV/contains_html.csv'],

View File

@ -0,0 +1,14 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xml;
use PhpOffice\PhpSpreadsheet\Reader\IReadFilter;
/** Define a Read Filter class implementing IReadFilter */
class XmlFilter implements IReadFilter
{
public function readCell($column, $row, $worksheetName = '')
{
return $row !== 4;
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xml;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Reader\Xml;
use PHPUnit\Framework\TestCase;
class XmlInfoTest extends TestCase
{
public function testListNames(): void
{
$filename = __DIR__
. '/../../../..'
. '/samples/templates/excel2003.xml';
$reader = new Xml();
$names = $reader->listWorksheetNames($filename);
self::assertCount(2, $names);
self::assertEquals('Sample Data', $names[0]);
self::assertEquals('Report Data', $names[1]);
}
public function testListNamesInvalidFile(): void
{
$this->expectException(ReaderException::class);
$filename = __FILE__;
$reader = new Xml();
$names = $reader->listWorksheetNames($filename);
self::assertNotEquals($names, $names);
}
public function testListInfo(): void
{
$filename = __DIR__
. '/../../../..'
. '/samples/templates/excel2003.xml';
$reader = new Xml();
$info = $reader->listWorksheetInfo($filename);
$expected = [
[
'worksheetName' => 'Sample Data',
'lastColumnLetter' => 'J',
'lastColumnIndex' => 9,
'totalRows' => 31,
'totalColumns' => 10,
],
[
'worksheetName' => 'Report Data',
'lastColumnLetter' => 'I',
'lastColumnIndex' => 8,
'totalRows' => 15,
'totalColumns' => 9,
],
];
self::assertEquals($expected, $info);
}
public function testListInfoInvalidFile(): void
{
$this->expectException(ReaderException::class);
$filename = __FILE__;
$reader = new Xml();
$info = $reader->listWorksheetInfo($filename);
self::assertNotEquals($info, $info);
}
public function testLoadInvalidFile(): void
{
$this->expectException(ReaderException::class);
$filename = __FILE__;
$reader = new Xml();
$spreadsheet = $reader->load($filename);
self::assertNotEquals($spreadsheet, $spreadsheet);
}
}

View File

@ -0,0 +1,100 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xml;
use PhpOffice\PhpSpreadsheet\Reader\Xml;
use PHPUnit\Framework\TestCase;
class XmlLoadTest extends TestCase
{
public function testLoad(): void
{
$filename = __DIR__
. '/../../../..'
. '/samples/templates/excel2003.xml';
$reader = new Xml();
$spreadsheet = $reader->load($filename);
self::assertEquals(2, $spreadsheet->getSheetCount());
$sheet = $spreadsheet->getSheet(1);
self::assertEquals('Report Data', $sheet->getTitle());
self::assertEquals('BCD', $sheet->getCell('A4')->getValue());
$props = $spreadsheet->getProperties();
self::assertEquals('Mark Baker', $props->getCreator());
self::assertEquals('AbCd1234', $props->getCustomPropertyValue('my_API_Token'));
self::assertEquals('2', $props->getCustomPropertyValue('myאInt'));
$sheet = $spreadsheet->getSheet(0);
self::assertEquals('Sample Data', $sheet->getTitle());
self::assertEquals('Test String 1', $sheet->getCell('A1')->getValue());
self::assertEquals('Test with (") in string', $sheet->getCell('A4')->getValue());
self::assertEquals(22269, $sheet->getCell('A10')->getValue());
self::assertEquals('dd/mm/yyyy', $sheet->getCell('A10')->getStyle()->getNumberFormat()->getFormatCode());
self::assertEquals('19/12/1960', $sheet->getCell('A10')->getFormattedValue());
self::assertEquals(1.5, $sheet->getCell('A11')->getValue());
self::assertEquals('# ?0/??0', $sheet->getCell('A11')->getStyle()->getNumberFormat()->getFormatCode());
// Same pattern, same value, different display in Gnumeric vs Excel
//self::assertEquals('1 1/2', $sheet->getCell('A11')->getFormattedValue());
self::assertEquals('=B1+C1', $sheet->getCell('H1')->getValue());
self::assertEquals('=E2&F2', $sheet->getCell('J2')->getValue());
self::assertEquals('=SUM(C1:C4)', $sheet->getCell('I5')->getValue());
// property not yet supported
//self::assertFalse($sheet->getRowDimension(30)->getVisible());
$hyperlink = $sheet->getCell('A21');
self::assertEquals('PhpSpreadsheet', $hyperlink->getValue());
self::assertEquals('https://github.com/PHPOffice/PhpSpreadsheet', $hyperlink->getHyperlink()->getUrl());
}
public function testLoadFilter(): void
{
$filename = __DIR__
. '/../../../..'
. '/samples/templates/excel2003.xml';
$reader = new Xml();
$filter = new XmlFilter();
$reader->setReadFilter($filter);
$spreadsheet = $reader->load($filename);
self::assertEquals(2, $spreadsheet->getSheetCount());
$sheet = $spreadsheet->getSheet(1);
self::assertEquals('Report Data', $sheet->getTitle());
self::assertEquals('', $sheet->getCell('A4')->getValue());
$props = $spreadsheet->getProperties();
self::assertEquals('Mark Baker', $props->getCreator());
}
public function testLoadSelectedSheets(): void
{
$filename = __DIR__
. '/../../../..'
. '/samples/templates/excel2003.xml';
$reader = new Xml();
$reader->setLoadSheetsOnly(['Unknown Sheet', 'Report Data']);
$spreadsheet = $reader->load($filename);
self::assertEquals(1, $spreadsheet->getSheetCount());
$sheet = $spreadsheet->getSheet(0);
self::assertEquals('Report Data', $sheet->getTitle());
self::assertEquals('Third Heading', $sheet->getCell('C2')->getValue());
}
public function testLoadUnusableSample(): void
{
// Sample spreadsheet is not readable by Excel.
// But PhpSpreadsheet can load it except for coverage test.
//global $argv;
//if (in_array('--coverage-clover', $argv)) {
// self::markTestSkipped('Mysterious Travis coverage failure IOFactoryTest');
//}
$filename = __DIR__
. '/../../../..'
//. '/samples/templates/Excel2003XMLTest.xml';
. '/samples/templates/excel2003.short.bad.xml';
$reader = new Xml();
$spreadsheet = $reader->load($filename);
self::assertEquals(1, $spreadsheet->getSheetCount());
$sheet = $spreadsheet->getSheet(0);
self::assertEquals('Sample Data', $sheet->getTitle());
}
}

View File

@ -0,0 +1,113 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xml;
use PhpOffice\PhpSpreadsheet\Reader\Xml;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PHPUnit\Framework\TestCase;
class XmlStyleCoverageTest extends TestCase
{
/**
* @dataProvider providerBorderStyle
*/
public function testBorderStyle(string $style, string $expectedResult): void
{
$styles = Xml::XmlMappings();
$borders = $styles['borderStyle'];
self::assertEquals($expectedResult, $borders[$style]);
}
public function testBorderStyleCoverage(): void
{
$styles = Xml::XmlMappings();
$expected = $styles['borderStyle'];
$covered = [];
foreach ($expected as $key => $val) {
$covered[$key] = 0;
}
$tests = $this->providerBorderStyle();
foreach ($tests as $test) {
$covered[$test[0]] = 1;
}
foreach ($covered as $key => $val) {
self::assertEquals(1, $val, "Borderstyle $key not tested");
}
}
/**
* @dataProvider providerfillType
*/
public function testFillType(string $style, string $expectedResult): void
{
$styles = Xml::xmlMappings();
$borders = $styles['fillType'];
self::assertEquals($expectedResult, $borders[$style]);
}
public function testFillTypeCoverage(): void
{
$styles = Xml::XmlMappings();
$expected = $styles['fillType'];
$covered = [];
foreach ($expected as $key => $val) {
$covered[$key] = 0;
}
$tests = $this->providerfillType();
foreach ($tests as $test) {
$covered[$test[0]] = 1;
}
foreach ($covered as $key => $val) {
self::assertEquals(1, $val, "fillType $key not tested");
}
}
public function providerBorderStyle(): array
{
return [
['1continuous', Border::BORDER_THIN],
['1dash', Border::BORDER_DASHED],
['1dashdot', Border::BORDER_DASHDOT],
['1dashdotdot', Border::BORDER_DASHDOTDOT],
['1dot', Border::BORDER_DOTTED],
['1double', Border::BORDER_DOUBLE],
['2continuous', Border::BORDER_MEDIUM],
['2dash', Border::BORDER_MEDIUMDASHED],
['2dashdot', Border::BORDER_MEDIUMDASHDOT],
['2dashdotdot', Border::BORDER_MEDIUMDASHDOTDOT],
['2dot', Border::BORDER_DOTTED],
['2double', Border::BORDER_DOUBLE],
['3continuous', Border::BORDER_THICK],
['3dash', Border::BORDER_MEDIUMDASHED],
['3dashdot', Border::BORDER_MEDIUMDASHDOT],
['3dashdotdot', Border::BORDER_MEDIUMDASHDOTDOT],
['3dot', Border::BORDER_DOTTED],
['3double', Border::BORDER_DOUBLE],
];
}
public function providerFillType(): array
{
return [
['solid', Fill::FILL_SOLID],
['gray75', Fill::FILL_PATTERN_DARKGRAY],
['gray50', Fill::FILL_PATTERN_MEDIUMGRAY],
['gray25', Fill::FILL_PATTERN_LIGHTGRAY],
['gray125', Fill::FILL_PATTERN_GRAY125],
['gray0625', Fill::FILL_PATTERN_GRAY0625],
['horzstripe', Fill::FILL_PATTERN_DARKHORIZONTAL],
['vertstripe', Fill::FILL_PATTERN_DARKVERTICAL],
['reversediagstripe', Fill::FILL_PATTERN_DARKUP],
['diagstripe', Fill::FILL_PATTERN_DARKDOWN],
['diagcross', Fill::FILL_PATTERN_DARKGRID],
['thickdiagcross', Fill::FILL_PATTERN_DARKTRELLIS],
['thinhorzstripe', Fill::FILL_PATTERN_LIGHTHORIZONTAL],
['thinvertstripe', Fill::FILL_PATTERN_LIGHTVERTICAL],
['thinreversediagstripe', Fill::FILL_PATTERN_LIGHTUP],
['thindiagstripe', Fill::FILL_PATTERN_LIGHTDOWN],
['thinhorzcross', Fill::FILL_PATTERN_LIGHTGRID],
['thindiagcross', Fill::FILL_PATTERN_LIGHTTRELLIS],
];
}
}

View File

@ -0,0 +1,135 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xml;
use PhpOffice\PhpSpreadsheet\Reader\Xml;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Borders;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font;
use PHPUnit\Framework\TestCase;
class XmlStylesTest extends TestCase
{
public function testBorders(): void
{
$filename = __DIR__
. '/../../../..'
. '/samples/templates/excel2003.xml';
$reader = new Xml();
$spreadsheet = $reader->load($filename);
$sheet = $spreadsheet->getSheet(0);
self::assertEquals(Border::BORDER_MEDIUM, $sheet->getCell('C10')->getStyle()->getBorders()->getTop()->getBorderStyle());
self::assertEquals(Border::BORDER_NONE, $sheet->getCell('C10')->getStyle()->getBorders()->getRight()->getBorderStyle());
self::assertEquals(Border::BORDER_NONE, $sheet->getCell('C10')->getStyle()->getBorders()->getBottom()->getBorderStyle());
self::assertEquals(Border::BORDER_NONE, $sheet->getCell('C10')->getStyle()->getBorders()->getLeft()->getBorderStyle());
self::assertEquals(Border::BORDER_NONE, $sheet->getCell('C12')->getStyle()->getBorders()->getTop()->getBorderStyle());
self::assertEquals(Border::BORDER_NONE, $sheet->getCell('C12')->getStyle()->getBorders()->getRight()->getBorderStyle());
self::assertEquals(Border::BORDER_MEDIUM, $sheet->getCell('C12')->getStyle()->getBorders()->getBottom()->getBorderStyle());
self::assertEquals(Border::BORDER_NONE, $sheet->getCell('C12')->getStyle()->getBorders()->getLeft()->getBorderStyle());
self::assertEquals(Border::BORDER_NONE, $sheet->getCell('C14')->getStyle()->getBorders()->getTop()->getBorderStyle());
self::assertEquals(Border::BORDER_NONE, $sheet->getCell('C14')->getStyle()->getBorders()->getRight()->getBorderStyle());
self::assertEquals(Border::BORDER_NONE, $sheet->getCell('C14')->getStyle()->getBorders()->getBottom()->getBorderStyle());
self::assertEquals(Border::BORDER_MEDIUM, $sheet->getCell('C14')->getStyle()->getBorders()->getLeft()->getBorderStyle());
self::assertEquals(Border::BORDER_NONE, $sheet->getCell('C16')->getStyle()->getBorders()->getTop()->getBorderStyle());
self::assertEquals(Border::BORDER_MEDIUM, $sheet->getCell('C16')->getStyle()->getBorders()->getRight()->getBorderStyle());
self::assertEquals(Border::BORDER_NONE, $sheet->getCell('C16')->getStyle()->getBorders()->getBottom()->getBorderStyle());
self::assertEquals(Border::BORDER_NONE, $sheet->getCell('C16')->getStyle()->getBorders()->getLeft()->getBorderStyle());
self::assertEquals(Border::BORDER_THICK, $sheet->getCell('C18')->getStyle()->getBorders()->getTop()->getBorderStyle());
self::assertEquals(Color::COLOR_RED, $sheet->getCell('C18')->getStyle()->getBorders()->getTop()->getColor()->getARGB());
self::assertEquals(Border::BORDER_THICK, $sheet->getCell('C18')->getStyle()->getBorders()->getRight()->getBorderStyle());
self::assertEquals(Color::COLOR_YELLOW, $sheet->getCell('C18')->getStyle()->getBorders()->getRight()->getColor()->getARGB());
self::assertEquals(Border::BORDER_THICK, $sheet->getCell('C18')->getStyle()->getBorders()->getRight()->getBorderStyle());
self::assertEquals(Border::BORDER_NONE, $sheet->getCell('C18')->getStyle()->getBorders()->getBottom()->getBorderStyle());
self::assertEquals(Border::BORDER_NONE, $sheet->getCell('C18')->getStyle()->getBorders()->getLeft()->getBorderStyle());
self::assertEquals(Borders::DIAGONAL_BOTH, $sheet->getCell('E18')->getStyle()->getBorders()->getDiagonalDirection());
self::assertEquals(Borders::DIAGONAL_DOWN, $sheet->getCell('I18')->getStyle()->getBorders()->getDiagonalDirection());
self::assertEquals(Borders::DIAGONAL_UP, $sheet->getCell('J18')->getStyle()->getBorders()->getDiagonalDirection());
}
public function testFont(): void
{
$filename = __DIR__
. '/../../../..'
. '/samples/templates/excel2003.xml';
$reader = new Xml();
$spreadsheet = $reader->load($filename);
$sheet = $spreadsheet->getSheet(0);
self::assertEquals('FFFF0000', $sheet->getCell('A1')->getStyle()->getFont()->getColor()->getARGB());
self::assertEquals(Font::UNDERLINE_SINGLE, $sheet->getCell('A3')->getStyle()->getFont()->getUnderline());
self::assertTrue($sheet->getCell('E1')->getStyle()->getFont()->getBold());
self::assertTrue($sheet->getCell('E1')->getStyle()->getFont()->getItalic());
self::assertFalse($sheet->getCell('E2')->getStyle()->getFont()->getBold());
self::assertFalse($sheet->getCell('E2')->getStyle()->getFont()->getItalic());
self::assertEquals(Font::UNDERLINE_NONE, $sheet->getCell('E2')->getStyle()->getFont()->getUnderline());
self::assertTrue($sheet->getCell('E3')->getStyle()->getFont()->getBold());
self::assertFalse($sheet->getCell('E3')->getStyle()->getFont()->getItalic());
self::assertEquals(Font::UNDERLINE_NONE, $sheet->getCell('E3')->getStyle()->getFont()->getUnderline());
self::assertFalse($sheet->getCell('E4')->getStyle()->getFont()->getBold());
self::assertTrue($sheet->getCell('E4')->getStyle()->getFont()->getItalic());
self::assertEquals(Font::UNDERLINE_NONE, $sheet->getCell('E4')->getStyle()->getFont()->getUnderline());
self::assertTrue($sheet->getCell('F1')->getStyle()->getFont()->getBold());
self::assertFalse($sheet->getCell('F1')->getStyle()->getFont()->getItalic());
self::assertEquals(Font::UNDERLINE_NONE, $sheet->getCell('F1')->getStyle()->getFont()->getUnderline());
self::assertFalse($sheet->getCell('F2')->getStyle()->getFont()->getBold());
self::assertFalse($sheet->getCell('F2')->getStyle()->getFont()->getItalic());
self::assertEquals(Font::UNDERLINE_NONE, $sheet->getCell('F2')->getStyle()->getFont()->getUnderline());
self::assertTrue($sheet->getCell('F3')->getStyle()->getFont()->getBold());
self::assertTrue($sheet->getCell('F3')->getStyle()->getFont()->getItalic());
self::assertEquals(Font::UNDERLINE_NONE, $sheet->getCell('F3')->getStyle()->getFont()->getUnderline());
self::assertFalse($sheet->getCell('F4')->getStyle()->getFont()->getBold());
self::assertFalse($sheet->getCell('F4')->getStyle()->getFont()->getItalic());
self::assertEquals(Font::UNDERLINE_NONE, $sheet->getCell('F4')->getStyle()->getFont()->getUnderline());
self::assertEquals(45, $sheet->getCell('E22')->getStyle()->getAlignment()->getTextRotation());
self::assertEquals(-90, $sheet->getCell('G22')->getStyle()->getAlignment()->getTextRotation());
self::assertEquals(Border::BORDER_DOUBLE, $sheet->getCell('N13')->getStyle()->getBorders()->getBottom()->getBorderStyle());
self::assertEquals(Font::UNDERLINE_DOUBLE, $sheet->getCell('A24')->getStyle()->getFont()->getUnderline());
self::assertTrue($sheet->getCell('B23')->getStyle()->getFont()->getSubScript());
self::assertTrue($sheet->getCell('B24')->getStyle()->getFont()->getSuperScript());
}
public function testFill(): void
{
$filename = __DIR__
. '/../../../..'
. '/samples/templates/excel2003.xml';
$reader = new Xml();
$spreadsheet = $reader->load($filename);
$sheet = $spreadsheet->getSheet(0);
self::assertEquals(Fill::FILL_PATTERN_DARKHORIZONTAL, $sheet->getCell('K19')->getStyle()->getFill()->getFillType());
self::assertEquals('FF00CCFF', $sheet->getCell('K19')->getStyle()->getFill()->getEndColor()->getARGB());
self::assertEquals(Color::COLOR_BLUE, $sheet->getCell('K19')->getStyle()->getFill()->getStartColor()->getARGB());
self::assertEquals(Fill::FILL_PATTERN_GRAY0625, $sheet->getCell('L19')->getStyle()->getFill()->getFillType());
self::assertEquals(Color::COLOR_RED, $sheet->getCell('L19')->getStyle()->getFill()->getEndColor()->getARGB());
self::assertEquals(Color::COLOR_YELLOW, $sheet->getCell('L19')->getStyle()->getFill()->getStartColor()->getARGB());
self::assertEquals(Fill::FILL_SOLID, $sheet->getCell('K3')->getStyle()->getFill()->getFillType());
self::assertEquals(Color::COLOR_RED, $sheet->getCell('K3')->getStyle()->getFill()->getEndColor()->getARGB());
}
public function testAlignment(): void
{
$filename = __DIR__
. '/../../../..'
. '/samples/templates/excel2003.xml';
$reader = new Xml();
$spreadsheet = $reader->load($filename);
$sheet = $spreadsheet->getSheet(0);
self::assertEquals(45, $sheet->getCell('E22')->getStyle()->getAlignment()->getTextRotation());
self::assertEquals(-90, $sheet->getCell('G22')->getStyle()->getAlignment()->getTextRotation());
self::assertEquals(Alignment::HORIZONTAL_CENTER, $sheet->getCell('N2')->getStyle()->getAlignment()->getHorizontal());
self::assertEquals(Alignment::HORIZONTAL_RIGHT, $sheet->getCell('N3')->getStyle()->getAlignment()->getHorizontal());
self::assertEquals(Alignment::VERTICAL_TOP, $sheet->getCell('K19')->getStyle()->getAlignment()->getVertical());
}
}

View File

@ -1,6 +1,6 @@
<?php <?php
namespace PhpOffice\PhpSpreadsheetTests\Reader; namespace PhpOffice\PhpSpreadsheetTests\Reader\Xml;
use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Reader\Xml; use PhpOffice\PhpSpreadsheet\Reader\Xml;
@ -34,23 +34,17 @@ class XmlTest extends TestCase
/** /**
* Check if it can read XML Hyperlink correctly. * Check if it can read XML Hyperlink correctly.
*/ */
public function testReadHyperlinks(): void public function testHyperlinksAltCharset(): void
{ {
$reader = new Xml(); $reader = new Xml();
$spreadsheet = $reader->load('samples/templates/Excel2003XMLTest.xml'); $spreadsheet = $reader->load('tests/data/Reader/Xml/excel2003.iso8859-1.xml');
$firstSheet = $spreadsheet->getSheet(0); $firstSheet = $spreadsheet->getSheet(0);
self::assertSame('Voilà', $spreadsheet->getActiveSheet()->getCell('A1')->getValue());
$hyperlink = $firstSheet->getCell('L1'); $hyperlink = $firstSheet->getCell('A2');
self::assertEquals(DataType::TYPE_STRING, $hyperlink->getDataType()); self::assertEquals(DataType::TYPE_STRING, $hyperlink->getDataType());
self::assertEquals('PhpSpreadsheet', $hyperlink->getValue()); self::assertEquals('PhpSpreadsheet', $hyperlink->getValue());
self::assertEquals('https://phpspreadsheet.readthedocs.io', $hyperlink->getHyperlink()->getUrl()); self::assertEquals('https://phpspreadsheet.readthedocs.io', $hyperlink->getHyperlink()->getUrl());
} }
public function testReadWithoutStyle(): void
{
$reader = new Xml();
$spreadsheet = $reader->load('tests/data/Reader/Xml/WithoutStyle.xml');
self::assertSame('Test String 1', $spreadsheet->getActiveSheet()->getCell('A1')->getValue());
}
} }

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<Author>Owen Leibman</Author>
<LastAuthor>Owen Leibman</LastAuthor>
<Created>2020-06-20T18:12:18Z</Created>
<Version>16.00</Version>
</DocumentProperties>
<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
<AllowPNG/>
</OfficeDocumentSettings>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>8964</WindowHeight>
<WindowWidth>23040</WindowWidth>
<WindowTopX>32767</WindowTopX>
<WindowTopY>32767</WindowTopY>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
<Borders/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="11" ss:Color="#000000"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s62" ss:Name="Hyperlink">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="11" ss:Color="#0563C1"
ss:Underline="Single"/>
</Style>
</Styles>
<Worksheet ss:Name="Sheet1">
<Table ss:ExpandedColumnCount="1" ss:ExpandedRowCount="2" x:FullColumns="1"
x:FullRows="1" ss:DefaultRowHeight="14.4">
<Row>
<Cell><Data ss:Type="String">Voilà</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s62" ss:HRef="https://phpspreadsheet.readthedocs.io"><Data
ss:Type="String">PhpSpreadsheet</Data></Cell>
</Row>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<Selected/>
<Panes>
<Pane>
<Number>3</Number>
<ActiveRow>2</ActiveRow>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
</Workbook>