2008-02-24 13:18:34 +00:00
|
|
|
{
|
|
|
|
xlsxooxml.pas
|
|
|
|
|
|
|
|
Writes an OOXML (Office Open XML) document
|
|
|
|
|
|
|
|
An OOXML document is a compressed ZIP file with the following files inside:
|
|
|
|
|
2009-02-02 09:58:51 +00:00
|
|
|
[Content_Types].xml -
|
2014-07-08 22:02:13 +00:00
|
|
|
_rels/.rels -
|
|
|
|
xl/_rels\workbook.xml.rels -
|
|
|
|
xl/workbook.xml - Global workbook data and list of worksheets
|
|
|
|
xl/styles.xml -
|
|
|
|
xl/sharedStrings.xml -
|
|
|
|
xl/worksheets\sheet1.xml - Contents of each worksheet
|
2008-02-24 13:18:34 +00:00
|
|
|
...
|
2014-07-08 22:02:13 +00:00
|
|
|
xl/worksheets\sheetN.xml
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
Specifications obtained from:
|
|
|
|
|
|
|
|
http://openxmldeveloper.org/default.aspx
|
|
|
|
|
2014-07-15 13:31:45 +00:00
|
|
|
also:
|
|
|
|
http://office.microsoft.com/en-us/excel-help/excel-specifications-and-limits-HP010073849.aspx#BMworksheetworkbook
|
|
|
|
|
2015-01-23 21:54:23 +00:00
|
|
|
AUTHORS: Felipe Monteiro de Carvalho, Reinier Olislagers, Werner Pamler
|
2008-02-24 13:18:34 +00:00
|
|
|
}
|
2015-01-23 21:54:23 +00:00
|
|
|
|
2008-02-24 13:18:34 +00:00
|
|
|
unit xlsxooxml;
|
|
|
|
|
|
|
|
{$ifdef fpc}
|
2015-05-01 15:14:25 +00:00
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
// {$mode delphi}
|
2008-02-24 13:18:34 +00:00
|
|
|
{$endif}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
2009-02-02 09:58:51 +00:00
|
|
|
Classes, SysUtils,
|
2014-07-24 22:22:26 +00:00
|
|
|
laz2_xmlread, laz2_DOM,
|
|
|
|
AVL_Tree,
|
2015-01-17 22:57:23 +00:00
|
|
|
{$IF FPC_FULLVERSION >= 20701}
|
|
|
|
zipper,
|
|
|
|
{$ELSE}
|
|
|
|
fpszipper,
|
|
|
|
{$ENDIF}
|
2015-05-28 20:08:24 +00:00
|
|
|
fpsTypes, fpSpreadsheet, fpsUtils, fpsReaderWriter, fpsNumFormat, fpsPalette,
|
2015-02-25 09:43:37 +00:00
|
|
|
fpsxmlcommon, xlsCommon;
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
|
2014-07-24 22:22:26 +00:00
|
|
|
{ TsSpreadOOXMLReader }
|
|
|
|
|
2014-07-25 16:31:34 +00:00
|
|
|
TsSpreadOOXMLReader = class(TsSpreadXMLReader)
|
2014-07-24 22:22:26 +00:00
|
|
|
private
|
2014-07-26 21:18:49 +00:00
|
|
|
FDateMode: TDateMode;
|
2014-07-24 22:22:26 +00:00
|
|
|
FPointSeparatorSettings: TFormatSettings;
|
|
|
|
FSharedStrings: TStringList;
|
2014-07-26 19:43:02 +00:00
|
|
|
FFillList: TFPList;
|
|
|
|
FBorderList: TFPList;
|
2015-02-23 22:51:42 +00:00
|
|
|
FHyperlinkList: TFPList;
|
2015-03-20 23:24:12 +00:00
|
|
|
FSharedFormulaBaseList: TFPList;
|
2015-05-28 20:08:24 +00:00
|
|
|
FPalette: TsPalette;
|
|
|
|
FThemeColors: array of TsColor;
|
2014-07-27 22:44:17 +00:00
|
|
|
FWrittenByFPS: Boolean;
|
2015-02-23 22:51:42 +00:00
|
|
|
procedure ApplyCellFormatting(ACell: PCell; XfIndex: Integer);
|
|
|
|
procedure ApplyHyperlinks(AWorksheet: TsWorksheet);
|
2015-01-31 18:42:22 +00:00
|
|
|
function FindCommentsFileName(ANode: TDOMNode): String;
|
2014-07-31 21:05:01 +00:00
|
|
|
procedure ReadBorders(ANode: TDOMNode);
|
2014-07-26 21:18:49 +00:00
|
|
|
procedure ReadCell(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
2014-07-26 19:43:02 +00:00
|
|
|
procedure ReadCellXfs(ANode: TDOMNode);
|
2014-08-06 15:49:04 +00:00
|
|
|
function ReadColor(ANode: TDOMNode): TsColor;
|
2014-08-03 21:21:31 +00:00
|
|
|
procedure ReadCols(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
2015-01-31 18:42:22 +00:00
|
|
|
procedure ReadComments(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
2014-07-26 21:18:49 +00:00
|
|
|
procedure ReadDateMode(ANode: TDOMNode);
|
2014-07-27 22:44:17 +00:00
|
|
|
procedure ReadFileVersion(ANode: TDOMNode);
|
2014-07-29 22:09:29 +00:00
|
|
|
procedure ReadFills(ANode: TDOMNode);
|
2015-07-09 11:10:15 +00:00
|
|
|
function ReadFont(ANode: TDOMNode): Integer;
|
2014-07-24 22:22:26 +00:00
|
|
|
procedure ReadFonts(ANode: TDOMNode);
|
2015-05-01 15:14:25 +00:00
|
|
|
procedure ReadHeaderFooter(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
2015-02-24 23:23:15 +00:00
|
|
|
procedure ReadHyperlinks(ANode: TDOMNode);
|
2014-09-10 16:48:34 +00:00
|
|
|
procedure ReadMergedCells(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
2014-07-26 19:43:02 +00:00
|
|
|
procedure ReadNumFormats(ANode: TDOMNode);
|
2015-04-29 20:00:07 +00:00
|
|
|
procedure ReadPageMargins(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
2015-05-01 15:14:25 +00:00
|
|
|
procedure ReadPageSetup(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
2014-07-29 21:02:14 +00:00
|
|
|
procedure ReadPalette(ANode: TDOMNode);
|
2015-04-30 21:55:55 +00:00
|
|
|
procedure ReadPrintOptions(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
2014-08-03 21:21:31 +00:00
|
|
|
procedure ReadRowHeight(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
2014-07-24 22:22:26 +00:00
|
|
|
procedure ReadSharedStrings(ANode: TDOMNode);
|
2014-09-09 11:42:20 +00:00
|
|
|
procedure ReadSheetFormatPr(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
2014-07-24 22:22:26 +00:00
|
|
|
procedure ReadSheetList(ANode: TDOMNode; AList: TStrings);
|
2014-08-03 22:55:58 +00:00
|
|
|
procedure ReadSheetViews(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
2014-08-06 15:49:04 +00:00
|
|
|
procedure ReadThemeElements(ANode: TDOMNode);
|
|
|
|
procedure ReadThemeColors(ANode: TDOMNode);
|
2014-07-26 21:18:49 +00:00
|
|
|
procedure ReadWorksheet(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
2014-07-24 22:22:26 +00:00
|
|
|
protected
|
2015-04-18 14:58:38 +00:00
|
|
|
FFirstNumFormatIndexInFile: Integer;
|
|
|
|
procedure AddBuiltinNumFormats; override;
|
2014-07-24 22:22:26 +00:00
|
|
|
public
|
|
|
|
constructor Create(AWorkbook: TsWorkbook); override;
|
|
|
|
destructor Destroy; override;
|
2015-02-04 19:50:50 +00:00
|
|
|
procedure ReadFromFile(AFileName: string); override;
|
|
|
|
procedure ReadFromStream(AStream: TStream); override;
|
2014-07-24 22:22:26 +00:00
|
|
|
end;
|
|
|
|
|
2008-02-24 13:18:34 +00:00
|
|
|
{ TsSpreadOOXMLWriter }
|
|
|
|
|
|
|
|
TsSpreadOOXMLWriter = class(TsCustomSpreadWriter)
|
2015-02-02 18:51:13 +00:00
|
|
|
private
|
2015-02-24 16:57:36 +00:00
|
|
|
FNext_rId: Integer;
|
2015-04-18 14:58:38 +00:00
|
|
|
FFirstNumFormatIndexInFile: Integer;
|
2008-02-24 13:18:34 +00:00
|
|
|
protected
|
2014-07-26 21:18:49 +00:00
|
|
|
FDateMode: TDateMode;
|
2012-04-27 08:01:15 +00:00
|
|
|
FPointSeparatorSettings: TFormatSettings;
|
2009-02-02 09:58:51 +00:00
|
|
|
FSharedStringsCount: Integer;
|
2015-01-23 21:54:23 +00:00
|
|
|
FFillList: array of PsCellFormat;
|
|
|
|
FBorderList: array of PsCellFormat;
|
2014-05-15 12:53:56 +00:00
|
|
|
protected
|
2015-04-18 14:58:38 +00:00
|
|
|
procedure AddBuiltinNumFormats; override;
|
2014-07-10 15:55:40 +00:00
|
|
|
procedure CreateStreams;
|
|
|
|
procedure DestroyStreams;
|
2015-01-23 21:54:23 +00:00
|
|
|
function FindBorderInList(AFormat: PsCellFormat): Integer;
|
|
|
|
function FindFillInList(AFormat: PsCellFormat): Integer;
|
2014-07-10 15:55:40 +00:00
|
|
|
function GetStyleIndex(ACell: PCell): Cardinal;
|
2014-07-13 22:09:27 +00:00
|
|
|
procedure ListAllBorders;
|
|
|
|
procedure ListAllFills;
|
2014-08-30 18:03:22 +00:00
|
|
|
function PrepareFormula(const AFormula: String): String;
|
2014-07-12 22:12:38 +00:00
|
|
|
procedure ResetStreams;
|
2014-07-13 22:09:27 +00:00
|
|
|
procedure WriteBorderList(AStream: TStream);
|
2014-08-04 19:11:17 +00:00
|
|
|
procedure WriteCols(AStream: TStream; AWorksheet: TsWorksheet);
|
2015-02-02 18:51:13 +00:00
|
|
|
procedure WriteComments(AWorksheet: TsWorksheet);
|
|
|
|
procedure WriteDimension(AStream: TStream; AWorksheet: TsWorksheet);
|
2014-07-13 22:09:27 +00:00
|
|
|
procedure WriteFillList(AStream: TStream);
|
2015-07-09 11:10:15 +00:00
|
|
|
procedure WriteFont(AStream: TStream; AFont: TsFont; ATag: String);
|
2014-07-12 22:12:38 +00:00
|
|
|
procedure WriteFontList(AStream: TStream);
|
2015-05-01 15:14:25 +00:00
|
|
|
procedure WriteHeaderFooter(AStream: TStream; AWorksheet: TsWorksheet);
|
2015-02-24 16:57:36 +00:00
|
|
|
procedure WriteHyperlinks(AStream: TStream; AWorksheet: TsWorksheet);
|
2014-09-10 16:48:34 +00:00
|
|
|
procedure WriteMergedCells(AStream: TStream; AWorksheet: TsWorksheet);
|
2014-07-14 20:47:53 +00:00
|
|
|
procedure WriteNumFormatList(AStream: TStream);
|
2014-07-29 21:02:14 +00:00
|
|
|
procedure WritePalette(AStream: TStream);
|
2015-04-29 20:00:07 +00:00
|
|
|
procedure WritePageMargins(AStream: TStream; AWorksheet: TsWorksheet);
|
2015-04-30 21:55:55 +00:00
|
|
|
procedure WritePageSetup(AStream: TStream; AWorksheet: TsWorksheet);
|
|
|
|
procedure WritePrintOptions(AStream: TStream; AWorksheet: TsWorksheet);
|
2014-08-04 19:11:17 +00:00
|
|
|
procedure WriteSheetData(AStream: TStream; AWorksheet: TsWorksheet);
|
2015-04-30 21:55:55 +00:00
|
|
|
procedure WriteSheetPr(AStream: TStream; AWorksheet: TsWorksheet);
|
2014-08-04 19:11:17 +00:00
|
|
|
procedure WriteSheetViews(AStream: TStream; AWorksheet: TsWorksheet);
|
2014-07-12 22:12:38 +00:00
|
|
|
procedure WriteStyleList(AStream: TStream; ANodeName: String);
|
2015-02-02 18:51:13 +00:00
|
|
|
procedure WriteVmlDrawings(AWorksheet: TsWorksheet);
|
|
|
|
procedure WriteWorksheet(AWorksheet: TsWorksheet);
|
|
|
|
procedure WriteWorksheetRels(AWorksheet: TsWorksheet);
|
2014-05-15 12:53:56 +00:00
|
|
|
protected
|
2009-02-02 09:58:51 +00:00
|
|
|
{ Streams with the contents of files }
|
2014-07-10 15:55:40 +00:00
|
|
|
FSContentTypes: TStream;
|
|
|
|
FSRelsRels: TStream;
|
|
|
|
FSWorkbook: TStream;
|
|
|
|
FSWorkbookRels: TStream;
|
|
|
|
FSStyles: TStream;
|
|
|
|
FSSharedStrings: TStream;
|
|
|
|
FSSharedStrings_complete: TStream;
|
|
|
|
FSSheets: array of TStream;
|
2015-02-02 18:51:13 +00:00
|
|
|
FSSheetRels: array of TStream;
|
|
|
|
FSComments: array of TStream;
|
|
|
|
FSVmlDrawings: array of TStream;
|
2011-08-29 10:55:22 +00:00
|
|
|
FCurSheetNum: Integer;
|
2014-05-15 12:53:56 +00:00
|
|
|
protected
|
2014-07-10 15:55:40 +00:00
|
|
|
{ Routines to write the files }
|
2014-04-23 22:29:32 +00:00
|
|
|
procedure WriteContent;
|
2015-02-02 18:51:13 +00:00
|
|
|
procedure WriteContentTypes;
|
|
|
|
procedure WriteGlobalFiles;
|
2014-05-15 12:53:56 +00:00
|
|
|
protected
|
2014-04-21 11:30:22 +00:00
|
|
|
{ Record writing methods }
|
|
|
|
//todo: add WriteDate
|
2014-08-08 19:00:27 +00:00
|
|
|
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
|
|
|
|
ACell: PCell); override;
|
2014-10-14 15:56:08 +00:00
|
|
|
procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal;
|
|
|
|
const AValue: Boolean; ACell: PCell); override;
|
2015-02-24 16:57:36 +00:00
|
|
|
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal;
|
|
|
|
const AValue: TDateTime; ACell: PCell); override;
|
2014-10-14 21:44:00 +00:00
|
|
|
procedure WriteError(AStream: TStream; const ARow, ACol: Cardinal;
|
|
|
|
const AValue: TsErrorValue; ACell: PCell); override;
|
2014-08-08 19:00:27 +00:00
|
|
|
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
2014-08-17 22:25:46 +00:00
|
|
|
ACell: PCell); override;
|
2014-08-08 19:00:27 +00:00
|
|
|
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
|
|
|
const AValue: string; ACell: PCell); override;
|
|
|
|
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal;
|
|
|
|
const AValue: double; ACell: PCell); override;
|
2014-05-15 12:53:56 +00:00
|
|
|
|
2008-02-24 13:18:34 +00:00
|
|
|
public
|
2014-04-23 22:29:32 +00:00
|
|
|
constructor Create(AWorkbook: TsWorkbook); override;
|
2008-02-24 13:18:34 +00:00
|
|
|
{ General writing methods }
|
|
|
|
procedure WriteStringToFile(AFileName, AString: string);
|
2014-04-23 22:29:32 +00:00
|
|
|
procedure WriteToFile(const AFileName: string; const AOverwriteExisting: Boolean = False); override;
|
|
|
|
procedure WriteToStream(AStream: TStream); override;
|
2008-02-24 13:18:34 +00:00
|
|
|
end;
|
|
|
|
|
2015-02-25 10:55:46 +00:00
|
|
|
|
|
|
|
TXlsxSettings = record
|
|
|
|
DateMode: TDateMode;
|
|
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
|
|
XlsxSettings: TXlsxSettings = (
|
|
|
|
DateMode: dm1900;
|
|
|
|
);
|
|
|
|
|
|
|
|
|
2008-02-24 13:18:34 +00:00
|
|
|
implementation
|
|
|
|
|
2014-07-10 20:43:46 +00:00
|
|
|
uses
|
2015-07-09 11:10:15 +00:00
|
|
|
variants, strutils, math, lazutf8, LazFileUtils, uriparser,
|
2015-06-01 19:58:26 +00:00
|
|
|
{%H-}fpsPatches, fpsStrings, fpsStreams, fpsNumFormatParser, fpsClasses;
|
2014-07-10 20:43:46 +00:00
|
|
|
|
2008-02-24 13:18:34 +00:00
|
|
|
const
|
|
|
|
{ OOXML general XML constants }
|
2015-06-01 19:58:26 +00:00
|
|
|
XML_HEADER = '<?xml version="1.0" encoding="utf-8" ?>';
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ OOXML Directory structure constants }
|
2014-07-08 22:02:13 +00:00
|
|
|
// Note: directory separators are always / because the .xlsx is a zip file which
|
|
|
|
// requires / instead of \, even on Windows; see
|
|
|
|
// http://www.pkware.com/documents/casestudies/APPNOTE.TXT
|
2015-06-01 19:58:26 +00:00
|
|
|
// 4.4.17.1 All slashes MUST be forward slashes '/' as opposed to backward slashes '\'
|
2015-01-31 18:42:22 +00:00
|
|
|
OOXML_PATH_TYPES = '[Content_Types].xml';
|
|
|
|
{%H-}OOXML_PATH_RELS = '_rels/';
|
|
|
|
OOXML_PATH_RELS_RELS = '_rels/.rels';
|
|
|
|
{%H-}OOXML_PATH_XL = 'xl/';
|
|
|
|
{%H-}OOXML_PATH_XL_RELS = 'xl/_rels/';
|
|
|
|
OOXML_PATH_XL_RELS_RELS = 'xl/_rels/workbook.xml.rels';
|
|
|
|
OOXML_PATH_XL_WORKBOOK = 'xl/workbook.xml';
|
|
|
|
OOXML_PATH_XL_STYLES = 'xl/styles.xml';
|
|
|
|
OOXML_PATH_XL_STRINGS = 'xl/sharedStrings.xml';
|
|
|
|
OOXML_PATH_XL_WORKSHEETS = 'xl/worksheets/';
|
|
|
|
OOXML_PATH_XL_WORKSHEETS_RELS = 'xl/worksheets/_rels/';
|
2015-02-02 18:51:13 +00:00
|
|
|
OOXML_PATH_XL_DRAWINGS = 'xl/drawings/';
|
2015-01-31 18:42:22 +00:00
|
|
|
OOXML_PATH_XL_THEME = 'xl/theme/theme1.xml';
|
2014-08-12 14:52:57 +00:00
|
|
|
|
|
|
|
{ OOXML schemas constants }
|
|
|
|
SCHEMAS_TYPES = 'http://schemas.openxmlformats.org/package/2006/content-types';
|
|
|
|
SCHEMAS_RELS = 'http://schemas.openxmlformats.org/package/2006/relationships';
|
|
|
|
SCHEMAS_DOC_RELS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships';
|
|
|
|
SCHEMAS_DOCUMENT = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument';
|
|
|
|
SCHEMAS_WORKSHEET = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet';
|
|
|
|
SCHEMAS_STYLES = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles';
|
|
|
|
SCHEMAS_STRINGS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings';
|
2015-02-02 18:51:13 +00:00
|
|
|
SCHEMAS_COMMENTS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments';
|
|
|
|
SCHEMAS_DRAWINGS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing';
|
2015-02-23 22:51:42 +00:00
|
|
|
SCHEMAS_HYPERLINKS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink';
|
2014-08-12 14:52:57 +00:00
|
|
|
SCHEMAS_SPREADML = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main';
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2015-01-31 18:42:22 +00:00
|
|
|
{ OOXML mime types constants }
|
2014-08-12 14:52:57 +00:00
|
|
|
{%H-}MIME_XML = 'application/xml';
|
|
|
|
MIME_RELS = 'application/vnd.openxmlformats-package.relationships+xml';
|
2015-02-07 22:04:03 +00:00
|
|
|
MIME_OFFICEDOCUMENT = 'application/vnd.openxmlformats-officedocument';
|
|
|
|
MIME_SPREADML = MIME_OFFICEDOCUMENT + '.spreadsheetml';
|
2014-08-12 14:52:57 +00:00
|
|
|
MIME_SHEET = MIME_SPREADML + '.sheet.main+xml';
|
|
|
|
MIME_WORKSHEET = MIME_SPREADML + '.worksheet+xml';
|
|
|
|
MIME_STYLES = MIME_SPREADML + '.styles+xml';
|
|
|
|
MIME_STRINGS = MIME_SPREADML + '.sharedStrings+xml';
|
2015-02-02 18:51:13 +00:00
|
|
|
MIME_COMMENTS = MIME_SPREADML + '.comments+xml';
|
2015-02-07 22:04:03 +00:00
|
|
|
MIME_VMLDRAWING = MIME_OFFICEDOCUMENT + '.vmlDrawing';
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2015-05-28 20:08:24 +00:00
|
|
|
LAST_PALETTE_INDEX = 63;
|
2014-08-06 21:52:20 +00:00
|
|
|
|
2014-07-26 19:43:02 +00:00
|
|
|
type
|
2014-07-29 22:09:29 +00:00
|
|
|
TFillListData = class
|
|
|
|
PatternType: String;
|
|
|
|
FgColor: TsColor;
|
|
|
|
BgColor: Tscolor;
|
|
|
|
end;
|
|
|
|
|
2014-07-31 21:05:01 +00:00
|
|
|
TBorderListData = class
|
|
|
|
Borders: TsCellBorders;
|
|
|
|
BorderStyles: TsCellBorderStyles;
|
|
|
|
end;
|
2015-01-24 00:36:10 +00:00
|
|
|
|
2015-02-23 22:51:42 +00:00
|
|
|
THyperlinkListData = class
|
|
|
|
ID: String;
|
|
|
|
CellRef: String;
|
2015-02-28 23:46:08 +00:00
|
|
|
Target: String;
|
|
|
|
TextMark: String;
|
2015-02-23 22:51:42 +00:00
|
|
|
Display: String;
|
|
|
|
Tooltip: String;
|
|
|
|
end;
|
|
|
|
|
2015-02-17 23:32:00 +00:00
|
|
|
const
|
|
|
|
PATTERN_TYPES: array [TsFillStyle] of string = (
|
|
|
|
'none', // fsNoFill
|
|
|
|
'solid', // fsSolidFill
|
|
|
|
'darkGray', // fsGray75
|
|
|
|
'mediumGray', // fsGray50
|
|
|
|
'lightGray', // fsGray25
|
|
|
|
'gray125', // fsGray12
|
|
|
|
'gray0625', // fsGray6,
|
|
|
|
'darkHorizontal', // fsStripeHor
|
|
|
|
'darkVertical', // fsStripeVert
|
|
|
|
'darkUp', // fsStripeDiagUp
|
|
|
|
'darkDown', // fsStripeDiagDown
|
|
|
|
'lightHorizontal', // fsThinStripeHor
|
|
|
|
'lightVertical', // fsThinStripeVert
|
|
|
|
'lightUp', // fsThinStripeDiagUp
|
|
|
|
'lightDown', // fsThinStripeDiagDown
|
|
|
|
'darkTrellis', // fsHatchDiag
|
|
|
|
'lightTrellis', // fsHatchThinDiag
|
|
|
|
'darkTellis', // fsHatchTickDiag
|
|
|
|
'lightGrid' // fsHatchThinHor
|
|
|
|
);
|
|
|
|
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{------------------------------------------------------------------------------}
|
|
|
|
{ TsSpreadOOXMLReader }
|
|
|
|
{------------------------------------------------------------------------------}
|
2014-07-24 22:22:26 +00:00
|
|
|
|
|
|
|
constructor TsSpreadOOXMLReader.Create(AWorkbook: TsWorkbook);
|
|
|
|
begin
|
|
|
|
inherited Create(AWorkbook);
|
2015-02-25 10:55:46 +00:00
|
|
|
FDateMode := XlsxSettings.DateMode;
|
2014-07-26 19:43:02 +00:00
|
|
|
|
2014-07-24 22:22:26 +00:00
|
|
|
FSharedStrings := TStringList.Create;
|
2014-07-26 19:43:02 +00:00
|
|
|
FFillList := TFPList.Create;
|
|
|
|
FBorderList := TFPList.Create;
|
2015-02-23 22:51:42 +00:00
|
|
|
FHyperlinkList := TFPList.Create;
|
2015-01-23 21:54:23 +00:00
|
|
|
FCellFormatList := TsCellFormatList.Create(true);
|
|
|
|
// Allow duplicates because xf indexes used in cell records cannot be found any more.
|
2015-03-20 23:24:12 +00:00
|
|
|
FSharedFormulaBaseList := TFPList.Create;
|
2014-07-26 19:43:02 +00:00
|
|
|
|
2015-05-28 20:08:24 +00:00
|
|
|
FPalette := TsPalette.Create;
|
|
|
|
|
2014-07-24 22:22:26 +00:00
|
|
|
FPointSeparatorSettings := DefaultFormatSettings;
|
|
|
|
FPointSeparatorSettings.DecimalSeparator := '.';
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TsSpreadOOXMLReader.Destroy;
|
2014-07-26 19:43:02 +00:00
|
|
|
var
|
|
|
|
j: Integer;
|
2014-07-24 22:22:26 +00:00
|
|
|
begin
|
2014-07-26 19:43:02 +00:00
|
|
|
for j := FFillList.Count-1 downto 0 do TObject(FFillList[j]).Free;
|
|
|
|
FFillList.Free;
|
|
|
|
|
|
|
|
for j := FBorderList.Count-1 downto 0 do TObject(FBorderList[j]).Free;
|
|
|
|
FBorderList.Free;
|
|
|
|
|
2015-02-23 22:51:42 +00:00
|
|
|
for j := FHyperlinkList.Count-1 downto 0 do TObject(FHyperlinkList[j]).Free;
|
|
|
|
FHyperlinkList.Free;
|
|
|
|
|
2015-07-09 11:10:15 +00:00
|
|
|
for j := FSharedStrings.Count-1 downto 0 do
|
|
|
|
if FSharedstrings.Objects[j] <> nil then FSharedStrings.Objects[j].Free;
|
2014-07-24 22:22:26 +00:00
|
|
|
FSharedStrings.Free;
|
2015-03-20 23:24:12 +00:00
|
|
|
FSharedFormulaBaseList.Free; // Don't free items, they are worksheet cells
|
2015-03-11 22:28:07 +00:00
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
// FCellFormatList, FNumFormatList and FFontList are destroyed by ancestor
|
2014-07-26 19:43:02 +00:00
|
|
|
|
2015-05-28 20:08:24 +00:00
|
|
|
FPalette.Free;
|
2014-07-24 22:22:26 +00:00
|
|
|
inherited Destroy;
|
|
|
|
end;
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Adds the built-in number formats to the NumFormatList.
|
|
|
|
-------------------------------------------------------------------------------}
|
|
|
|
procedure TsSpreadOOXMLReader.AddBuiltinNumFormats;
|
|
|
|
begin
|
|
|
|
FFirstNumFormatIndexInFile := 164;
|
|
|
|
AddBuiltInBiffFormats(
|
2015-04-19 22:03:33 +00:00
|
|
|
FNumFormatList, Workbook.FormatSettings, FFirstNumFormatIndexInFile-1
|
2015-04-18 14:58:38 +00:00
|
|
|
);
|
|
|
|
end;
|
|
|
|
|
2015-01-23 21:54:23 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ApplyCellFormatting(ACell: PCell; XFIndex: Integer);
|
2014-07-26 19:43:02 +00:00
|
|
|
var
|
2015-01-23 21:54:23 +00:00
|
|
|
i: Integer;
|
|
|
|
fmt: PsCellFormat;
|
2014-07-26 19:43:02 +00:00
|
|
|
begin
|
2015-01-23 21:54:23 +00:00
|
|
|
if Assigned(ACell) then begin
|
|
|
|
i := FCellFormatList.FindIndexOfID(XFIndex);
|
|
|
|
fmt := FCellFormatList.Items[i];
|
|
|
|
ACell^.FormatIndex := FWorkbook.AddCellFormat(fmt^);
|
|
|
|
end;
|
2014-07-26 19:43:02 +00:00
|
|
|
end;
|
|
|
|
|
2015-02-23 22:51:42 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ApplyHyperlinks(AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
hyperlinkData: THyperlinkListData;
|
|
|
|
r1, c1, r2, c2, r, c: Cardinal;
|
|
|
|
begin
|
|
|
|
for i:=0 to FHyperlinkList.Count-1 do
|
|
|
|
begin
|
|
|
|
hyperlinkData := THyperlinkListData(FHyperlinkList.Items[i]);
|
|
|
|
if pos(':', hyperlinkdata.CellRef) = 0 then
|
|
|
|
begin
|
|
|
|
ParseCellString(hyperlinkData.CellRef, r1, c1);
|
|
|
|
r2 := r1;
|
|
|
|
c2 := c1;
|
|
|
|
end else
|
|
|
|
ParseCellRangeString(hyperlinkData.CellRef, r1, c1, r2, c2);
|
|
|
|
|
|
|
|
for r := r1 to r2 do
|
|
|
|
for c := c1 to c2 do
|
|
|
|
with hyperlinkData do
|
2015-02-28 23:46:08 +00:00
|
|
|
if Target = '' then
|
|
|
|
AWorksheet.WriteHyperlink(r, c, '#'+TextMark, ToolTip)
|
|
|
|
else
|
|
|
|
if TextMark = '' then
|
|
|
|
AWorksheet.WriteHyperlink(r, c, Target, ToolTip)
|
|
|
|
else
|
|
|
|
AWorksheet.WriteHyperlink(r, c, Target+'#'+TextMark, ToolTip);
|
2015-02-23 22:51:42 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2015-01-31 18:42:22 +00:00
|
|
|
function TsSpreadOOXMLReader.FindCommentsFileName(ANode: TDOMNode): String;
|
|
|
|
var
|
|
|
|
s: String;
|
|
|
|
begin
|
|
|
|
while ANode <> nil do
|
|
|
|
begin
|
|
|
|
s := GetAttrValue(ANode, 'Type');
|
2015-02-02 18:51:13 +00:00
|
|
|
if s = SCHEMAS_COMMENTS then
|
2015-01-31 18:42:22 +00:00
|
|
|
begin
|
|
|
|
Result := ExtractFileName(GetAttrValue(ANode, 'Target'));
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
ANode := ANode.NextSibling;
|
|
|
|
end;
|
|
|
|
Result := '';
|
|
|
|
end;
|
|
|
|
|
2014-07-31 21:05:01 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadBorders(ANode: TDOMNode);
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
function ReadBorderStyle(ANode: TDOMNode;
|
|
|
|
out ABorderStyle: TsCellBorderStyle): Boolean;
|
2014-07-31 21:05:01 +00:00
|
|
|
var
|
|
|
|
s: String;
|
|
|
|
colorNode: TDOMNode;
|
|
|
|
nodeName: String;
|
|
|
|
begin
|
|
|
|
Result := false;
|
2014-10-14 15:56:08 +00:00
|
|
|
ABorderStyle.LineStyle := lsThin;
|
|
|
|
ABorderStyle.Color := scBlack;
|
2014-07-31 21:05:01 +00:00
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'style');
|
|
|
|
if s = '' then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
if s = 'thin' then
|
|
|
|
ABorderStyle.LineStyle := lsThin
|
|
|
|
else if s = 'medium' then
|
|
|
|
ABorderStyle.LineStyle := lsMedium
|
|
|
|
else if s = 'thick' then
|
|
|
|
ABorderStyle.LineStyle := lsThick
|
|
|
|
else if s = 'dotted' then
|
|
|
|
ABorderStyle.LineStyle := lsDotted
|
|
|
|
else if s = 'dashed' then
|
|
|
|
ABorderStyle.LineStyle := lsDashed
|
|
|
|
else if s = 'double' then
|
|
|
|
ABorderStyle.LineStyle := lsDouble
|
|
|
|
else if s = 'hair' then
|
|
|
|
ABorderStyle.LineStyle := lsHair;
|
|
|
|
|
|
|
|
colorNode := ANode.FirstChild;
|
|
|
|
while Assigned(colorNode) do begin
|
|
|
|
nodeName := colorNode.NodeName;
|
2014-10-14 15:56:08 +00:00
|
|
|
if nodeName = 'color' then
|
2014-08-06 15:49:04 +00:00
|
|
|
ABorderStyle.Color := ReadColor(colorNode);
|
2014-07-31 21:05:01 +00:00
|
|
|
colorNode := colorNode.NextSibling;
|
|
|
|
end;
|
|
|
|
Result := true;
|
|
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
|
|
borderNode: TDOMNode;
|
|
|
|
edgeNode: TDOMNode;
|
|
|
|
nodeName: String;
|
|
|
|
borders: TsCellBorders;
|
|
|
|
borderStyles: TsCellBorderStyles;
|
|
|
|
borderData: TBorderListData;
|
|
|
|
s: String;
|
|
|
|
|
|
|
|
begin
|
|
|
|
if ANode = nil then
|
|
|
|
exit;
|
|
|
|
|
2014-10-14 15:56:08 +00:00
|
|
|
borderStyles := DEFAULT_BORDERSTYLES;
|
2014-07-31 21:05:01 +00:00
|
|
|
borderNode := ANode.FirstChild;
|
|
|
|
while Assigned(borderNode) do begin
|
|
|
|
nodeName := borderNode.NodeName;
|
|
|
|
if nodeName = 'border' then begin
|
|
|
|
borders := [];
|
|
|
|
s := GetAttrValue(borderNode, 'diagonalUp');
|
|
|
|
if s = '1' then
|
|
|
|
Include(borders, cbDiagUp);
|
|
|
|
s := GetAttrValue(borderNode, 'diagonalDown');
|
|
|
|
if s = '1' then
|
|
|
|
Include(borders, cbDiagDown);
|
|
|
|
edgeNode := borderNode.FirstChild;
|
|
|
|
while Assigned(edgeNode) do begin
|
|
|
|
nodeName := edgeNode.NodeName;
|
|
|
|
if nodeName = 'left' then begin
|
|
|
|
if ReadBorderStyle(edgeNode, borderStyles[cbWest]) then
|
|
|
|
Include(borders, cbWest);
|
|
|
|
end
|
|
|
|
else if nodeName = 'right' then begin
|
|
|
|
if ReadBorderStyle(edgeNode, borderStyles[cbEast]) then
|
|
|
|
Include(borders, cbEast);
|
|
|
|
end
|
|
|
|
else if nodeName = 'top' then begin
|
|
|
|
if ReadBorderStyle(edgeNode, borderStyles[cbNorth]) then
|
|
|
|
Include(borders, cbNorth);
|
|
|
|
end
|
|
|
|
else if nodeName = 'bottom' then begin
|
|
|
|
if ReadBorderStyle(edgeNode, borderStyles[cbSouth]) then
|
|
|
|
Include(borders, cbSouth);
|
|
|
|
end
|
|
|
|
else if nodeName = 'diagonal' then begin
|
|
|
|
if ReadBorderStyle(edgeNode, borderStyles[cbDiagUp]) then
|
|
|
|
borderStyles[cbDiagDown] := borderStyles[cbDiagUp];
|
|
|
|
end;
|
|
|
|
edgeNode := edgeNode.NextSibling;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// add to border list
|
|
|
|
borderData := TBorderListData.Create;
|
|
|
|
borderData.Borders := borders;
|
|
|
|
borderData.BorderStyles := borderStyles;
|
|
|
|
FBorderList.Add(borderData);
|
|
|
|
end;
|
|
|
|
borderNode := borderNode.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2014-07-26 21:18:49 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadCell(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
|
|
|
var
|
2014-09-22 10:51:33 +00:00
|
|
|
addr, s: String;
|
2014-07-26 21:18:49 +00:00
|
|
|
rowIndex, colIndex: Cardinal;
|
2015-03-20 23:24:12 +00:00
|
|
|
cell, sharedformulabase: PCell;
|
2014-07-26 21:18:49 +00:00
|
|
|
datanode: TDOMNode;
|
|
|
|
dataStr: String;
|
|
|
|
formulaStr: String;
|
|
|
|
sstIndex: Integer;
|
|
|
|
number: Double;
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt: TsCellFormat;
|
2015-04-18 14:58:38 +00:00
|
|
|
numFmt: TsNumFormatParams = nil;
|
2015-07-09 11:10:15 +00:00
|
|
|
ms: TMemoryStream;
|
|
|
|
n: Integer;
|
|
|
|
rtp: TsRichTextParam;
|
|
|
|
richTextParams: TsRichTextParams;
|
2014-07-26 21:18:49 +00:00
|
|
|
begin
|
|
|
|
if ANode = nil then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
// get row and column address
|
2014-09-22 10:51:33 +00:00
|
|
|
addr := GetAttrValue(ANode, 'r'); // cell address, like 'A1'
|
|
|
|
ParseCellString(addr, rowIndex, colIndex);
|
2014-07-26 21:18:49 +00:00
|
|
|
|
|
|
|
// create cell
|
2014-08-18 15:50:41 +00:00
|
|
|
if FIsVirtualMode then
|
|
|
|
begin
|
2014-07-26 21:18:49 +00:00
|
|
|
InitCell(rowIndex, colIndex, FVirtualCell);
|
|
|
|
cell := @FVirtualCell;
|
|
|
|
end else
|
2015-03-14 22:48:38 +00:00
|
|
|
cell := AWorksheet.AddCell(rowIndex, colIndex);
|
2014-07-26 21:18:49 +00:00
|
|
|
|
|
|
|
// get style index
|
|
|
|
s := GetAttrValue(ANode, 's');
|
2015-01-23 21:54:23 +00:00
|
|
|
if s <> '' then begin
|
2014-07-27 10:17:14 +00:00
|
|
|
ApplyCellFormatting(cell, StrToInt(s));
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt := Workbook.GetCellFormat(cell^.FormatIndex);
|
|
|
|
end else
|
|
|
|
InitFormatRecord(fmt);
|
2014-07-26 21:18:49 +00:00
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
// get number format parameters
|
|
|
|
numFmt := Workbook.GetNumberFormat(fmt.NumberFormatIndex);
|
|
|
|
|
2014-07-26 21:18:49 +00:00
|
|
|
// get data
|
|
|
|
datanode := ANode.FirstChild;
|
|
|
|
dataStr := '';
|
|
|
|
formulaStr := '';
|
2014-08-18 15:50:41 +00:00
|
|
|
while Assigned(datanode) do
|
|
|
|
begin
|
2014-07-26 21:18:49 +00:00
|
|
|
if datanode.NodeName = 'v' then
|
|
|
|
dataStr := GetNodeValue(datanode)
|
|
|
|
else
|
2014-08-30 18:03:22 +00:00
|
|
|
if (boReadFormulas in FWorkbook.Options) and (datanode.NodeName = 'f') then
|
2014-08-18 15:50:41 +00:00
|
|
|
begin
|
|
|
|
// Formula to cell
|
2014-07-26 21:18:49 +00:00
|
|
|
formulaStr := GetNodeValue(datanode);
|
2014-09-04 09:19:45 +00:00
|
|
|
|
|
|
|
s := GetAttrValue(datanode, 't');
|
|
|
|
if s = 'shared' then
|
2014-08-18 15:50:41 +00:00
|
|
|
begin
|
2014-09-22 10:51:33 +00:00
|
|
|
// Shared formula
|
2014-09-04 09:19:45 +00:00
|
|
|
s := GetAttrValue(datanode, 'ref');
|
2015-03-20 23:24:12 +00:00
|
|
|
if (s <> '') then // This defines the shared formula range
|
2014-09-22 10:51:33 +00:00
|
|
|
begin
|
2015-03-20 23:24:12 +00:00
|
|
|
AWorksheet.WriteFormula(cell, FormulaStr);
|
|
|
|
// We store the shared formula base in the SharedFormulaBaseList.
|
|
|
|
// The list index is identical with the 'si' attribute of the node.
|
|
|
|
FSharedFormulaBaseList.Add(cell);
|
|
|
|
end else
|
|
|
|
begin
|
|
|
|
// Get index into the SharedFormulaBaseList
|
|
|
|
s := GetAttrValue(datanode, 'si');
|
|
|
|
if s <> '' then
|
|
|
|
begin
|
|
|
|
sharedformulabase := PCell(FSharedFormulaBaseList[StrToInt(s)]);
|
|
|
|
FWorksheet.CopyFormula(sharedformulabase, rowindex, colindex);
|
|
|
|
end;
|
2014-09-22 10:51:33 +00:00
|
|
|
end;
|
2014-09-04 09:19:45 +00:00
|
|
|
end
|
|
|
|
else
|
2014-09-22 10:51:33 +00:00
|
|
|
// "Normal" formula
|
2014-09-04 09:19:45 +00:00
|
|
|
AWorksheet.WriteFormula(cell, formulaStr);
|
2014-08-18 15:50:41 +00:00
|
|
|
end;
|
2014-07-26 21:18:49 +00:00
|
|
|
datanode := datanode.NextSibling;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// get data type
|
|
|
|
s := GetAttrValue(ANode, 't'); // "t" = data type
|
|
|
|
if (s = '') and (dataStr = '') then
|
2015-03-20 23:24:12 +00:00
|
|
|
begin
|
|
|
|
AWorksheet.WriteBlank(cell); // this erases the formula!!!
|
|
|
|
if formulaStr <> '' then cell^.FormulaValue := formulaStr;
|
|
|
|
end else
|
2014-07-26 21:18:49 +00:00
|
|
|
if (s = '') or (s = 'n') then begin
|
|
|
|
// Number or date/time, depending on format
|
|
|
|
number := StrToFloat(dataStr, FPointSeparatorSettings);
|
2015-04-18 14:58:38 +00:00
|
|
|
if IsDateTimeFormat(numFmt) then
|
|
|
|
begin
|
|
|
|
if not IsTimeIntervalFormat(numFmt) then // no correction of time origin for "time interval" format
|
2014-08-05 08:25:17 +00:00
|
|
|
number := ConvertExcelDateTimeToDateTime(number, FDateMode);
|
2015-04-18 14:58:38 +00:00
|
|
|
AWorksheet.WriteDateTime(cell, number);
|
2014-07-26 21:18:49 +00:00
|
|
|
end
|
|
|
|
else
|
|
|
|
AWorksheet.WriteNumber(cell, number);
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if s = 's' then begin
|
|
|
|
// String from shared strings table
|
|
|
|
sstIndex := StrToInt(dataStr);
|
2015-07-09 11:10:15 +00:00
|
|
|
// Standard cell, no rich-text parameters
|
|
|
|
if FSharedStrings.Objects[sstIndex] = nil then
|
|
|
|
AWorksheet.WriteUTF8Text(cell, FSharedStrings[sstIndex])
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
// Read rich-text parameters from the stream stored in the Objects of the stringlist
|
|
|
|
ms := TMemoryStream(FSharedStrings.Objects[sstIndex]);
|
|
|
|
ms.Position := 0;
|
|
|
|
n := ms.ReadWord; // Count of array elements
|
|
|
|
SetLength(richTextParams, 0);
|
|
|
|
while (n > 0) do begin
|
|
|
|
ms.ReadBuffer(rtp, SizeOf(TsRichTextParam));
|
|
|
|
// Consider only those richtext parameters with font different from cell font
|
|
|
|
if rtp.FontIndex <> fmt.FontIndex then begin
|
|
|
|
SetLength(richTextParams, Length(richTextParams)+1);
|
|
|
|
richTextParams[High(richTextParams)] := rtp;
|
|
|
|
end;
|
|
|
|
dec(n);
|
|
|
|
end;
|
|
|
|
AWorksheet.WriteUTF8Text(cell,
|
|
|
|
FSharedStrings[sstIndex],
|
|
|
|
richTextParams
|
|
|
|
);
|
|
|
|
end;
|
2014-07-26 21:18:49 +00:00
|
|
|
end else
|
|
|
|
if s = 'str' then
|
|
|
|
// literal string
|
|
|
|
AWorksheet.WriteUTF8Text(cell, datastr)
|
|
|
|
else
|
|
|
|
if s = 'b' then
|
|
|
|
// boolean
|
|
|
|
AWorksheet.WriteBoolValue(cell, dataStr='1')
|
|
|
|
else
|
|
|
|
if s = 'e' then begin
|
|
|
|
// error value
|
|
|
|
if dataStr = '#NULL!' then
|
|
|
|
AWorksheet.WriteErrorValue(cell, errEmptyIntersection)
|
|
|
|
else if dataStr = '#DIV/0!' then
|
|
|
|
AWorksheet.WriteErrorValue(cell, errDivideByZero)
|
|
|
|
else if dataStr = '#VALUE!' then
|
|
|
|
AWorksheet.WriteErrorValue(cell, errWrongType)
|
|
|
|
else if dataStr = '#REF!' then
|
|
|
|
AWorksheet.WriteErrorValue(cell, errIllegalRef)
|
|
|
|
else if dataStr = '#NAME?' then
|
|
|
|
AWorksheet.WriteErrorValue(cell, errWrongName)
|
|
|
|
else if dataStr = '#NUM!' then
|
|
|
|
AWorksheet.WriteErrorValue(cell, errOverflow)
|
|
|
|
else if dataStr = '#N/A' then
|
|
|
|
AWorksheet.WriteErrorValue(cell, errArgError)
|
|
|
|
else
|
2014-10-08 09:23:34 +00:00
|
|
|
raise Exception.Create(rsUnknownErrorType);
|
2014-07-26 21:18:49 +00:00
|
|
|
end else
|
2014-10-08 09:23:34 +00:00
|
|
|
raise Exception.Create(rsUnknownDataType);
|
2014-08-05 21:49:23 +00:00
|
|
|
|
|
|
|
if FIsVirtualMode then
|
|
|
|
Workbook.OnReadCellData(Workbook, rowIndex, colIndex, cell);
|
2014-07-26 21:18:49 +00:00
|
|
|
end;
|
|
|
|
|
2014-07-26 19:43:02 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadCellXfs(ANode: TDOMNode);
|
|
|
|
var
|
|
|
|
node: TDOMNode;
|
2014-08-02 19:21:23 +00:00
|
|
|
childNode: TDOMNode;
|
2014-07-26 19:43:02 +00:00
|
|
|
nodeName: String;
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt: TsCellFormat;
|
2015-02-17 23:32:00 +00:00
|
|
|
fs: TsFillStyle;
|
2014-07-26 19:43:02 +00:00
|
|
|
s1, s2: String;
|
2015-04-18 14:58:38 +00:00
|
|
|
numFmtIndex, fillIndex, borderIndex: Integer;
|
|
|
|
numFmtStr: String;
|
|
|
|
numFmtParams: TsNumFormatParams;
|
2015-01-23 21:54:23 +00:00
|
|
|
fillData: TFillListData;
|
|
|
|
borderData: TBorderListData;
|
2015-03-11 22:28:07 +00:00
|
|
|
fnt: TsFont;
|
2014-07-26 19:43:02 +00:00
|
|
|
begin
|
|
|
|
node := ANode.FirstChild;
|
2015-01-23 21:54:23 +00:00
|
|
|
while Assigned(node) do
|
|
|
|
begin
|
2014-07-26 19:43:02 +00:00
|
|
|
nodeName := node.NodeName;
|
2015-01-23 21:54:23 +00:00
|
|
|
if nodeName = 'xf' then
|
|
|
|
begin
|
|
|
|
InitFormatRecord(fmt);
|
|
|
|
fmt.ID := FCellFormatList.Count;
|
|
|
|
fmt.Name := '';
|
2014-07-26 19:43:02 +00:00
|
|
|
|
2014-08-09 11:14:22 +00:00
|
|
|
// strange: sometimes the "apply*" are missing. Therefore, it may be better
|
|
|
|
// to check against "<>0" instead of "=1"
|
2014-07-26 19:43:02 +00:00
|
|
|
s1 := GetAttrValue(node, 'numFmtId');
|
|
|
|
s2 := GetAttrValue(node, 'applyNumberFormat');
|
2015-01-23 21:54:23 +00:00
|
|
|
if (s1 <> '') and (s2 <> '0') then
|
|
|
|
begin
|
|
|
|
numFmtIndex := StrToInt(s1);
|
2015-04-18 14:58:38 +00:00
|
|
|
numFmtStr := NumFormatList[numFmtIndex];
|
|
|
|
if SameText(numFmtStr, 'General') then
|
|
|
|
numFmtParams := nil
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
fmt.NumberFormatIndex := Workbook.AddNumberFormat(numFmtStr);
|
|
|
|
numFmtParams := Workbook.GetNumberFormat(fmt.NumberFormatIndex);
|
|
|
|
end;
|
|
|
|
if numFmtParams <> nil then
|
|
|
|
begin
|
|
|
|
fmt.NumberFormat := numFmtParams.NumFormat;
|
|
|
|
fmt.NumberFormatStr := numFmtStr;
|
|
|
|
Include(fmt.UsedFormattingFields, uffNumberFormat);
|
|
|
|
end else
|
2015-01-23 21:54:23 +00:00
|
|
|
begin
|
2015-04-18 14:58:38 +00:00
|
|
|
fmt.NumberFormat := nfGeneral;
|
|
|
|
fmt.NumberFormatStr := '';
|
|
|
|
Exclude(fmt.UsedFormattingFields, uffNumberFormat);
|
2015-01-23 21:54:23 +00:00
|
|
|
end;
|
|
|
|
end;
|
2014-07-26 19:43:02 +00:00
|
|
|
|
|
|
|
s1 := GetAttrValue(node, 'fontId');
|
|
|
|
s2 := GetAttrValue(node, 'applyFont');
|
2015-01-23 21:54:23 +00:00
|
|
|
if (s1 <> '') and (s2 <> '0') then
|
|
|
|
begin
|
2015-03-11 22:28:07 +00:00
|
|
|
fnt := TsFont(FFontList.Items[StrToInt(s1)]);
|
|
|
|
fmt.FontIndex := Workbook.FindFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color);
|
|
|
|
if fmt.FontIndex = -1 then
|
|
|
|
fmt.FontIndex := Workbook.AddFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color);
|
|
|
|
if fmt.FontIndex > 0 then
|
2015-01-23 21:54:23 +00:00
|
|
|
Include(fmt.UsedFormattingFields, uffFont);
|
|
|
|
end;
|
2014-07-26 19:43:02 +00:00
|
|
|
|
|
|
|
s1 := GetAttrValue(node, 'fillId');
|
|
|
|
s2 := GetAttrValue(node, 'applyFill');
|
2015-01-23 21:54:23 +00:00
|
|
|
if (s1 <> '') and (s2 <> '0') then
|
|
|
|
begin
|
|
|
|
fillIndex := StrToInt(s1);
|
2015-05-01 15:14:25 +00:00
|
|
|
fillData := TFillListData(FFillList[fillIndex]);
|
2015-01-23 21:54:23 +00:00
|
|
|
if (fillData <> nil) and (fillData.PatternType <> 'none') then begin
|
2015-02-17 23:32:00 +00:00
|
|
|
fmt.Background.FgColor := fillData.FgColor;
|
|
|
|
fmt.Background.BgColor := fillData.BgColor;
|
|
|
|
for fs in TsFillStyle do
|
|
|
|
if SameText(fillData.PatternType, PATTERN_TYPES[fs]) then
|
|
|
|
begin
|
|
|
|
fmt.Background.Style := fs;
|
|
|
|
Include(fmt.UsedFormattingFields, uffBackground);
|
|
|
|
break;
|
|
|
|
end;
|
2015-01-23 21:54:23 +00:00
|
|
|
end;
|
|
|
|
end;
|
2014-07-26 19:43:02 +00:00
|
|
|
|
|
|
|
s1 := GetAttrValue(node, 'borderId');
|
|
|
|
s2 := GetAttrValue(node, 'applyBorder');
|
2015-01-23 21:54:23 +00:00
|
|
|
if (s1 <> '') and (s2 <> '0') then
|
|
|
|
begin
|
|
|
|
borderIndex := StrToInt(s1);
|
2015-05-01 15:14:25 +00:00
|
|
|
borderData := TBorderListData(FBorderList[borderIndex]);
|
2015-01-23 21:54:23 +00:00
|
|
|
if (borderData <> nil) then
|
|
|
|
begin
|
|
|
|
fmt.BorderStyles := borderData.BorderStyles;
|
|
|
|
fmt.Border := borderData.Borders;
|
|
|
|
end;
|
|
|
|
end;
|
2014-07-26 19:43:02 +00:00
|
|
|
|
2014-08-02 19:21:23 +00:00
|
|
|
s2 := GetAttrValue(node, 'applyAlignment');
|
2015-01-23 21:54:23 +00:00
|
|
|
if (s2 <> '0') and (s2 <> '') then begin
|
2014-08-02 19:21:23 +00:00
|
|
|
childNode := node.FirstChild;
|
|
|
|
while Assigned(childNode) do begin
|
|
|
|
nodeName := childNode.NodeName;
|
|
|
|
if nodeName = 'alignment' then begin
|
|
|
|
s1 := GetAttrValue(childNode, 'horizontal');
|
|
|
|
if s1 = 'left' then
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt.HorAlignment := haLeft
|
2014-08-02 19:21:23 +00:00
|
|
|
else
|
|
|
|
if s1 = 'center' then
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt.HorAlignment := haCenter
|
2014-08-02 19:21:23 +00:00
|
|
|
else
|
|
|
|
if s1 = 'right' then
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt.HorAlignment := haRight;
|
2014-08-02 19:21:23 +00:00
|
|
|
|
|
|
|
s1 := GetAttrValue(childNode, 'vertical');
|
|
|
|
if s1 = 'top' then
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt.VertAlignment := vaTop
|
2014-08-02 19:21:23 +00:00
|
|
|
else
|
|
|
|
if s1 = 'center' then
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt.VertAlignment := vaCenter
|
2014-08-02 19:21:23 +00:00
|
|
|
else
|
|
|
|
if s1 = 'bottom' then
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt.VertAlignment := vaBottom;
|
2014-08-02 19:21:23 +00:00
|
|
|
|
|
|
|
s1 := GetAttrValue(childNode, 'wrapText');
|
2014-08-10 15:16:58 +00:00
|
|
|
if (s1 <> '0') then
|
2015-01-23 21:54:23 +00:00
|
|
|
Include(fmt.UsedFormattingFields, uffWordWrap);
|
2014-08-02 19:21:23 +00:00
|
|
|
|
|
|
|
s1 := GetAttrValue(childNode, 'textRotation');
|
|
|
|
if s1 = '90' then
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt.TextRotation := rt90DegreeCounterClockwiseRotation
|
2014-08-02 19:21:23 +00:00
|
|
|
else
|
|
|
|
if s1 = '180' then
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt.TextRotation := rt90DegreeClockwiseRotation
|
2014-08-02 19:21:23 +00:00
|
|
|
else
|
|
|
|
if s1 = '255' then
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt.TextRotation := rtStacked
|
2014-08-02 19:21:23 +00:00
|
|
|
else
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt.TextRotation := trHorizontal;
|
2014-08-02 19:21:23 +00:00
|
|
|
end;
|
|
|
|
childNode := childNode.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
2015-01-23 21:54:23 +00:00
|
|
|
if fmt.FontIndex > 0 then
|
|
|
|
Include(fmt.UsedFormattingFields, uffFont);
|
|
|
|
if fmt.Border <> [] then
|
|
|
|
Include(fmt.UsedFormattingFields, uffBorder);
|
|
|
|
if fmt.HorAlignment <> haDefault then
|
|
|
|
Include(fmt.UsedFormattingFields, uffHorAlign);
|
|
|
|
if fmt.VertAlignment <> vaDefault then
|
|
|
|
Include(fmt.UsedFormattingFields, uffVertAlign);
|
|
|
|
if fmt.TextRotation <> trHorizontal then
|
|
|
|
Include(fmt.UsedFormattingFields, uffTextRotation);
|
|
|
|
FCellFormatList.Add(fmt);
|
2014-07-26 19:43:02 +00:00
|
|
|
end;
|
|
|
|
node := node.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2014-08-06 15:49:04 +00:00
|
|
|
function TsSpreadOOXMLReader.ReadColor(ANode: TDOMNode): TsColor;
|
|
|
|
var
|
|
|
|
s: String;
|
2015-05-28 20:08:24 +00:00
|
|
|
rgb: TsColor;
|
2014-08-06 15:49:04 +00:00
|
|
|
idx: Integer;
|
|
|
|
tint: Double;
|
2015-05-31 16:06:22 +00:00
|
|
|
n, i: Integer;
|
2014-08-06 15:49:04 +00:00
|
|
|
begin
|
|
|
|
Assert(ANode <> nil);
|
|
|
|
|
2015-02-17 23:32:00 +00:00
|
|
|
s := GetAttrValue(ANode, 'auto');
|
|
|
|
if s = '1' then begin
|
|
|
|
if ANode.NodeName = 'fgColor' then
|
|
|
|
Result := scBlack
|
|
|
|
else
|
|
|
|
Result := scTransparent;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
2014-08-06 15:49:04 +00:00
|
|
|
s := GetAttrValue(ANode, 'rgb');
|
|
|
|
if s <> '' then begin
|
2015-05-28 20:08:24 +00:00
|
|
|
Result := HTMLColorStrToColor('#' + s);
|
2014-08-06 15:49:04 +00:00
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'indexed');
|
|
|
|
if s <> '' then begin
|
2015-05-31 16:06:22 +00:00
|
|
|
i := StrToInt(s);
|
2015-05-28 20:08:24 +00:00
|
|
|
n := FPalette.Count;
|
2015-05-31 16:06:22 +00:00
|
|
|
if (i <= LAST_PALETTE_INDEX) and (i < n) then
|
2015-05-28 20:08:24 +00:00
|
|
|
begin
|
2015-05-31 16:06:22 +00:00
|
|
|
Result := FPalette[i];
|
2014-08-06 21:52:20 +00:00
|
|
|
exit;
|
2015-05-28 20:08:24 +00:00
|
|
|
end;
|
2014-08-06 21:52:20 +00:00
|
|
|
// System colors
|
|
|
|
// taken from OpenOffice docs
|
2015-05-31 16:06:22 +00:00
|
|
|
case i of
|
2014-08-06 21:52:20 +00:00
|
|
|
$0040: Result := scBlack; // Default border color
|
|
|
|
$0041: Result := scWhite; // Default background color
|
|
|
|
$0043: Result := scGray; // Dialog background color
|
|
|
|
$004D: Result := scBlack; // Text color, chart border lines
|
|
|
|
$004E: Result := scGray; // Background color for chart areas
|
|
|
|
$004F: Result := scBlack; // Automatic color for chart border lines
|
|
|
|
$0050: Result := scBlack; // ???
|
|
|
|
$0051: Result := scBlack; // ??
|
2015-05-31 16:06:22 +00:00
|
|
|
$7FFF: Result := scBlack; // Automatic text color
|
2014-08-06 21:52:20 +00:00
|
|
|
else Result := scBlack;
|
|
|
|
end;
|
2014-08-06 15:49:04 +00:00
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'theme');
|
|
|
|
if s <> '' then begin
|
|
|
|
idx := StrToInt(s);
|
|
|
|
if idx < Length(FThemeColors) then begin
|
2014-08-06 21:52:20 +00:00
|
|
|
// For some reason the first two pairs of colors are interchanged in Excel!
|
|
|
|
case idx of
|
|
|
|
0: idx := 1;
|
|
|
|
1: idx := 0;
|
|
|
|
2: idx := 3;
|
|
|
|
3: idx := 2;
|
|
|
|
end;
|
|
|
|
rgb := FThemeColors[idx];
|
2014-08-06 15:49:04 +00:00
|
|
|
s := GetAttrValue(ANode, 'tint');
|
|
|
|
if s <> '' then begin
|
|
|
|
tint := StrToFloat(s, FPointSeparatorSettings);
|
|
|
|
rgb := TintedColor(rgb, tint);
|
|
|
|
end;
|
2015-05-28 20:08:24 +00:00
|
|
|
Result := rgb;
|
2014-08-06 15:49:04 +00:00
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
Result := scBlack;
|
|
|
|
end;
|
|
|
|
|
2014-08-03 21:21:31 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadCols(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
2014-09-09 11:42:20 +00:00
|
|
|
const
|
|
|
|
EPS = 1e-2;
|
2014-08-03 21:21:31 +00:00
|
|
|
var
|
|
|
|
colNode: TDOMNode;
|
|
|
|
col, col1, col2: Cardinal;
|
|
|
|
w: Double;
|
|
|
|
s: String;
|
|
|
|
begin
|
|
|
|
if ANode = nil then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
colNode := ANode.FirstChild;
|
|
|
|
while Assigned(colNode) do begin
|
|
|
|
s := GetAttrValue(colNode, 'customWidth');
|
|
|
|
if s = '1' then begin
|
|
|
|
s := GetAttrValue(colNode, 'min');
|
|
|
|
if s <> '' then col1 := StrToInt(s)-1 else col1 := 0;
|
|
|
|
s := GetAttrValue(colNode, 'max');
|
|
|
|
if s <> '' then col2 := StrToInt(s)-1 else col2 := col1;
|
|
|
|
s := GetAttrValue(colNode, 'width');
|
2014-09-09 11:42:20 +00:00
|
|
|
if (s <> '') and TryStrToFloat(s, w, FPointSeparatorSettings) then
|
|
|
|
if not SameValue(w, AWorksheet.DefaultColWidth, EPS) then
|
|
|
|
for col := col1 to col2 do
|
|
|
|
AWorksheet.WriteColWidth(col, w);
|
2014-08-03 21:21:31 +00:00
|
|
|
end;
|
|
|
|
colNode := colNode.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2015-01-31 18:42:22 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadComments(ANode: TDOMNode;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
node, txtNode, rNode, rchild: TDOMNode;
|
|
|
|
nodeName: String;
|
|
|
|
cellAddr: String;
|
|
|
|
s: String;
|
|
|
|
r, c: Cardinal;
|
|
|
|
comment: String;
|
|
|
|
begin
|
|
|
|
comment := '';
|
|
|
|
node := ANode.FirstChild;
|
|
|
|
while node <> nil do
|
|
|
|
begin
|
|
|
|
nodeName := node.NodeName;
|
|
|
|
cellAddr := GetAttrValue(node, 'ref');
|
|
|
|
if cellAddr <> '' then
|
|
|
|
begin
|
2015-02-02 18:51:13 +00:00
|
|
|
comment := '';
|
2015-01-31 18:42:22 +00:00
|
|
|
txtNode := node.FirstChild;
|
|
|
|
while txtNode <> nil do
|
|
|
|
begin
|
|
|
|
rNode := txtnode.FirstChild;
|
|
|
|
while rNode <> nil do
|
|
|
|
begin
|
|
|
|
nodeName := rnode.NodeName;
|
|
|
|
rchild := rNode.FirstChild;
|
|
|
|
while rchild <> nil do begin
|
|
|
|
nodename := rchild.NodeName;
|
|
|
|
if nodename = 't' then begin
|
|
|
|
s := GetNodeValue(rchild);
|
|
|
|
if comment = '' then comment := s else comment := comment + s;
|
|
|
|
end;
|
|
|
|
rchild := rchild.NextSibling;
|
|
|
|
end;
|
|
|
|
rNode := rNode.NextSibling;
|
|
|
|
end;
|
|
|
|
if (comment <> '') and ParseCellString(cellAddr, r, c) then begin
|
|
|
|
// Fix line endings // #10 --> "LineEnding"
|
|
|
|
comment := UTF8StringReplace(comment, #10, LineEnding, [rfReplaceAll]);
|
|
|
|
AWorksheet.WriteComment(r, c, comment);
|
|
|
|
end;
|
|
|
|
txtNode := txtNode.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
2015-02-02 18:51:13 +00:00
|
|
|
node := node.NextSibling;
|
2015-01-31 18:42:22 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2014-07-26 21:18:49 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadDateMode(ANode: TDOMNode);
|
|
|
|
var
|
|
|
|
s: String;
|
|
|
|
begin
|
|
|
|
if Assigned(ANode) then begin
|
|
|
|
s := GetAttrValue(ANode, 'date1904');
|
|
|
|
if s = '1' then FDateMode := dm1904
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2014-07-27 22:44:17 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadFileVersion(ANode: TDOMNode);
|
|
|
|
begin
|
|
|
|
FWrittenByFPS := GetAttrValue(ANode, 'appName') = 'fpspreadsheet';
|
|
|
|
end;
|
|
|
|
|
2014-07-29 22:09:29 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadFills(ANode: TDOMNode);
|
|
|
|
var
|
|
|
|
fillNode, patternNode, colorNode: TDOMNode;
|
|
|
|
nodeName: String;
|
|
|
|
filldata: TFillListData;
|
|
|
|
patt: String;
|
|
|
|
fgclr: TsColor;
|
|
|
|
bgclr: TsColor;
|
|
|
|
begin
|
|
|
|
if ANode = nil then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
fillNode := ANode.FirstChild;
|
|
|
|
while Assigned(fillNode) do begin
|
|
|
|
nodename := fillNode.NodeName;
|
|
|
|
patternNode := fillNode.FirstChild;
|
|
|
|
while Assigned(patternNode) do begin
|
|
|
|
nodename := patternNode.NodeName;
|
|
|
|
if nodename = 'patternFill' then begin
|
|
|
|
patt := GetAttrValue(patternNode, 'patternType');
|
|
|
|
fgclr := scWhite;
|
|
|
|
bgclr := scBlack;
|
|
|
|
colorNode := patternNode.FirstChild;
|
|
|
|
while Assigned(colorNode) do begin
|
|
|
|
nodeName := colorNode.NodeName;
|
2014-10-08 09:23:34 +00:00
|
|
|
if nodeName = 'fgColor' then
|
|
|
|
fgclr := ReadColor(colorNode)
|
2014-07-29 22:09:29 +00:00
|
|
|
else
|
2014-10-08 09:23:34 +00:00
|
|
|
if nodeName = 'bgColor' then
|
2014-08-06 15:49:04 +00:00
|
|
|
bgclr := ReadColor(colorNode);
|
2014-07-29 22:09:29 +00:00
|
|
|
colorNode := colorNode.NextSibling;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Store in FFillList
|
|
|
|
fillData := TFillListData.Create;
|
|
|
|
fillData.PatternType := patt;
|
|
|
|
fillData.FgColor := fgclr;
|
|
|
|
fillData.BgColor := bgclr;
|
|
|
|
FFillList.Add(fillData);
|
|
|
|
end;
|
|
|
|
patternNode := patternNode.NextSibling;
|
|
|
|
end;
|
|
|
|
fillNode := fillNode.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2015-07-09 11:10:15 +00:00
|
|
|
{ Reads the font described by the specified node. If the node is already
|
|
|
|
contained in the font list the font's index is returned; otherwise the
|
|
|
|
new font is added to the list and its index is returned. }
|
|
|
|
function TsSpreadOOXMLReader.ReadFont(ANode: TDOMNode): Integer;
|
2014-07-24 22:22:26 +00:00
|
|
|
var
|
|
|
|
node: TDOMNode;
|
|
|
|
fnt: TsFont;
|
|
|
|
fntName: String;
|
|
|
|
fntSize: Single;
|
|
|
|
fntStyles: TsFontStyles;
|
|
|
|
fntColor: TsColor;
|
2015-07-09 11:10:15 +00:00
|
|
|
fntPos: TsFontPosition;
|
2014-07-24 22:22:26 +00:00
|
|
|
nodename: String;
|
|
|
|
s: String;
|
|
|
|
begin
|
|
|
|
fnt := Workbook.GetDefaultFont;
|
2015-07-09 11:10:15 +00:00
|
|
|
if fnt <> nil then
|
|
|
|
begin
|
2014-07-27 22:44:17 +00:00
|
|
|
fntName := fnt.FontName;
|
|
|
|
fntSize := fnt.Size;
|
|
|
|
fntStyles := fnt.Style;
|
|
|
|
fntColor := fnt.Color;
|
2015-07-09 11:10:15 +00:00
|
|
|
fntPos := fnt.Position;
|
|
|
|
end else
|
|
|
|
begin
|
2015-01-23 21:54:23 +00:00
|
|
|
fntName := DEFAULT_FONTNAME;
|
|
|
|
fntSize := DEFAULT_FONTSIZE;
|
2014-07-27 22:44:17 +00:00
|
|
|
fntStyles := [];
|
|
|
|
fntColor := scBlack;
|
2015-07-09 11:10:15 +00:00
|
|
|
fntPos := fpNormal;
|
2014-07-27 22:44:17 +00:00
|
|
|
end;
|
2014-07-24 22:22:26 +00:00
|
|
|
|
|
|
|
node := ANode.FirstChild;
|
2015-07-09 11:10:15 +00:00
|
|
|
while node <> nil do
|
|
|
|
begin
|
2014-07-24 22:22:26 +00:00
|
|
|
nodename := node.NodeName;
|
2015-07-09 11:10:15 +00:00
|
|
|
if (nodename = 'name') or (nodename = 'rFont') then
|
|
|
|
begin
|
2014-07-27 22:44:17 +00:00
|
|
|
s := GetAttrValue(node, 'val');
|
2014-07-24 22:22:26 +00:00
|
|
|
if s <> '' then fntName := s;
|
|
|
|
end
|
|
|
|
else
|
2015-07-09 11:10:15 +00:00
|
|
|
if nodename = 'sz' then
|
|
|
|
begin
|
2014-07-24 22:22:26 +00:00
|
|
|
s := GetAttrValue(node, 'val');
|
|
|
|
if s <> '' then fntSize := StrToFloat(s);
|
|
|
|
end
|
|
|
|
else
|
2015-07-09 11:10:15 +00:00
|
|
|
if nodename = 'b' then
|
|
|
|
begin
|
2014-07-27 22:44:17 +00:00
|
|
|
if GetAttrValue(node, 'val') <> 'false'
|
2014-07-24 22:22:26 +00:00
|
|
|
then fntStyles := fntStyles + [fssBold];
|
|
|
|
end
|
|
|
|
else
|
2015-07-09 11:10:15 +00:00
|
|
|
if nodename = 'i' then
|
|
|
|
begin
|
2014-07-27 22:44:17 +00:00
|
|
|
if GetAttrValue(node, 'val') <> 'false'
|
2014-07-24 22:22:26 +00:00
|
|
|
then fntStyles := fntStyles + [fssItalic];
|
|
|
|
end
|
|
|
|
else
|
2015-07-09 11:10:15 +00:00
|
|
|
if nodename = 'u' then
|
|
|
|
begin
|
2014-07-27 22:44:17 +00:00
|
|
|
if GetAttrValue(node, 'val') <> 'false'
|
2014-07-24 22:22:26 +00:00
|
|
|
then fntStyles := fntStyles+ [fssUnderline]
|
|
|
|
end
|
|
|
|
else
|
2015-07-09 11:10:15 +00:00
|
|
|
if nodename = 'strike' then
|
|
|
|
begin
|
2014-07-27 22:44:17 +00:00
|
|
|
if GetAttrValue(node, 'val') <> 'false'
|
2014-07-24 22:22:26 +00:00
|
|
|
then fntStyles := fntStyles + [fssStrikeout];
|
|
|
|
end
|
|
|
|
else
|
2015-07-09 11:10:15 +00:00
|
|
|
if nodename = 'vertAlign' then
|
|
|
|
begin
|
|
|
|
s := GetAttrValue(node, 'val');
|
|
|
|
if s = 'superscript' then
|
|
|
|
fntPos := fpSuperscript
|
|
|
|
else
|
|
|
|
if s = 'subscript' then
|
|
|
|
fntPos := fpSubscript
|
|
|
|
else
|
|
|
|
fntPos := fpNormal;
|
|
|
|
end
|
|
|
|
else
|
2014-08-06 15:49:04 +00:00
|
|
|
if nodename = 'color' then
|
|
|
|
fntColor := ReadColor(node);
|
2014-07-24 22:22:26 +00:00
|
|
|
node := node.NextSibling;
|
|
|
|
end;
|
|
|
|
|
2015-07-09 11:10:15 +00:00
|
|
|
// Check whether font is already contained in font list
|
|
|
|
for Result := 0 to FFontList.Count-1 do
|
|
|
|
begin
|
|
|
|
fnt := TsFont(FFontList[Result]);
|
|
|
|
if (fnt.FontName = fntName) and
|
|
|
|
(fnt.Size = fntSize) and
|
|
|
|
(fnt.Style = fntStyles) and
|
|
|
|
(fnt.Color = fntColor) and
|
|
|
|
(fnt.Position = fntPos)
|
|
|
|
then
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Font not yet stored --> create a new font and store it in list
|
2015-03-11 22:28:07 +00:00
|
|
|
fnt := TsFont.Create;
|
|
|
|
fnt.FontName := fntName;
|
|
|
|
fnt.Size := fntSize;
|
|
|
|
fnt.Style := fntStyles;
|
|
|
|
fnt.Color := fntColor;
|
2015-07-09 11:10:15 +00:00
|
|
|
fnt.Position := fntPos;
|
2015-03-11 22:28:07 +00:00
|
|
|
|
2015-07-09 11:10:15 +00:00
|
|
|
Result := FFontList.Add(fnt);
|
2014-07-24 22:22:26 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TsSpreadOOXMLReader.ReadFonts(ANode: TDOMNode);
|
|
|
|
var
|
|
|
|
node: TDOMNode;
|
|
|
|
begin
|
|
|
|
node := ANode.FirstChild;
|
|
|
|
while node <> nil do begin
|
|
|
|
ReadFont(node);
|
|
|
|
node := node.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2015-05-01 15:14:25 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadHeaderFooter(ANode: TDOMNode;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
node: TDOMNode;
|
|
|
|
nodeName: String;
|
|
|
|
s: String;
|
2015-05-10 22:04:09 +00:00
|
|
|
|
|
|
|
function FixLineEnding(AString: String): String;
|
|
|
|
begin
|
|
|
|
Result := StringReplace(AString, #10, LineEnding, [rfReplaceAll]);
|
|
|
|
end;
|
|
|
|
|
2015-05-01 15:14:25 +00:00
|
|
|
begin
|
|
|
|
if ANode = nil then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'differentOddEven');
|
|
|
|
if s = '1' then
|
|
|
|
Include(AWorksheet.PageLayout.Options, poDifferentOddEven);
|
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'differentFirst');
|
|
|
|
if s = '1' then
|
|
|
|
Include(AWorksheet.PageLayout.Options, poDifferentFirst);
|
|
|
|
|
|
|
|
node := ANode.FirstChild;
|
|
|
|
while node <> nil do
|
|
|
|
begin
|
|
|
|
nodeName := node.NodeName;
|
|
|
|
case nodeName of
|
2015-05-10 22:04:09 +00:00
|
|
|
'firstHeader': AWorksheet.PageLayout.Headers[0] := FixLineEnding(GetNodeValue(node));
|
|
|
|
'oddHeader' : AWorksheet.PageLayout.Headers[1] := FixLineEnding(GetNodeValue(node));
|
|
|
|
'evenHeader' : AWorksheet.PageLayout.Headers[2] := FixLineEnding(GetNodeValue(node));
|
|
|
|
'firstFooter': AWorksheet.PageLayout.Footers[0] := FixLineEnding(GetNodeValue(node));
|
|
|
|
'oddFooter' : AWorksheet.PageLayout.Footers[1] := FixLineEnding(GetNodeValue(node));
|
|
|
|
'evenFooter' : AWorksheet.PageLayout.Footers[2] := FixLineEnding(GetNodeValue(node));
|
2015-05-01 15:14:25 +00:00
|
|
|
end;
|
|
|
|
node := node.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2015-02-24 23:23:15 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadHyperlinks(ANode: TDOMNode);
|
2015-02-23 22:51:42 +00:00
|
|
|
var
|
|
|
|
node: TDOMNode;
|
|
|
|
nodeName: String;
|
|
|
|
hyperlinkData: THyperlinkListData;
|
|
|
|
s: String;
|
|
|
|
|
|
|
|
function FindHyperlinkID(ID: String): THyperlinkListData;
|
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
begin
|
|
|
|
for i:=0 to FHyperlinkList.Count-1 do
|
|
|
|
if THyperlinkListData(FHyperlinkList.Items[i]).ID = ID then
|
|
|
|
begin
|
|
|
|
Result := THyperlinkListData(FHyperlinkList.Items[i]);
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
begin
|
|
|
|
if Assigned(ANode) then begin
|
|
|
|
nodename := ANode.NodeName;
|
|
|
|
if nodename = 'hyperlinks' then
|
|
|
|
begin
|
|
|
|
node := ANode.FirstChild;
|
|
|
|
while Assigned(node) do
|
|
|
|
begin
|
|
|
|
nodename := node.NodeName;
|
|
|
|
if nodename = 'hyperlink' then begin
|
|
|
|
hyperlinkData := THyperlinkListData.Create;
|
|
|
|
hyperlinkData.CellRef := GetAttrValue(node, 'ref');
|
|
|
|
hyperlinkData.ID := GetAttrValue(node, 'r:id');
|
2015-02-28 23:46:08 +00:00
|
|
|
hyperlinkData.Target := '';
|
|
|
|
hyperlinkData.TextMark := GetAttrValue(node, 'location');
|
2015-02-23 22:51:42 +00:00
|
|
|
hyperlinkData.Display := GetAttrValue(node, 'display');
|
|
|
|
hyperlinkData.Tooltip := GetAttrValue(node, 'tooltip');
|
|
|
|
end;
|
|
|
|
FHyperlinkList.Add(hyperlinkData);
|
|
|
|
node := node.NextSibling;
|
|
|
|
end;
|
|
|
|
end else
|
|
|
|
if nodename = 'Relationship' then
|
|
|
|
begin
|
|
|
|
node := ANode;
|
|
|
|
while Assigned(node) do
|
|
|
|
begin
|
|
|
|
nodename := node.NodeName;
|
|
|
|
if nodename = 'Relationship' then
|
|
|
|
begin
|
|
|
|
s := GetAttrValue(node, 'Type');
|
|
|
|
if s = SCHEMAS_HYPERLINKS then
|
|
|
|
begin
|
|
|
|
s := GetAttrValue(node, 'Id');
|
|
|
|
if s <> '' then
|
|
|
|
begin
|
|
|
|
hyperlinkData := FindHyperlinkID(s);
|
|
|
|
if hyperlinkData <> nil then begin
|
|
|
|
s := GetAttrValue(node, 'Target');
|
2015-02-28 23:46:08 +00:00
|
|
|
if s <> '' then hyperlinkData.Target := s;
|
2015-02-23 22:51:42 +00:00
|
|
|
s := GetAttrValue(node, 'TargetMode');
|
2015-02-28 23:46:08 +00:00
|
|
|
if s <> 'External' then // Only "External" accepted!
|
|
|
|
begin
|
|
|
|
hyperlinkData.Target := '';
|
|
|
|
hyperlinkData.TextMark := '';
|
|
|
|
end;
|
2015-02-23 22:51:42 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
node := node.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2014-09-10 16:48:34 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadMergedCells(ANode: TDOMNode;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
node: TDOMNode;
|
|
|
|
nodename: String;
|
|
|
|
s: String;
|
|
|
|
begin
|
|
|
|
if Assigned(ANode) then begin
|
|
|
|
node := ANode.FirstChild;
|
|
|
|
while Assigned(node) do
|
|
|
|
begin
|
|
|
|
nodename := node.NodeName;
|
|
|
|
if nodename = 'mergeCell' then begin
|
|
|
|
s := GetAttrValue(node, 'ref');
|
|
|
|
if s <> '' then
|
|
|
|
AWorksheet.MergeCells(s);
|
|
|
|
end;
|
|
|
|
node := node.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2014-07-26 19:43:02 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadNumFormats(ANode: TDOMNode);
|
|
|
|
var
|
|
|
|
node: TDOMNode;
|
|
|
|
idStr: String;
|
|
|
|
fmtStr: String;
|
|
|
|
nodeName: String;
|
2015-04-18 14:58:38 +00:00
|
|
|
id: Integer;
|
2014-07-26 19:43:02 +00:00
|
|
|
begin
|
2015-04-18 14:58:38 +00:00
|
|
|
if Assigned(ANode) then
|
|
|
|
begin
|
2014-07-26 21:52:59 +00:00
|
|
|
node := ANode.FirstChild;
|
2015-04-18 14:58:38 +00:00
|
|
|
while Assigned(node) do
|
|
|
|
begin
|
2014-07-26 21:52:59 +00:00
|
|
|
nodeName := node.NodeName;
|
2015-04-18 14:58:38 +00:00
|
|
|
if nodeName = 'numFmt' then
|
|
|
|
begin
|
2014-07-26 21:52:59 +00:00
|
|
|
fmtStr := GetAttrValue(node, 'formatCode');
|
2015-04-18 14:58:38 +00:00
|
|
|
idStr := GetAttrValue(node, 'numFmtId');
|
|
|
|
id := StrToInt(idStr);
|
|
|
|
while id >= NumFormatList.Count do
|
|
|
|
NumFormatList.Add('');
|
|
|
|
NumFormatList[id] := fmtStr;
|
2014-07-26 21:52:59 +00:00
|
|
|
end;
|
|
|
|
node := node.NextSibling;
|
2014-07-26 19:43:02 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2015-04-29 20:00:07 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadPageMargins(ANode: TDOMNode;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
s: String;
|
|
|
|
begin
|
2015-04-30 21:55:55 +00:00
|
|
|
if (ANode = nil) or (AWorksheet = nil) then // just to make sure...
|
2015-04-29 20:00:07 +00:00
|
|
|
exit;
|
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'left');
|
|
|
|
if s <> '' then
|
2015-04-30 21:55:55 +00:00
|
|
|
AWorksheet.PageLayout.LeftMargin := PtsToMM(HtmlLengthStrToPts(s, 'in'));
|
2015-04-29 20:00:07 +00:00
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'right');
|
|
|
|
if s <> '' then
|
2015-04-30 21:55:55 +00:00
|
|
|
AWorksheet.PageLayout.RightMargin := PtsToMM(HtmlLengthStrToPts(s, 'in'));
|
2015-04-29 20:00:07 +00:00
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'top');
|
|
|
|
if s <> '' then
|
2015-04-30 21:55:55 +00:00
|
|
|
AWorksheet.PageLayout.TopMargin := PtsToMM(HtmlLengthStrToPts(s, 'in'));
|
2015-04-29 20:00:07 +00:00
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'bottom');
|
|
|
|
if s <> '' then
|
2015-04-30 21:55:55 +00:00
|
|
|
AWorksheet.PageLayout.BottomMargin := PtsToMM(HtmlLengthStrToPts(s, 'in'));
|
2015-04-29 20:00:07 +00:00
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'header');
|
|
|
|
if s <> '' then
|
2015-04-30 21:55:55 +00:00
|
|
|
AWorksheet.PageLayout.HeaderMargin := PtsToMM(HtmlLengthStrToPts(s, 'in'));
|
2015-04-29 20:00:07 +00:00
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'footer');
|
|
|
|
if s <> '' then
|
2015-04-30 21:55:55 +00:00
|
|
|
AWorksheet.PageLayout.FooterMargin := PtsToMM(HtmlLengthStrToPts(s, 'in'));
|
2015-04-29 20:00:07 +00:00
|
|
|
end;
|
|
|
|
|
2015-05-01 15:14:25 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadPageSetup(ANode: TDOMNode;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
s: String;
|
|
|
|
n: Integer;
|
|
|
|
begin
|
|
|
|
if ANode = nil then
|
|
|
|
exit;
|
|
|
|
|
2015-05-02 23:04:59 +00:00
|
|
|
// Paper size
|
2015-05-01 15:14:25 +00:00
|
|
|
s := GetAttrValue(ANode, 'paperSize');
|
|
|
|
if s <> '' then
|
|
|
|
begin
|
|
|
|
n := StrToInt(s);
|
|
|
|
if (n >= 0) and (n <= High(PAPER_SIZES)) then
|
|
|
|
begin
|
|
|
|
AWorksheet.PageLayout.PageWidth := PAPER_SIZES[n, 0];
|
|
|
|
AWorksheet.PageLayout.PageHeight := PAPER_SIZES[n, 1];
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2015-05-02 23:04:59 +00:00
|
|
|
// Orientation
|
|
|
|
s := GetAttrValue(ANode, 'orientation');
|
2015-05-01 15:14:25 +00:00
|
|
|
if s = 'portrait' then
|
|
|
|
AWorksheet.PageLayout.Orientation := spoPortrait
|
|
|
|
else if s = 'landscape' then
|
|
|
|
AWorksheet.PageLayout.Orientation := spoLandscape;
|
2015-05-02 23:04:59 +00:00
|
|
|
|
|
|
|
// Scaling factor
|
|
|
|
s := GetAttrValue(ANode, 'scale');
|
|
|
|
if s <> '' then
|
2015-05-04 17:38:56 +00:00
|
|
|
begin
|
2015-05-02 23:04:59 +00:00
|
|
|
AWorksheet.PageLayout.ScalingFactor := StrToInt(s);
|
2015-05-04 17:38:56 +00:00
|
|
|
Exclude(AWorksheet.PageLayout.Options, poFitPages);
|
|
|
|
end;
|
2015-05-02 23:04:59 +00:00
|
|
|
|
|
|
|
// Fit print job to pages
|
|
|
|
s := GetAttrValue(ANode, 'fitToHeight');
|
|
|
|
if s <> '' then
|
2015-05-04 17:38:56 +00:00
|
|
|
begin
|
2015-05-02 23:04:59 +00:00
|
|
|
AWorksheet.PageLayout.FitHeightToPages := StrToInt(s);
|
2015-05-04 17:38:56 +00:00
|
|
|
Include(AWorksheet.PageLayout.Options, poFitPages);
|
|
|
|
end;
|
2015-05-02 23:04:59 +00:00
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'fitToWidth');
|
|
|
|
if s <> '' then
|
2015-05-04 17:38:56 +00:00
|
|
|
begin
|
2015-05-02 23:04:59 +00:00
|
|
|
AWorksheet.PageLayout.FitWidthToPages := StrToInt(s);
|
2015-05-04 17:38:56 +00:00
|
|
|
Include(AWorksheet.PageLayout.Options, poFitPages);
|
|
|
|
end;
|
2015-05-02 23:04:59 +00:00
|
|
|
|
|
|
|
// First page number
|
|
|
|
s := GetAttrValue(ANode, 'useFirstPageNumber');
|
2015-05-04 17:38:56 +00:00
|
|
|
if s = '1' then
|
|
|
|
Include(AWorksheet.PageLayout.Options, poUseStartPageNumber);
|
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'firstPageNumber');
|
2015-05-02 23:04:59 +00:00
|
|
|
if s <> '' then
|
|
|
|
AWorksheet.PageLayout.StartPageNumber := StrToInt(s);
|
|
|
|
|
|
|
|
// Print order
|
|
|
|
s := GetAttrValue(ANode, 'pageOrder');
|
|
|
|
if s = 'overThenDown' then
|
|
|
|
Include(AWorksheet.PageLayout.Options, poPrintPagesByRows);
|
|
|
|
|
|
|
|
// Monochrome
|
|
|
|
s := GetAttrValue(ANode, 'blackAndWhite');
|
|
|
|
if s = '1' then
|
|
|
|
Include(AWorksheet.PageLayout.Options, poMonochrome);
|
|
|
|
|
|
|
|
// Quality
|
|
|
|
s := GetAttrValue(ANode, 'draft');
|
|
|
|
if s = '1' then
|
|
|
|
Include(AWorksheet.PageLayout.Options, poDraftQuality);
|
2015-05-01 15:14:25 +00:00
|
|
|
end;
|
|
|
|
|
2014-07-29 21:02:14 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadPalette(ANode: TDOMNode);
|
|
|
|
var
|
|
|
|
node, colornode: TDOMNode;
|
|
|
|
nodename: String;
|
2014-08-06 21:52:20 +00:00
|
|
|
s: string;
|
2015-05-28 20:08:24 +00:00
|
|
|
cidx: Integer; // color index
|
|
|
|
rgb: TsColor;
|
2014-07-29 21:02:14 +00:00
|
|
|
n: Integer;
|
|
|
|
begin
|
2014-08-06 21:52:20 +00:00
|
|
|
// OOXML sometimes specifies color by index even if a palette ("indexedColors")
|
|
|
|
// is not loaeded. Therefore, we use the BIFF8 palette as default because
|
|
|
|
// the default indexedColors are identical to it.
|
2015-05-28 20:08:24 +00:00
|
|
|
FPalette.Clear;
|
|
|
|
FPalette.AddBuiltinColors; // This adds the BIFF2 colors 0..7
|
|
|
|
FPalette.AddExcelColors; // This adds the BIFF8 colors 8..63
|
|
|
|
n := FPalette.Count;
|
|
|
|
|
2014-07-29 21:02:14 +00:00
|
|
|
if ANode = nil then
|
|
|
|
exit;
|
2014-08-06 21:52:20 +00:00
|
|
|
|
2015-05-28 20:08:24 +00:00
|
|
|
cidx := 0;
|
2014-07-29 21:02:14 +00:00
|
|
|
node := ANode.FirstChild;
|
2015-05-28 20:08:24 +00:00
|
|
|
while Assigned(node) do
|
|
|
|
begin
|
2014-07-29 21:02:14 +00:00
|
|
|
nodename := node.NodeName;
|
2015-05-28 20:08:24 +00:00
|
|
|
if nodename = 'indexedColors' then
|
|
|
|
begin
|
2014-07-29 21:02:14 +00:00
|
|
|
colornode := node.FirstChild;
|
2015-05-28 20:08:24 +00:00
|
|
|
while Assigned(colornode) do
|
|
|
|
begin
|
2014-07-29 21:02:14 +00:00
|
|
|
nodename := colornode.NodeName;
|
|
|
|
if nodename = 'rgbColor' then begin
|
2014-08-06 21:52:20 +00:00
|
|
|
s := GetAttrValue(colornode, 'rgb');
|
|
|
|
if s <> '' then begin
|
|
|
|
rgb := HTMLColorStrToColor('#' + s);
|
2015-05-28 20:08:24 +00:00
|
|
|
if cidx < n then begin
|
|
|
|
FPalette[cidx] := rgb;
|
|
|
|
inc(cidx);
|
2014-08-06 21:52:20 +00:00
|
|
|
end
|
|
|
|
else
|
2015-05-28 20:08:24 +00:00
|
|
|
FPalette.AddColor(rgb);
|
2014-07-29 21:02:14 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
colornode := colorNode.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
node := node.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2015-04-30 21:55:55 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadPrintOptions(ANode: TDOMNode;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
s: String;
|
|
|
|
begin
|
|
|
|
if ANode = nil then
|
|
|
|
exit;
|
|
|
|
s := GetAttrValue(ANode, 'headings');
|
|
|
|
if (s = '1') then
|
|
|
|
Include(AWorksheet.PageLayout.Options, poPrintHeaders);
|
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'gridLines');
|
|
|
|
if (s = '1') then
|
|
|
|
Include(AWorksheet.PageLayout.Options, poPrintGridLines);
|
|
|
|
end;
|
|
|
|
|
2014-08-03 21:21:31 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadRowHeight(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
s: String;
|
|
|
|
ht: Single;
|
|
|
|
r: Cardinal;
|
|
|
|
row: PRow;
|
|
|
|
begin
|
|
|
|
if ANode = nil then
|
|
|
|
exit;
|
|
|
|
s := GetAttrValue(ANode, 'customHeight');
|
|
|
|
if s = '1' then begin
|
|
|
|
s := GetAttrValue(ANode, 'r');
|
|
|
|
r := StrToInt(s) - 1;
|
|
|
|
s := GetAttrValue(ANode, 'ht');
|
|
|
|
ht := StrToFloat(s, FPointSeparatorSettings); // seems to be in "Points"
|
2014-08-10 17:59:30 +00:00
|
|
|
row := AWorksheet.GetRow(r);
|
2014-08-03 21:21:31 +00:00
|
|
|
row^.Height := ht / FWorkbook.GetDefaultFontSize;
|
|
|
|
if row^.Height > ROW_HEIGHT_CORRECTION then
|
|
|
|
row^.Height := row^.Height - ROW_HEIGHT_CORRECTION
|
|
|
|
else
|
|
|
|
row^.Height := 0;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2014-07-24 22:22:26 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadSharedStrings(ANode: TDOMNode);
|
|
|
|
var
|
|
|
|
valuenode: TDOMNode;
|
2014-08-29 10:48:20 +00:00
|
|
|
childnode: TDOMNode;
|
|
|
|
nodename: String;
|
2015-07-09 11:10:15 +00:00
|
|
|
s, sval: String;
|
|
|
|
fntIndex, startIndex, count: Integer;
|
|
|
|
richTextParams: TsRichTextParams;
|
|
|
|
ms: TMemoryStream;
|
|
|
|
fnt: TsFont;
|
2014-07-24 22:22:26 +00:00
|
|
|
begin
|
|
|
|
while Assigned(ANode) do begin
|
|
|
|
if ANode.NodeName = 'si' then begin
|
2014-08-29 10:48:20 +00:00
|
|
|
s := '';
|
2015-07-09 11:10:15 +00:00
|
|
|
richTextParams := nil;
|
2014-07-24 22:22:26 +00:00
|
|
|
valuenode := ANode.FirstChild;
|
2014-08-29 10:48:20 +00:00
|
|
|
while valuenode <> nil do begin
|
|
|
|
nodename := valuenode.NodeName;
|
|
|
|
if nodename = 't' then
|
|
|
|
s := GetNodeValue(valuenode)
|
|
|
|
else
|
|
|
|
if nodename = 'r' then begin
|
2015-07-09 11:10:15 +00:00
|
|
|
fntIndex := -1;
|
|
|
|
startIndex := -1;
|
|
|
|
count := -1;
|
2014-08-29 10:48:20 +00:00
|
|
|
childnode := valuenode.FirstChild;
|
|
|
|
while childnode <> nil do begin
|
2015-07-09 11:10:15 +00:00
|
|
|
nodename := childnode.NodeName;
|
|
|
|
if nodename = 't' then
|
|
|
|
begin
|
|
|
|
startIndex := Length(s);
|
|
|
|
sval := GetNodevalue(childNode);
|
|
|
|
s := s + sval;
|
|
|
|
count := Length(sval);
|
|
|
|
if fntIndex <> -1 then
|
|
|
|
begin
|
|
|
|
SetLength(richTextParams, Length(richTextParams)+1);
|
|
|
|
richTextParams[Length(richTextParams)-1].StartIndex := startIndex;
|
|
|
|
richTextParams[Length(richTextParams)-1].EndIndex := startIndex + count;
|
|
|
|
richTextParams[Length(richTextParams)-1].FontIndex := fntIndex;
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
else if nodename = 'rPr' then begin
|
|
|
|
fntIndex := ReadFont(childnode);
|
|
|
|
// Here we store the font in the internal font list of the reader.
|
|
|
|
// But this fontindex may be different from the one needed for the
|
|
|
|
// workbook's font list. We fix this here.
|
|
|
|
fnt := TsFont(FFontList[fntIndex]);
|
|
|
|
fntIndex := Workbook.FindFont(fnt.FontName, fnt.Size, fnt.style, fnt.Color, fnt.Position);
|
|
|
|
if fntIndex = -1 then
|
|
|
|
fntIndex := Workbook.AddFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color, fnt.Position);
|
|
|
|
if startIndex <> -1 then begin
|
|
|
|
SetLength(richTextParams, Length(richTextParams)+1);
|
|
|
|
richTextParams[Length(richTextParams)-1].StartIndex := startIndex;
|
|
|
|
richTextParams[Length(richTextParams)-1].EndIndex := startIndex + count;
|
|
|
|
richTextParams[Length(richTextParams)-1].FontIndex := fntIndex;
|
|
|
|
end;
|
|
|
|
end;
|
2014-08-29 10:48:20 +00:00
|
|
|
childnode := childnode.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
valuenode := valuenode.NextSibling;
|
|
|
|
end;
|
2015-07-09 11:10:15 +00:00
|
|
|
if Length(richTextParams) = 0 then
|
|
|
|
FSharedStrings.Add(s)
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
ms := TMemoryStream.Create;
|
|
|
|
ms.WriteWord(Length(richTextParams));
|
|
|
|
ms.WriteBuffer(richTextParams[0], SizeOf(TsRichTextParam)*Length(richTextParams));
|
|
|
|
FSharedStrings.AddObject(s, ms);
|
|
|
|
end;
|
2014-07-24 22:22:26 +00:00
|
|
|
end;
|
|
|
|
ANode := ANode.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2014-09-09 11:42:20 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadSheetFormatPr(ANode: TDOMNode;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
w, h: Single;
|
|
|
|
s: String;
|
|
|
|
begin
|
|
|
|
if ANode = nil then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'defaultColWidth'); // is in characters
|
|
|
|
if (s <> '') and TryStrToFloat(s, w, FPointSeparatorSettings) then
|
|
|
|
AWorksheet.DefaultColWidth := w;
|
|
|
|
|
|
|
|
s := GetAttrValue(ANode, 'defaultRowHeight'); // in in points
|
|
|
|
if (s <> '') and TryStrToFloat(s, h, FPointSeparatorSettings) then begin
|
|
|
|
h := h / Workbook.GetDefaultFontSize;
|
|
|
|
if h > ROW_HEIGHT_CORRECTION then begin
|
|
|
|
h := h - ROW_HEIGHT_CORRECTION;
|
|
|
|
AWorksheet.DefaultRowHeight := h;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2014-07-24 22:22:26 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadSheetList(ANode: TDOMNode; AList: TStrings);
|
|
|
|
var
|
|
|
|
node: TDOMNode;
|
2015-01-23 21:54:23 +00:00
|
|
|
nodename: String;
|
2014-07-24 22:22:26 +00:00
|
|
|
sheetName: String;
|
2015-01-31 18:42:22 +00:00
|
|
|
sheetId: String;
|
2014-07-24 22:22:26 +00:00
|
|
|
begin
|
|
|
|
node := ANode.FirstChild;
|
|
|
|
while node <> nil do begin
|
2015-01-23 21:54:23 +00:00
|
|
|
nodename := node.NodeName;
|
|
|
|
if nodename = 'sheet' then
|
|
|
|
begin
|
|
|
|
sheetName := GetAttrValue(node, 'name');
|
2015-01-31 18:42:22 +00:00
|
|
|
sheetId := GetAttrValue(node, 'sheetId');
|
|
|
|
AList.AddObject(sheetName, TObject(ptrInt(StrToInt(sheetID))));
|
2015-01-23 21:54:23 +00:00
|
|
|
end;
|
2014-07-24 22:22:26 +00:00
|
|
|
node := node.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2014-08-03 22:55:58 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadSheetViews(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
sheetViewNode: TDOMNode;
|
2014-08-04 19:11:17 +00:00
|
|
|
childNode: TDOMNode;
|
2014-08-03 22:55:58 +00:00
|
|
|
nodeName: String;
|
|
|
|
s: String;
|
|
|
|
begin
|
|
|
|
if ANode = nil then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
sheetViewNode := ANode.FirstChild;
|
|
|
|
while Assigned(sheetViewNode) do begin
|
|
|
|
nodeName := sheetViewNode.NodeName;
|
|
|
|
if nodeName = 'sheetView' then begin
|
|
|
|
s := GetAttrValue(sheetViewNode, 'showGridLines');
|
|
|
|
if s = '0' then
|
|
|
|
AWorksheet.Options := AWorksheet.Options - [soShowGridLines];
|
|
|
|
s := GetAttrValue(sheetViewNode, 'showRowColHeaders');
|
|
|
|
if s = '0' then
|
|
|
|
AWorksheet.Options := AWorksheet.Options - [soShowHeaders];
|
2014-08-04 19:11:17 +00:00
|
|
|
|
|
|
|
childNode := sheetViewNode.FirstChild;
|
|
|
|
while Assigned(childNode) do begin
|
|
|
|
nodeName := childNode.NodeName;
|
|
|
|
if nodeName = 'pane' then begin
|
|
|
|
s := GetAttrValue(childNode, 'state');
|
|
|
|
if s = 'frozen' then begin
|
|
|
|
AWorksheet.Options := AWorksheet.Options + [soHasFrozenPanes];
|
|
|
|
s := GetAttrValue(childNode, 'xSplit');
|
|
|
|
if s <> '' then AWorksheet.LeftPaneWidth := StrToInt(s);
|
|
|
|
s := GetAttrValue(childNode, 'ySplit');
|
|
|
|
if s <> '' then AWorksheet.TopPaneHeight := StrToInt(s);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
childNode := childNode.NextSibling;
|
|
|
|
end;
|
2014-08-03 22:55:58 +00:00
|
|
|
end;
|
|
|
|
sheetViewNode := sheetViewNode.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2014-08-06 15:49:04 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadThemeColors(ANode: TDOMNode);
|
|
|
|
var
|
|
|
|
clrNode: TDOMNode;
|
|
|
|
nodeName: String;
|
|
|
|
|
|
|
|
procedure AddColor(AColorStr: String);
|
|
|
|
begin
|
|
|
|
if AColorStr <> '' then begin
|
|
|
|
SetLength(FThemeColors, Length(FThemeColors)+1);
|
|
|
|
FThemeColors[Length(FThemeColors)-1] := HTMLColorStrToColor('#' + AColorStr);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
begin
|
|
|
|
if not Assigned(ANode) then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
SetLength(FThemeColors, 0);
|
|
|
|
clrNode := ANode.FirstChild;
|
|
|
|
while Assigned(clrNode) do begin
|
|
|
|
nodeName := clrNode.NodeName;
|
|
|
|
if nodeName = 'a:dk1' then
|
|
|
|
AddColor(GetAttrValue(clrNode.FirstChild, 'lastClr'))
|
|
|
|
else
|
|
|
|
if nodeName = 'a:lt1' then
|
|
|
|
AddColor(GetAttrValue(clrNode.FirstChild, 'lastClr'))
|
|
|
|
else
|
|
|
|
if nodeName = 'a:dk2' then
|
|
|
|
AddColor(GetAttrValue(clrNode.FirstChild, 'val'))
|
|
|
|
else
|
|
|
|
if nodeName = 'a:lt2' then
|
|
|
|
AddColor(GetAttrValue(clrNode.FirstChild, 'val'))
|
|
|
|
else
|
|
|
|
if nodeName = 'a:accent1' then
|
|
|
|
AddColor(GetAttrValue(clrNode.FirstChild, 'val'))
|
|
|
|
else
|
|
|
|
if nodeName = 'a:accent2' then
|
|
|
|
AddColor(GetAttrValue(clrNode.FirstChild, 'val'))
|
|
|
|
else
|
|
|
|
if nodeName = 'a:accent3' then
|
|
|
|
AddColor(GetAttrValue(clrNode.FirstChild, 'val'))
|
|
|
|
else
|
|
|
|
if nodeName = 'a:accent4' then
|
|
|
|
AddColor(GetAttrValue(clrNode.FirstChild, 'val'))
|
|
|
|
else
|
|
|
|
if nodeName = 'a:accent5' then
|
|
|
|
AddColor(GetAttrValue(clrNode.FirstChild, 'val'))
|
|
|
|
else
|
|
|
|
if nodeName = 'a:accent6' then
|
|
|
|
AddColor(GetAttrValue(clrNode.FirstChild, 'val'))
|
|
|
|
else
|
|
|
|
if nodeName = 'a:hlink' then
|
|
|
|
AddColor(GetAttrValue(clrNode.FirstChild, 'val'))
|
|
|
|
else
|
|
|
|
if nodeName = 'a:folHlink' then
|
|
|
|
AddColor(GetAttrValue(clrNode.FirstChild, 'aval'));
|
|
|
|
clrNode := clrNode.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TsSpreadOOXMLReader.ReadThemeElements(ANode: TDOMNode);
|
|
|
|
var
|
|
|
|
childNode: TDOMNode;
|
|
|
|
nodeName: String;
|
|
|
|
begin
|
|
|
|
if not Assigned(ANode) then
|
|
|
|
exit;
|
|
|
|
childNode := ANode.FirstChild;
|
|
|
|
while Assigned(childNode) do begin
|
|
|
|
nodeName := childNode.NodeName;
|
|
|
|
if nodeName = 'a:clrScheme' then
|
|
|
|
ReadThemeColors(childNode);
|
|
|
|
childNode := childNode.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2014-07-26 21:18:49 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadWorksheet(ANode: TDOMNode; AWorksheet: TsWorksheet);
|
2014-07-24 22:22:26 +00:00
|
|
|
var
|
|
|
|
rownode: TDOMNode;
|
|
|
|
cellnode: TDOMNode;
|
|
|
|
begin
|
|
|
|
rownode := ANode.FirstChild;
|
|
|
|
while Assigned(rownode) do begin
|
|
|
|
if rownode.NodeName = 'row' then begin
|
2014-08-03 21:21:31 +00:00
|
|
|
ReadRowHeight(rownode, AWorksheet);
|
2014-07-24 22:22:26 +00:00
|
|
|
cellnode := rownode.FirstChild;
|
|
|
|
while Assigned(cellnode) do begin
|
2014-07-26 21:18:49 +00:00
|
|
|
if cellnode.NodeName = 'c' then
|
|
|
|
ReadCell(cellnode, AWorksheet);
|
2014-07-24 22:22:26 +00:00
|
|
|
cellnode := cellnode.NextSibling;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
rownode := rownode.NextSibling;
|
|
|
|
end;
|
2014-09-07 22:25:11 +00:00
|
|
|
FixCols(AWorksheet);
|
2014-09-09 11:42:20 +00:00
|
|
|
FixRows(AWorksheet);
|
2014-07-24 22:22:26 +00:00
|
|
|
end;
|
|
|
|
|
2015-02-04 19:50:50 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadFromFile(AFileName: string);
|
2014-07-24 22:22:26 +00:00
|
|
|
var
|
|
|
|
Doc : TXMLDocument;
|
|
|
|
FilePath : string;
|
|
|
|
UnZip : TUnZipper;
|
|
|
|
FileList : TStringList;
|
|
|
|
SheetList: TStringList;
|
|
|
|
i: Integer;
|
2014-07-25 16:46:39 +00:00
|
|
|
fn: String;
|
2015-02-24 00:30:26 +00:00
|
|
|
fn_comments: String;
|
2014-07-24 22:22:26 +00:00
|
|
|
begin
|
2015-01-31 18:42:22 +00:00
|
|
|
//unzip "content.xml" of "AFileName" to folder "FilePath"
|
2014-07-24 22:22:26 +00:00
|
|
|
FilePath := GetTempDir(false);
|
|
|
|
UnZip := TUnZipper.Create;
|
|
|
|
FileList := TStringList.Create;
|
|
|
|
try
|
2015-01-31 18:42:22 +00:00
|
|
|
FileList.Add(OOXML_PATH_XL_STYLES); // styles
|
|
|
|
FileList.Add(OOXML_PATH_XL_STRINGS); // sharedstrings
|
|
|
|
FileList.Add(OOXML_PATH_XL_WORKBOOK); // workbook
|
|
|
|
FileList.Add(OOXML_PATH_XL_THEME); // theme
|
|
|
|
UnZip.OutputPath := FilePath;
|
2014-07-24 22:22:26 +00:00
|
|
|
Unzip.UnZipFiles(AFileName,FileList);
|
|
|
|
finally
|
|
|
|
FreeAndNil(FileList);
|
|
|
|
FreeAndNil(UnZip);
|
|
|
|
end; //try
|
|
|
|
|
|
|
|
Doc := nil;
|
|
|
|
SheetList := TStringList.Create;
|
|
|
|
try
|
2014-08-06 15:49:04 +00:00
|
|
|
// Retrieve theme colors
|
|
|
|
if FileExists(FilePath + OOXML_PATH_XL_THEME) then begin
|
|
|
|
ReadXMLFile(Doc, FilePath + OOXML_PATH_XL_THEME);
|
|
|
|
DeleteFile(FilePath + OOXML_PATH_XL_THEME);
|
|
|
|
ReadThemeElements(Doc.DocumentElement.FindNode('a:themeElements'));
|
|
|
|
FreeAndNil(Doc);
|
|
|
|
end;
|
|
|
|
|
2014-07-27 22:44:17 +00:00
|
|
|
// process the workbook.xml file
|
|
|
|
if not FileExists(FilePath + OOXML_PATH_XL_WORKBOOK) then
|
2014-10-08 09:23:34 +00:00
|
|
|
raise Exception.CreateFmt(rsDefectiveInternalStructure, ['xlsx']);
|
2014-07-27 22:44:17 +00:00
|
|
|
ReadXMLFile(Doc, FilePath + OOXML_PATH_XL_WORKBOOK);
|
|
|
|
DeleteFile(FilePath + OOXML_PATH_XL_WORKBOOK);
|
|
|
|
ReadFileVersion(Doc.DocumentElement.FindNode('fileVersion'));
|
|
|
|
ReadDateMode(Doc.DocumentElement.FindNode('workbookPr'));
|
|
|
|
ReadSheetList(Doc.DocumentElement.FindNode('sheets'), SheetList);
|
|
|
|
FreeAndNil(Doc);
|
|
|
|
|
2014-07-24 22:22:26 +00:00
|
|
|
// process the styles.xml file
|
2014-07-26 19:43:02 +00:00
|
|
|
if FileExists(FilePath + OOXML_PATH_XL_STYLES) then begin // should always exist, just to make sure...
|
|
|
|
ReadXMLFile(Doc, FilePath + OOXML_PATH_XL_STYLES);
|
|
|
|
DeleteFile(FilePath + OOXML_PATH_XL_STYLES);
|
2014-07-29 21:02:14 +00:00
|
|
|
ReadPalette(Doc.DocumentElement.FindNode('colors'));
|
2014-07-26 19:43:02 +00:00
|
|
|
ReadFonts(Doc.DocumentElement.FindNode('fonts'));
|
2014-07-29 22:09:29 +00:00
|
|
|
ReadFills(Doc.DocumentElement.FindNode('fills'));
|
2014-07-31 21:05:01 +00:00
|
|
|
ReadBorders(Doc.DocumentElement.FindNode('borders'));
|
2014-07-26 19:43:02 +00:00
|
|
|
ReadNumFormats(Doc.DocumentElement.FindNode('numFmts'));
|
|
|
|
ReadCellXfs(Doc.DocumentElement.FindNode('cellXfs'));
|
|
|
|
FreeAndNil(Doc);
|
|
|
|
end;
|
2014-07-24 22:22:26 +00:00
|
|
|
|
2015-07-09 11:10:15 +00:00
|
|
|
// process the sharedstrings.xml file
|
|
|
|
if FileExists(FilePath + OOXML_PATH_XL_STRINGS) then begin
|
|
|
|
ReadXMLFile(Doc, FilePath + OOXML_PATH_XL_STRINGS);
|
|
|
|
DeleteFile(FilePath + OOXML_PATH_XL_STRINGS);
|
|
|
|
ReadSharedStrings(Doc.DocumentElement.FindNode('si'));
|
|
|
|
FreeAndNil(Doc);
|
|
|
|
end;
|
|
|
|
|
2014-07-24 22:22:26 +00:00
|
|
|
// read worksheets
|
|
|
|
for i:=0 to SheetList.Count-1 do begin
|
2015-01-31 18:42:22 +00:00
|
|
|
// Create worksheet
|
2015-02-04 19:50:50 +00:00
|
|
|
FWorksheet := FWorkbook.AddWorksheet(SheetList[i], true);
|
2014-07-24 22:22:26 +00:00
|
|
|
|
|
|
|
// unzip sheet file
|
2015-01-31 18:42:22 +00:00
|
|
|
fn := OOXML_PATH_XL_WORKSHEETS + Format('sheet%d.xml', [i+1]);
|
|
|
|
UnzipFile(AFileName, fn, FilePath);
|
2014-07-25 16:46:39 +00:00
|
|
|
ReadXMLFile(Doc, FilePath + fn);
|
|
|
|
DeleteFile(FilePath + fn);
|
2014-07-24 22:22:26 +00:00
|
|
|
|
2015-01-31 18:42:22 +00:00
|
|
|
// Sheet data, formats, etc.
|
2014-08-03 22:55:58 +00:00
|
|
|
ReadSheetViews(Doc.DocumentElement.FindNode('sheetViews'), FWorksheet);
|
2014-09-09 11:42:20 +00:00
|
|
|
ReadSheetFormatPr(Doc.DocumentElement.FindNode('sheetFormatPr'), FWorksheet);
|
2014-08-03 21:21:31 +00:00
|
|
|
ReadCols(Doc.DocumentElement.FindNode('cols'), FWorksheet);
|
2014-07-24 22:22:26 +00:00
|
|
|
ReadWorksheet(Doc.DocumentElement.FindNode('sheetData'), FWorksheet);
|
2014-09-10 16:48:34 +00:00
|
|
|
ReadMergedCells(Doc.DocumentElement.FindNode('mergeCells'), FWorksheet);
|
2015-02-24 23:23:15 +00:00
|
|
|
ReadHyperlinks(Doc.DocumentElement.FindNode('hyperlinks'));
|
2015-04-30 21:55:55 +00:00
|
|
|
ReadPrintOptions(Doc.DocumentElement.FindNode('printOptions'), FWorksheet);
|
2015-04-29 20:00:07 +00:00
|
|
|
ReadPageMargins(Doc.DocumentElement.FindNode('pageMargins'), FWorksheet);
|
2015-05-01 15:14:25 +00:00
|
|
|
ReadPageSetup(Doc.DocumentElement.FindNode('pageSetup'), FWorksheet);
|
|
|
|
ReadHeaderFooter(Doc.DocumentElement.FindNode('headerFooter'), FWorksheet);
|
2014-07-24 22:22:26 +00:00
|
|
|
|
|
|
|
FreeAndNil(Doc);
|
2015-01-31 18:42:22 +00:00
|
|
|
|
|
|
|
// Comments:
|
|
|
|
// The comments are stored in separate "comments<n>.xml" files (n = 1, 2, ...)
|
|
|
|
// The relationship which comment belongs to which sheet file must be
|
2015-02-23 22:51:42 +00:00
|
|
|
// retrieved from the "sheet<n>.xml.rels" file (n = 1, 2, ...).
|
|
|
|
// The rels file contains also the second part of the hyperlink data.
|
2015-01-31 18:42:22 +00:00
|
|
|
fn := OOXML_PATH_XL_WORKSHEETS_RELS + Format('sheet%d.xml.rels', [i+1]);
|
|
|
|
UnzipFile(AFilename, fn, FilePath);
|
|
|
|
if FileExists(FilePath + fn) then begin
|
|
|
|
// find exact name of comments<n>.xml file
|
|
|
|
ReadXMLFile(Doc, FilePath + fn);
|
|
|
|
DeleteFile(FilePath + fn);
|
2015-02-24 00:30:26 +00:00
|
|
|
fn_comments := FindCommentsFileName(Doc.DocumentElement.FindNode('Relationship'));
|
2015-02-24 23:23:15 +00:00
|
|
|
ReadHyperlinks(Doc.DocumentElement.FindNode('Relationship'));
|
2015-01-31 18:42:22 +00:00
|
|
|
FreeAndNil(Doc);
|
|
|
|
end else
|
|
|
|
if (SheetList.Count = 1) then
|
|
|
|
// if the wookbook has only 1 sheet then the sheet.xml.rels file is missing
|
2015-02-24 00:30:26 +00:00
|
|
|
fn_comments := 'comments1.xml'
|
2015-01-31 18:42:22 +00:00
|
|
|
else
|
|
|
|
// this sheet does not have any cell comments
|
|
|
|
continue;
|
|
|
|
// Extract texts from the comments file found and apply to worksheet.
|
2015-02-24 00:30:26 +00:00
|
|
|
if fn_comments <> '' then
|
|
|
|
begin
|
|
|
|
fn := OOXML_PATH_XL + fn_comments;
|
|
|
|
UnzipFile(AFileName, fn, FilePath);
|
|
|
|
if FileExists(FilePath + fn) then begin
|
|
|
|
ReadXMLFile(Doc, FilePath + fn);
|
|
|
|
DeleteFile(FilePath + fn);
|
|
|
|
ReadComments(Doc.DocumentElement.FindNode('commentList'), FWorksheet);
|
|
|
|
FreeAndNil(Doc);
|
|
|
|
end;
|
2015-01-31 18:42:22 +00:00
|
|
|
end;
|
2015-02-23 22:51:42 +00:00
|
|
|
ApplyHyperlinks(FWorksheet);
|
|
|
|
end; // for
|
2014-07-24 22:22:26 +00:00
|
|
|
|
|
|
|
finally
|
|
|
|
SheetList.Free;
|
|
|
|
FreeAndNil(Doc);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2015-02-04 19:50:50 +00:00
|
|
|
procedure TsSpreadOOXMLReader.ReadFromStream(AStream: TStream);
|
2014-10-08 08:13:45 +00:00
|
|
|
begin
|
2015-02-04 19:50:50 +00:00
|
|
|
Unused(AStream);
|
2014-10-08 08:13:45 +00:00
|
|
|
raise Exception.Create('[TsSpreadOOXMLReader.ReadFromStream] '+
|
|
|
|
'Method not implemented. Use "ReadFromFile" instead.');
|
|
|
|
end;
|
2014-07-24 22:22:26 +00:00
|
|
|
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{------------------------------------------------------------------------------}
|
|
|
|
{ TsSpreadOOXMLWriter }
|
|
|
|
{------------------------------------------------------------------------------}
|
|
|
|
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Constructor of the OOXML writer
|
|
|
|
|
|
|
|
Defines the date mode and the limitations of the file format.
|
|
|
|
Initializes the format settings to be used when writing to xml.
|
|
|
|
-------------------------------------------------------------------------------}
|
|
|
|
constructor TsSpreadOOXMLWriter.Create(AWorkbook: TsWorkbook);
|
|
|
|
begin
|
|
|
|
inherited Create(AWorkbook);
|
|
|
|
|
|
|
|
// Initial base date in case it won't be set otherwise.
|
|
|
|
// Use 1900 to get a bit more range between 1900..1904.
|
|
|
|
FDateMode := XlsxSettings.DateMode;
|
|
|
|
|
|
|
|
// Special version of FormatSettings using a point decimal separator for sure.
|
|
|
|
FPointSeparatorSettings := DefaultFormatSettings;
|
|
|
|
FPointSeparatorSettings.DecimalSeparator := '.';
|
|
|
|
|
|
|
|
// http://en.wikipedia.org/wiki/List_of_spreadsheet_software#Specifications
|
|
|
|
FLimitations.MaxColCount := 16384;
|
|
|
|
FLimitations.MaxRowCount := 1048576;
|
|
|
|
end;
|
|
|
|
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2015-01-24 00:36:10 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Looks for the combination of border attributes of the given format record in
|
|
|
|
the FBorderList and returns its index.
|
|
|
|
-------------------------------------------------------------------------------}
|
2015-01-23 21:54:23 +00:00
|
|
|
function TsSpreadOOXMLWriter.FindBorderInList(AFormat: PsCellFormat): Integer;
|
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
fmt: PsCellFormat;
|
|
|
|
begin
|
|
|
|
// No cell, or border-less --> index 0
|
2015-05-01 15:14:25 +00:00
|
|
|
if (AFormat = nil) or not (uffBorder in AFormat^.UsedFormattingFields) then begin
|
2015-01-23 21:54:23 +00:00
|
|
|
Result := 0;
|
|
|
|
exit;
|
|
|
|
end;
|
2014-07-13 22:09:27 +00:00
|
|
|
|
2015-01-23 21:54:23 +00:00
|
|
|
for i:=0 to High(FBorderList) do begin
|
|
|
|
fmt := FBorderList[i];
|
|
|
|
if SameCellBorders(fmt, AFormat) then begin
|
|
|
|
Result := i;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Not found --> return -1
|
|
|
|
Result := -1;
|
|
|
|
end;
|
2014-07-13 22:09:27 +00:00
|
|
|
|
2015-01-24 00:36:10 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Looks for the combination of fill attributes of the given format record in the
|
|
|
|
FFillList and returns its index.
|
|
|
|
-------------------------------------------------------------------------------}
|
2015-01-23 21:54:23 +00:00
|
|
|
function TsSpreadOOXMLWriter.FindFillInList(AFormat: PsCellFormat): Integer;
|
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
fmt: PsCellFormat;
|
|
|
|
begin
|
2015-02-17 23:32:00 +00:00
|
|
|
if (AFormat = nil) or not (uffBackground in AFormat^.UsedFormattingFields)
|
2015-01-23 21:54:23 +00:00
|
|
|
then begin
|
|
|
|
Result := 0;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Index 0 is "no fill" which already has been handled.
|
2015-02-17 23:32:00 +00:00
|
|
|
for i:=1 to High(FFillList) do begin
|
|
|
|
fmt := FFillList[i];
|
|
|
|
if (fmt <> nil) and (uffBackground in fmt^.UsedFormattingFields) then
|
|
|
|
begin
|
|
|
|
if (AFormat^.Background.Style = fmt^.Background.Style) and
|
|
|
|
(AFormat^.Background.BgColor = fmt^.Background.BgColor) and
|
|
|
|
(AFormat^.Background.FgColor = fmt^.Background.FgColor)
|
|
|
|
then begin
|
|
|
|
Result := i;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{
|
2015-01-23 21:54:23 +00:00
|
|
|
// Index 1 is also pre-defined (gray 25%)
|
|
|
|
for i:=2 to High(FFillList) do begin
|
|
|
|
fmt := FFillList[i];
|
|
|
|
if (fmt <> nil) and (uffBackgroundColor in fmt^.UsedFormattingFields) then
|
|
|
|
if (AFormat^.BackgroundColor = fmt^.BackgroundColor) then
|
|
|
|
begin
|
|
|
|
Result := i;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
end;
|
2015-02-17 23:32:00 +00:00
|
|
|
}
|
2015-01-23 21:54:23 +00:00
|
|
|
|
2015-02-17 23:32:00 +00:00
|
|
|
// Not found --> return -1
|
2015-01-23 21:54:23 +00:00
|
|
|
Result := -1;
|
|
|
|
end;
|
|
|
|
|
2014-07-12 22:12:38 +00:00
|
|
|
{ Determines the formatting index which a given cell has in list of
|
|
|
|
"FormattingStyles" which correspond to the section cellXfs of the styles.xml
|
|
|
|
file. }
|
|
|
|
function TsSpreadOOXMLWriter.GetStyleIndex(ACell: PCell): Cardinal;
|
2015-01-23 21:54:23 +00:00
|
|
|
begin
|
|
|
|
Result := ACell^.FormatIndex;
|
|
|
|
end;
|
2014-07-12 22:12:38 +00:00
|
|
|
|
2014-07-13 22:09:27 +00:00
|
|
|
{ Creates a list of all border styles found in the workbook.
|
|
|
|
The list contains indexes into the array FFormattingStyles for each unique
|
|
|
|
combination of border attributes.
|
|
|
|
To be used for the styles.xml. }
|
|
|
|
procedure TsSpreadOOXMLWriter.ListAllBorders;
|
|
|
|
var
|
2015-01-23 21:54:23 +00:00
|
|
|
//styleCell: PCell;
|
2014-07-13 22:09:27 +00:00
|
|
|
i, n : Integer;
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt: PsCellFormat;
|
2014-07-13 22:09:27 +00:00
|
|
|
begin
|
|
|
|
// first list entry is a no-border cell
|
2015-01-23 21:54:23 +00:00
|
|
|
n := 1;
|
|
|
|
SetLength(FBorderList, n);
|
2014-07-13 22:09:27 +00:00
|
|
|
FBorderList[0] := nil;
|
|
|
|
|
2015-01-23 21:54:23 +00:00
|
|
|
for i := 0 to FWorkbook.GetNumCellFormats - 1 do
|
|
|
|
begin
|
|
|
|
fmt := FWorkbook.GetPointerToCellFormat(i);
|
|
|
|
if FindBorderInList(fmt) = -1 then
|
|
|
|
begin
|
|
|
|
SetLength(FBorderList, n+1);
|
|
|
|
FBorderList[n] := fmt;
|
|
|
|
inc(n);
|
|
|
|
end;
|
|
|
|
end;
|
2014-07-13 22:09:27 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
{ Creates a list of all fill styles found in the workbook.
|
|
|
|
The list contains indexes into the array FFormattingStyles for each unique
|
|
|
|
combination of fill attributes.
|
|
|
|
Currently considers only backgroundcolor, fill style is always "solid".
|
|
|
|
To be used for styles.xml. }
|
|
|
|
procedure TsSpreadOOXMLWriter.ListAllFills;
|
|
|
|
var
|
|
|
|
i, n: Integer;
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt: PsCellFormat;
|
2014-07-13 22:09:27 +00:00
|
|
|
begin
|
|
|
|
// Add built-in fills first.
|
2015-01-23 21:54:23 +00:00
|
|
|
n := 2;
|
|
|
|
SetLength(FFillList, n);
|
2014-07-13 22:09:27 +00:00
|
|
|
FFillList[0] := nil; // built-in "no fill"
|
|
|
|
FFillList[1] := nil; // built-in "gray125"
|
|
|
|
|
2015-01-23 21:54:23 +00:00
|
|
|
for i := 0 to FWorkbook.GetNumCellFormats - 1 do
|
|
|
|
begin
|
|
|
|
fmt := FWorkbook.GetPointerToCellFormat(i);
|
|
|
|
if FindFillInList(fmt) = -1 then
|
|
|
|
begin
|
|
|
|
SetLength(FFillList, n+1);
|
|
|
|
FFillList[n] := fmt;
|
|
|
|
inc(n);
|
|
|
|
end;
|
|
|
|
end;
|
2014-07-13 22:09:27 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TsSpreadOOXMLWriter.WriteBorderList(AStream: TStream);
|
2014-07-31 19:34:54 +00:00
|
|
|
const
|
|
|
|
LINESTYLE_NAME: Array[TsLineStyle] of String = (
|
|
|
|
'thin', 'medium', 'dashed', 'dotted', 'thick', 'double', 'hair');
|
2014-07-13 22:09:27 +00:00
|
|
|
|
2015-01-23 21:54:23 +00:00
|
|
|
procedure WriteBorderStyle(AStream: TStream; AFormatRecord: PsCellFormat;
|
|
|
|
ABorder: TsCellBorder; ABorderName: String);
|
2014-07-13 22:09:27 +00:00
|
|
|
{ border names found in xlsx files for Excel selections:
|
|
|
|
"thin", "hair", "dotted", "dashed", "dashDotDot", "dashDot", "mediumDashDotDot",
|
|
|
|
"slantDashDot", "mediumDashDot", "mediumDashed", "medium", "thick", "double" }
|
|
|
|
var
|
|
|
|
styleName: String;
|
2015-05-28 20:08:24 +00:00
|
|
|
colorStr: String;
|
|
|
|
rgb: TsColor;
|
2014-07-13 22:09:27 +00:00
|
|
|
begin
|
2015-01-23 21:54:23 +00:00
|
|
|
if (ABorder in AFormatRecord^.Border) then begin
|
2014-07-13 22:09:27 +00:00
|
|
|
// Line style
|
2015-01-23 21:54:23 +00:00
|
|
|
styleName := LINESTYLE_NAME[AFormatRecord^.BorderStyles[ABorder].LineStyle];
|
|
|
|
|
2014-07-13 22:09:27 +00:00
|
|
|
// Border color
|
2015-05-28 20:08:24 +00:00
|
|
|
rgb := AFormatRecord^.BorderStyles[ABorder].Color;
|
|
|
|
colorStr := ColorToHTMLColorStr(rgb, true);
|
2014-07-13 22:09:27 +00:00
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<%s style="%s"><color rgb="%s" /></%s>',
|
2015-05-28 20:08:24 +00:00
|
|
|
[ABorderName, styleName, colorStr, ABorderName]
|
2014-07-13 22:09:27 +00:00
|
|
|
));
|
|
|
|
end else
|
|
|
|
AppendToStream(AStream, Format(
|
2014-07-31 19:34:54 +00:00
|
|
|
'<%s />', [ABorderName]));
|
2014-07-13 22:09:27 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
|
|
i: Integer;
|
2014-07-31 19:34:54 +00:00
|
|
|
diag: String;
|
2014-07-13 22:09:27 +00:00
|
|
|
begin
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<borders count="%d">', [Length(FBorderList)]));
|
|
|
|
|
2015-01-23 21:54:23 +00:00
|
|
|
// index 0 -- built-in "no borders"
|
2014-07-13 22:09:27 +00:00
|
|
|
AppendToStream(AStream,
|
|
|
|
'<border>',
|
|
|
|
'<left /><right /><top /><bottom /><diagonal />',
|
|
|
|
'</border>');
|
|
|
|
|
|
|
|
for i:=1 to High(FBorderList) do begin
|
2014-07-31 19:34:54 +00:00
|
|
|
diag := '';
|
2015-05-01 15:14:25 +00:00
|
|
|
if (cbDiagUp in FBorderList[i]^.Border) then
|
|
|
|
diag := diag + ' diagonalUp="1"';
|
|
|
|
if (cbDiagDown in FBorderList[i]^.Border) then diag := diag + ' diagonalDown="1"';
|
2014-07-13 22:09:27 +00:00
|
|
|
AppendToStream(AStream,
|
2014-07-31 19:34:54 +00:00
|
|
|
'<border' + diag + '>');
|
2015-01-23 21:54:23 +00:00
|
|
|
WriteBorderStyle(AStream, FBorderList[i], cbWest, 'left');
|
|
|
|
WriteBorderStyle(AStream, FBorderList[i], cbEast, 'right');
|
|
|
|
WriteBorderStyle(AStream, FBorderList[i], cbNorth, 'top');
|
|
|
|
WriteBorderStyle(AStream, FBorderList[i], cbSouth, 'bottom');
|
|
|
|
// OOXML uses the same border style for both diagonals. In agreement with
|
|
|
|
// the biff implementation we select the style from the diagonal-up line.
|
|
|
|
WriteBorderStyle(AStream, FBorderList[i], cbDiagUp, 'diagonal');
|
2014-07-13 22:09:27 +00:00
|
|
|
AppendToStream(AStream,
|
|
|
|
'</border>');
|
|
|
|
end;
|
|
|
|
|
|
|
|
AppendToStream(AStream,
|
|
|
|
'</borders>');
|
|
|
|
end;
|
|
|
|
|
2014-08-04 19:11:17 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteCols(AStream: TStream; AWorksheet: TsWorksheet);
|
2014-07-15 13:31:45 +00:00
|
|
|
var
|
|
|
|
col: PCol;
|
|
|
|
c: Integer;
|
|
|
|
begin
|
2014-08-04 19:11:17 +00:00
|
|
|
if AWorksheet.Cols.Count = 0 then
|
2014-07-15 13:31:45 +00:00
|
|
|
exit;
|
|
|
|
|
|
|
|
AppendToStream(AStream,
|
|
|
|
'<cols>');
|
|
|
|
|
2014-08-04 19:11:17 +00:00
|
|
|
for c:=0 to AWorksheet.GetLastColIndex do begin
|
|
|
|
col := AWorksheet.FindCol(c);
|
2014-07-15 13:31:45 +00:00
|
|
|
if col <> nil then
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<col min="%d" max="%d" width="%g" customWidth="1" />',
|
2015-05-01 15:14:25 +00:00
|
|
|
[c+1, c+1, col^.Width], FPointSeparatorSettings)
|
2014-07-15 13:31:45 +00:00
|
|
|
);
|
|
|
|
end;
|
|
|
|
|
|
|
|
AppendToStream(AStream,
|
|
|
|
'</cols>');
|
|
|
|
end;
|
|
|
|
|
2015-02-02 18:51:13 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteComments(AWorksheet: TsWorksheet);
|
2015-03-05 10:35:32 +00:00
|
|
|
var
|
|
|
|
comment: PsComment;
|
|
|
|
txt: String;
|
2015-02-02 18:51:13 +00:00
|
|
|
begin
|
2015-02-24 16:57:36 +00:00
|
|
|
if AWorksheet.Comments.Count = 0 then
|
|
|
|
exit;
|
|
|
|
|
2015-02-02 18:51:13 +00:00
|
|
|
// Create the comments stream
|
|
|
|
SetLength(FSComments, FCurSheetNum + 1);
|
|
|
|
if (boBufStream in Workbook.Options) then
|
|
|
|
FSComments[FCurSheetNum] := TBufStream.Create(GetTempFileName('', Format('fpsCMNT%d', [FCurSheetNum])))
|
|
|
|
else
|
|
|
|
FSComments[FCurSheetNum] := TMemoryStream.Create;
|
|
|
|
|
|
|
|
// Header
|
|
|
|
AppendToStream(FSComments[FCurSheetNum],
|
|
|
|
XML_HEADER);
|
|
|
|
AppendToStream(FSComments[FCurSheetNum], Format(
|
|
|
|
'<comments xmlns="%s">', [SCHEMAS_SPREADML]));
|
|
|
|
AppendToStream(FSComments[FCurSheetNum],
|
|
|
|
'<authors>'+
|
2015-02-06 20:11:39 +00:00
|
|
|
'<author />'+ // Not necessary to specify an author here. But the node must exist!
|
2015-02-02 18:51:13 +00:00
|
|
|
'</authors>');
|
|
|
|
AppendToStream(FSComments[FCurSheetNum],
|
|
|
|
'<commentList>');
|
|
|
|
|
|
|
|
// Comments
|
2015-03-05 10:35:32 +00:00
|
|
|
for comment in AWorksheet.Comments do
|
|
|
|
begin
|
|
|
|
txt := comment^.Text;
|
|
|
|
ValidXMLText(txt);
|
|
|
|
|
|
|
|
// Write comment text to Comments stream
|
|
|
|
AppendToStream(FSComments[FCurSheetNum], Format(
|
|
|
|
'<comment ref="%s" authorId="0">', [GetCellString(comment^.Row, comment^.Col)]) +
|
|
|
|
'<text>'+
|
|
|
|
'<r>'+
|
|
|
|
'<rPr>'+ // thie entire node could be omitted, but then Excel uses some ugly default font
|
|
|
|
'<sz val="9"/>'+
|
|
|
|
'<color rgb="000000" />'+ // Excel files have color index 81 here, but it could be that this does not exist in fps files --> use rgb instead
|
|
|
|
'<fFont vel="Arial" />'+ // It is not harmful to Excel if the font does not exist.
|
|
|
|
'<charset val="1" />'+
|
|
|
|
'</rPr>'+
|
|
|
|
'<t xml:space="preserve">' + txt + '</t>' +
|
|
|
|
'</r>' +
|
|
|
|
'</text>' +
|
|
|
|
'</comment>');
|
|
|
|
end;
|
|
|
|
|
2015-02-02 18:51:13 +00:00
|
|
|
// Footer
|
|
|
|
AppendToStream(FSComments[FCurSheetNum],
|
|
|
|
'</commentList>');
|
|
|
|
AppendToStream(FSComments[FCurSheetNum],
|
|
|
|
'</comments>');
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TsSpreadOOXMLWriter.WriteDimension(AStream: TStream;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
r1,c1,r2,c2: Cardinal;
|
|
|
|
dim: String;
|
|
|
|
begin
|
|
|
|
GetSheetDimensions(AWorksheet, r1, r2, c1, c2);
|
|
|
|
if (r1=r2) and (c1=c2) then
|
|
|
|
dim := GetCellString(r1, c1)
|
|
|
|
else
|
|
|
|
dim := GetCellRangeString(r1, c1, r2, c2);
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<dimension ref="%s" />', [dim]));
|
|
|
|
end;
|
|
|
|
|
2014-07-13 22:09:27 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteFillList(AStream: TStream);
|
|
|
|
var
|
|
|
|
i: Integer;
|
2015-02-17 23:32:00 +00:00
|
|
|
pt, bc, fc: string;
|
2014-07-13 22:09:27 +00:00
|
|
|
begin
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<fills count="%d">', [Length(FFillList)]));
|
|
|
|
|
|
|
|
// index 0 -- built-in empty fill
|
|
|
|
AppendToStream(AStream,
|
|
|
|
'<fill>',
|
|
|
|
'<patternFill patternType="none" />',
|
|
|
|
'</fill>');
|
|
|
|
|
|
|
|
// index 1 -- built-in gray125 pattern
|
|
|
|
AppendToStream(AStream,
|
|
|
|
'<fill>',
|
|
|
|
'<patternFill patternType="gray125" />',
|
|
|
|
'</fill>');
|
|
|
|
|
|
|
|
// user-defined fills
|
|
|
|
for i:=2 to High(FFillList) do begin
|
2015-02-17 23:32:00 +00:00
|
|
|
pt := PATTERN_TYPES[FFillList[i]^.Background.Style];
|
|
|
|
if FFillList[i]^.Background.FgColor = scTransparent then
|
|
|
|
fc := 'auto="1"'
|
|
|
|
else
|
2015-05-28 20:08:24 +00:00
|
|
|
fc := Format('rgb="%s"', [Copy(ColorToHTMLColorStr(FFillList[i]^.Background.FgColor), 2, MaxInt)]);
|
2015-05-01 15:14:25 +00:00
|
|
|
if FFillList[i]^.Background.BgColor = scTransparent then
|
2015-02-17 23:32:00 +00:00
|
|
|
bc := 'auto="1"'
|
|
|
|
else
|
2015-05-28 20:08:24 +00:00
|
|
|
bc := Format('rgb="%s"', [Copy(ColorToHTMLColorStr(FFillList[i]^.Background.BgColor), 2, MaxInt)]);
|
2014-07-13 22:09:27 +00:00
|
|
|
AppendToStream(AStream,
|
2015-02-17 23:32:00 +00:00
|
|
|
'<fill>');
|
2014-07-13 22:09:27 +00:00
|
|
|
AppendToStream(AStream, Format(
|
2015-02-17 23:32:00 +00:00
|
|
|
'<patternFill patternType="%s">', [pt]) + Format(
|
|
|
|
'<fgColor %s />', [fc]) + Format(
|
|
|
|
'<bgColor %s />', [bc]) +
|
|
|
|
// '<bgColor indexed="64" />' +
|
|
|
|
'</patternFill>' +
|
2014-07-13 22:09:27 +00:00
|
|
|
'</fill>');
|
|
|
|
end;
|
|
|
|
|
|
|
|
AppendToStream(FSStyles,
|
|
|
|
'</fills>');
|
|
|
|
end;
|
|
|
|
|
2015-07-09 11:10:15 +00:00
|
|
|
{ Writes font parameters to the stream.
|
|
|
|
ATag is "font" for the entry in "styles.xml", or "rPr" for the entry for
|
|
|
|
richtext parameters in the shared string list. }
|
|
|
|
procedure TsSpreadOOXMLWriter.WriteFont(AStream: TStream; AFont: TsFont;
|
|
|
|
ATag: String);
|
|
|
|
var
|
|
|
|
s: String;
|
|
|
|
begin
|
|
|
|
s := '';
|
|
|
|
s := s + Format('<sz val="%g" />', [AFont.Size], FPointSeparatorSettings);
|
|
|
|
s := s + Format('<name val="%s" />', [AFont.FontName]);
|
|
|
|
if (fssBold in AFont.Style) then
|
|
|
|
s := s + '<b />';
|
|
|
|
if (fssItalic in AFont.Style) then
|
|
|
|
s := s + '<i />';
|
|
|
|
if (fssUnderline in AFont.Style) then
|
|
|
|
s := s + '<u />';
|
|
|
|
if (fssStrikeout in AFont.Style) then
|
|
|
|
s := s + '<strike />';
|
|
|
|
if AFont.Color <> scBlack then
|
|
|
|
s := s + Format('<color rgb="%s" />', [Copy(ColorToHTMLColorStr(AFont.Color), 2, MaxInt)]);
|
|
|
|
case AFont.Position of
|
|
|
|
fpSubscript : s := s + '<vertAlign val="subscript" />';
|
|
|
|
fpSuperscript: s := s + '<vertAlign val="superscript" />';
|
|
|
|
end;
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<%s>%s</%s>', [ATag, s, ATag]));
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ Writes the fontlist of the workbook to the stream. }
|
2014-07-12 22:12:38 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteFontList(AStream: TStream);
|
2014-07-11 22:43:00 +00:00
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
font: TsFont;
|
|
|
|
begin
|
2015-07-09 11:10:15 +00:00
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<fonts count="%d">', [Workbook.GetFontCount]));
|
2014-07-11 22:43:00 +00:00
|
|
|
for i:=0 to Workbook.GetFontCount-1 do begin
|
|
|
|
font := Workbook.GetFont(i);
|
2015-07-09 11:10:15 +00:00
|
|
|
WriteFont(AStream, font, 'font');
|
2014-07-11 22:43:00 +00:00
|
|
|
end;
|
|
|
|
AppendToStream(AStream,
|
2015-07-09 11:10:15 +00:00
|
|
|
'</fonts>');
|
2014-07-11 22:43:00 +00:00
|
|
|
end;
|
|
|
|
|
2015-05-01 15:14:25 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteHeaderFooter(AStream: TStream;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
hasHeader: Boolean;
|
|
|
|
hasFooter: Boolean;
|
|
|
|
i: Integer;
|
|
|
|
s: String;
|
|
|
|
begin
|
|
|
|
hasHeader := false;
|
|
|
|
hasFooter := false;
|
|
|
|
|
|
|
|
with AWorksheet.PageLayout do
|
|
|
|
begin
|
|
|
|
for i:=HEADER_FOOTER_INDEX_FIRST to HEADER_FOOTER_INDEX_EVEN do
|
|
|
|
begin
|
|
|
|
if Headers[i] <> '' then
|
|
|
|
hasHeader := true;
|
|
|
|
if Footers[i] <> '' then
|
|
|
|
hasFooter := true;
|
|
|
|
end;
|
|
|
|
if not (hasHeader or hasFooter) then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
s := '';
|
|
|
|
if poDifferentFirst in Options then
|
|
|
|
s := s + ' differentFirst="1"';
|
|
|
|
if poDifferentOddEven in Options then
|
|
|
|
s := s + ' differentOddEven="1"';
|
|
|
|
|
|
|
|
AppendToStream(AStream,
|
2015-05-02 23:04:59 +00:00
|
|
|
'<headerFooter' + s + '>');
|
2015-05-01 15:14:25 +00:00
|
|
|
|
|
|
|
if Headers[HEADER_FOOTER_INDEX_ODD] <> '' then
|
|
|
|
AppendToStream(AStream,
|
2015-05-02 23:04:59 +00:00
|
|
|
'<oddHeader>' + UTF8TextToXMLText(Headers[HEADER_FOOTER_INDEX_ODD]) + '</oddHeader>');
|
2015-05-01 15:14:25 +00:00
|
|
|
if Footers[HEADER_FOOTER_INDEX_ODD] <> '' then
|
|
|
|
AppendToStream(AStream,
|
2015-05-02 23:04:59 +00:00
|
|
|
'<oddFooter>' + UTF8TextToXMLText(Footers[HEADER_FOOTER_INDEX_ODD]) + '</oddFooter>');
|
2015-05-01 15:14:25 +00:00
|
|
|
|
|
|
|
if poDifferentFirst in AWorksheet.PageLayout.Options then
|
|
|
|
begin
|
|
|
|
if Headers[HEADER_FOOTER_INDEX_FIRST] <> '' then
|
|
|
|
AppendToStream(AStream,
|
2015-05-02 23:04:59 +00:00
|
|
|
'<firstHeader>' + UTF8TextToXMLText(Headers[HEADER_FOOTER_INDEX_FIRST]) + '</firstHeader>');
|
2015-05-01 15:14:25 +00:00
|
|
|
if Footers[HEADER_FOOTER_INDEX_FIRST] <> '' then
|
|
|
|
AppendToStream(AStream,
|
2015-05-02 23:04:59 +00:00
|
|
|
'<firstFooter>' + UTF8TextToXMLText(Footers[HEADER_FOOTER_INDEX_FIRST]) + '</firstFooter>');
|
2015-05-01 15:14:25 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
if poDifferentOddEven in Options then
|
|
|
|
begin
|
|
|
|
AppendToStream(AStream,
|
2015-05-02 23:04:59 +00:00
|
|
|
'<evenHeader>' + UTF8TextToXMLText(Headers[HEADER_FOOTER_INDEX_EVEN]) + '</evenHeader>');
|
2015-05-01 15:14:25 +00:00
|
|
|
AppendToStream(AStream,
|
2015-05-02 23:04:59 +00:00
|
|
|
'<evenFooter>' + UTF8TextToXMLText(Footers[HEADER_FOOTER_INDEX_EVEN]) + '</evenFooter>');
|
2015-05-01 15:14:25 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
AppendToStream(AStream,
|
|
|
|
'</headerFooter>');
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2015-02-24 16:57:36 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteHyperlinks(AStream: TStream;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
hyperlink: PsHyperlink;
|
2015-02-28 23:46:08 +00:00
|
|
|
target, bookmark: String;
|
2015-02-24 16:57:36 +00:00
|
|
|
s: String;
|
|
|
|
txt: String;
|
|
|
|
AVLNode: TAVLTreeNode;
|
|
|
|
begin
|
|
|
|
if AWorksheet.Hyperlinks.Count = 0 then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
AppendToStream(AStream,
|
|
|
|
'<hyperlinks>');
|
|
|
|
|
|
|
|
// Keep in sync with WriteWorksheetRels !
|
|
|
|
FNext_rID := IfThen(AWorksheet.Comments.Count = 0, 1, 3);
|
|
|
|
|
|
|
|
AVLNode := AWorksheet.Hyperlinks.FindLowest;
|
|
|
|
while AVLNode <> nil do begin
|
|
|
|
hyperlink := PsHyperlink(AVLNode.Data);
|
2015-03-02 12:23:52 +00:00
|
|
|
SplitHyperlink(hyperlink^.Target, target, bookmark);
|
2015-02-24 16:57:36 +00:00
|
|
|
s := Format('ref="%s"', [GetCellString(hyperlink^.Row, hyperlink^.Col)]);
|
2015-02-28 23:46:08 +00:00
|
|
|
if target <> '' then
|
2015-02-24 16:57:36 +00:00
|
|
|
begin
|
|
|
|
s := Format('%s r:id="rId%d"', [s, FNext_rId]);
|
|
|
|
inc(FNext_rId);
|
|
|
|
end;
|
2015-03-08 00:50:10 +00:00
|
|
|
if bookmark <> '' then //target = '' then
|
2015-02-28 23:46:08 +00:00
|
|
|
s := Format('%s location="%s"', [s, bookmark]);
|
2015-02-24 16:57:36 +00:00
|
|
|
txt := AWorksheet.ReadAsUTF8Text(hyperlink^.Row, hyperlink^.Col);
|
|
|
|
if (txt <> '') and (txt <> hyperlink^.Target) then
|
|
|
|
s := Format('%s display="%s"', [s, txt]);
|
2015-03-08 00:50:10 +00:00
|
|
|
if hyperlink^.ToolTip <> '' then begin
|
|
|
|
txt := hyperlink^.Tooltip;
|
|
|
|
ValidXMLText(txt);
|
|
|
|
s := Format('%s tooltip="%s"', [s, txt]);
|
|
|
|
end;
|
2015-02-24 16:57:36 +00:00
|
|
|
AppendToStream(AStream,
|
|
|
|
'<hyperlink ' + s + ' />');
|
|
|
|
AVLNode := AWorksheet.Hyperlinks.FindSuccessor(AVLNode);
|
|
|
|
end;
|
|
|
|
|
|
|
|
AppendToStream(AStream,
|
|
|
|
'</hyperlinks>');
|
|
|
|
end;
|
|
|
|
|
2014-09-10 16:48:34 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteMergedCells(AStream: TStream;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
2015-03-02 16:50:14 +00:00
|
|
|
rng: PsCellRange;
|
|
|
|
n: Integer;
|
2014-09-10 16:48:34 +00:00
|
|
|
begin
|
2015-03-02 16:50:14 +00:00
|
|
|
n := AWorksheet.MergedCells.Count;
|
|
|
|
if n = 0 then
|
2014-09-10 16:48:34 +00:00
|
|
|
exit;
|
|
|
|
AppendToStream(AStream, Format(
|
2015-03-02 16:50:14 +00:00
|
|
|
'<mergeCells count="%d">', [n]) );
|
2015-03-05 10:35:32 +00:00
|
|
|
for rng in AWorksheet.MergedCells do
|
2014-09-10 16:48:34 +00:00
|
|
|
AppendToStream(AStream, Format(
|
2015-05-01 15:14:25 +00:00
|
|
|
'<mergeCell ref="%s" />', [GetCellRangeString(rng^.Row1, rng^.Col1, rng^.Row2, rng^.Col2)]));
|
2014-09-10 16:48:34 +00:00
|
|
|
AppendToStream(AStream,
|
|
|
|
'</mergeCells>');
|
|
|
|
end;
|
|
|
|
|
2014-07-14 20:47:53 +00:00
|
|
|
{ Writes all number formats to the stream. Saving starts at the item with the
|
|
|
|
FirstFormatIndexInFile. }
|
|
|
|
procedure TsSpreadOOXMLWriter.WriteNumFormatList(AStream: TStream);
|
|
|
|
var
|
2015-04-18 14:58:38 +00:00
|
|
|
i, n: Integer;
|
|
|
|
numFmtStr: String;
|
|
|
|
xmlStr: String;
|
|
|
|
parser: TsNumFormatParser;
|
2014-07-14 20:47:53 +00:00
|
|
|
begin
|
2015-04-18 14:58:38 +00:00
|
|
|
xmlStr := '';
|
2014-07-14 20:47:53 +00:00
|
|
|
n := 0;
|
2015-04-18 14:58:38 +00:00
|
|
|
for i:= FFirstNumFormatIndexInFile to NumFormatList.Count-1 do
|
|
|
|
begin
|
|
|
|
numFmtStr := NumFormatList[i];
|
2015-05-31 16:06:22 +00:00
|
|
|
parser := TsNumFormatParser.Create(numFmtStr, Workbook.FormatSettings);
|
2015-04-18 14:58:38 +00:00
|
|
|
try
|
2015-04-19 22:03:33 +00:00
|
|
|
numFmtStr := UTF8TextToXMLText(parser.FormatString);
|
2015-04-18 14:58:38 +00:00
|
|
|
xmlStr := xmlStr + Format('<numFmt numFmtId="%d" formatCode="%s" />',
|
|
|
|
[i, numFmtStr]);
|
|
|
|
inc(n);
|
|
|
|
finally
|
|
|
|
parser.Free;
|
2014-07-14 20:47:53 +00:00
|
|
|
end;
|
|
|
|
end;
|
2015-04-18 14:58:38 +00:00
|
|
|
|
|
|
|
if n > 0 then
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<numFmts count="%d">', [n]),
|
|
|
|
xmlStr,
|
|
|
|
'</numFmts>'
|
|
|
|
);
|
2014-07-14 20:47:53 +00:00
|
|
|
end;
|
|
|
|
|
2015-05-28 20:08:24 +00:00
|
|
|
{ In older versions, the workbook had a color palette which was written here.
|
|
|
|
Now there is no palette any more. }
|
2014-07-29 21:02:14 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WritePalette(AStream: TStream);
|
|
|
|
begin
|
2015-05-28 20:08:24 +00:00
|
|
|
// just keep it here in case we'd need it later...
|
2015-05-31 16:06:22 +00:00
|
|
|
Unused(AStream);
|
2014-07-29 21:02:14 +00:00
|
|
|
end;
|
|
|
|
|
2015-04-29 20:00:07 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WritePageMargins(AStream: TStream;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
begin
|
|
|
|
with AWorksheet.PageLayout do
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<pageMargins left="%g" right="%g" top="%g" bottom="%g" header="%g" footer="%g" />', [
|
2015-04-30 21:55:55 +00:00
|
|
|
mmToIn(LeftMargin), mmToIn(RightMargin), mmToIn(TopMargin), mmToIn(BottomMargin),
|
|
|
|
mmToIn(HeaderMargin), mmToIn(FooterMargin) ],
|
2015-04-29 20:00:07 +00:00
|
|
|
FPointSeparatorSettings
|
|
|
|
));
|
|
|
|
end;
|
|
|
|
|
2015-04-30 21:55:55 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WritePageSetup(AStream: TStream;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
s: String;
|
|
|
|
i: Integer;
|
|
|
|
begin
|
|
|
|
s := '';
|
|
|
|
|
|
|
|
// Paper size
|
|
|
|
for i:=0 to High(PAPER_SIZES) do
|
|
|
|
if (SameValue(PAPER_SIZES[i,0], AWorksheet.PageLayout.PageHeight) and
|
|
|
|
SameValue(PAPER_SIZES[i,1], AWorksheet.PageLayout.PageWidth))
|
|
|
|
or (SameValue(PAPER_SIZES[i,1], AWorksheet.PageLayout.PageHeight) and
|
|
|
|
SameValue(PAPER_SIZES[i,0], AWorksheet.PageLayout.PageWidth))
|
|
|
|
then begin
|
|
|
|
s := Format('%s paperSize="%d"', [s, i]);
|
|
|
|
break;
|
|
|
|
end;
|
|
|
|
|
2015-05-04 17:38:56 +00:00
|
|
|
if poFitPages in AWorksheet.PageLayout.Options then
|
|
|
|
begin
|
|
|
|
// Fit width to pages
|
2015-04-30 21:55:55 +00:00
|
|
|
s := Format('%s fitToWidth="%d"', [s, AWorksheet.PageLayout.FitWidthToPages]);
|
2015-05-04 17:38:56 +00:00
|
|
|
// Fit height to pages
|
2015-04-30 21:55:55 +00:00
|
|
|
s := Format('%s fitToHeight="%d"', [s, AWorksheet.PageLayout.FitHeightToPages]);
|
2015-05-04 17:38:56 +00:00
|
|
|
end else
|
|
|
|
// Scaling factor
|
|
|
|
s := Format('%s scale="%d"', [s, AWorksheet.PageLayout.ScalingFactor]);
|
2015-04-30 21:55:55 +00:00
|
|
|
|
|
|
|
// Orientation
|
|
|
|
s := Format('%s orientation="%s"', [
|
|
|
|
s, IfThen(AWorksheet.PageLayout.Orientation = spoPortrait, 'portrait', 'landscape')
|
|
|
|
]);
|
|
|
|
|
|
|
|
// First page number
|
|
|
|
if poUseStartPageNumber in FWorksheet.PageLayout.Options then
|
2015-05-04 17:38:56 +00:00
|
|
|
s := Format('%s useFirstPageNumber="1"', [s]);
|
|
|
|
|
|
|
|
s := Format('%s firstPageNumber="%d"', [s, AWorksheet.PageLayout.StartPageNumber]);
|
2015-04-30 21:55:55 +00:00
|
|
|
|
|
|
|
// Print order
|
|
|
|
if poPrintPagesByRows in AWorksheet.PageLayout.Options then
|
|
|
|
s := s + ' pageOrder="overThenDown"';
|
|
|
|
|
|
|
|
// Monochrome
|
|
|
|
if poMonochrome in AWorksheet.PageLayout.Options then
|
|
|
|
s := s + ' blackAndWhite="1"';
|
|
|
|
|
|
|
|
// Quality
|
|
|
|
if poDraftQuality in AWOrksheet.PageLayout.Options then
|
|
|
|
s := s + ' draft="1"';
|
|
|
|
|
|
|
|
if s <> '' then
|
|
|
|
AppendToStream(AStream,
|
|
|
|
'<pageSetup' + s + ' />');
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TsSpreadOOXMLWriter.WritePrintOptions(AStream: TStream;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
s: String;
|
|
|
|
begin
|
|
|
|
s := '';
|
|
|
|
if poPrintGridLines in AWorksheet.PageLayout.Options then
|
|
|
|
s := s + ' gridLines="1"';
|
|
|
|
if poPrintHeaders in AWorksheet.PageLayout.Options then
|
|
|
|
s := s + ' headings="1"';
|
|
|
|
|
|
|
|
if s <> '' then
|
|
|
|
AppendToStream(AStream,
|
|
|
|
'<printOptions' + s + ' />');
|
|
|
|
end;
|
|
|
|
|
2014-08-04 19:11:17 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteSheetData(AStream: TStream;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
2014-08-07 22:24:22 +00:00
|
|
|
r, r1, r2: Cardinal;
|
|
|
|
c, c1, c2: Cardinal;
|
2014-08-04 19:11:17 +00:00
|
|
|
row: PRow;
|
|
|
|
value: Variant;
|
|
|
|
lCell: TCell;
|
|
|
|
styleCell: PCell;
|
2015-03-04 17:30:59 +00:00
|
|
|
cell: PCell;
|
2014-08-04 19:11:17 +00:00
|
|
|
rh: String;
|
|
|
|
h0: Single;
|
|
|
|
begin
|
|
|
|
h0 := Workbook.GetDefaultFontSize; // Point size of default font
|
|
|
|
|
|
|
|
AppendToStream(AStream,
|
|
|
|
'<sheetData>');
|
|
|
|
|
2014-08-08 14:30:19 +00:00
|
|
|
GetSheetDimensions(AWorksheet, r1, r2, c1, c2);
|
|
|
|
|
2014-08-04 19:11:17 +00:00
|
|
|
if (boVirtualMode in Workbook.Options) and Assigned(Workbook.OnWriteCellData)
|
|
|
|
then begin
|
2014-08-08 14:30:19 +00:00
|
|
|
for r := 0 to r2 do begin
|
2014-08-04 19:11:17 +00:00
|
|
|
row := AWorksheet.FindRow(r);
|
|
|
|
if row <> nil then
|
|
|
|
rh := Format(' ht="%g" customHeight="1"', [
|
2014-11-17 23:44:13 +00:00
|
|
|
(row^.Height + ROW_HEIGHT_CORRECTION)*h0],
|
|
|
|
FPointSeparatorSettings)
|
2014-08-04 19:11:17 +00:00
|
|
|
else
|
|
|
|
rh := '';
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<row r="%d" spans="1:%d"%s>', [r+1, Workbook.VirtualColCount, rh]));
|
2014-08-08 14:30:19 +00:00
|
|
|
for c := 0 to c2 do begin
|
2014-12-06 18:09:15 +00:00
|
|
|
lCell.Row := r; // to silence a compiler hint
|
2014-08-04 19:11:17 +00:00
|
|
|
InitCell(lCell);
|
|
|
|
value := varNull;
|
|
|
|
styleCell := nil;
|
|
|
|
Workbook.OnWriteCellData(Workbook, r, c, value, styleCell);
|
|
|
|
if styleCell <> nil then
|
|
|
|
lCell := styleCell^;
|
|
|
|
lCell.Row := r;
|
|
|
|
lCell.Col := c;
|
|
|
|
if VarIsNull(value) then
|
2014-09-24 13:59:51 +00:00
|
|
|
begin
|
|
|
|
if styleCell <> nil then
|
|
|
|
lCell.ContentType := cctEmpty
|
|
|
|
else
|
|
|
|
Continue;
|
|
|
|
end else
|
|
|
|
if VarIsNumeric(value) then
|
|
|
|
begin
|
2014-08-04 19:11:17 +00:00
|
|
|
lCell.ContentType := cctNumber;
|
|
|
|
lCell.NumberValue := value;
|
|
|
|
end else
|
2014-09-24 13:59:51 +00:00
|
|
|
if VarType(value) = varDate then
|
|
|
|
begin
|
2014-08-04 19:11:17 +00:00
|
|
|
lCell.ContentType := cctDateTime;
|
2015-01-08 12:14:59 +00:00
|
|
|
lCell.DateTimeValue := StrToDateTime(VarToStr(value), Workbook.FormatSettings); // was: StrToDate
|
2014-08-04 19:11:17 +00:00
|
|
|
end else
|
2014-09-24 13:59:51 +00:00
|
|
|
if VarIsStr(value) then
|
|
|
|
begin
|
2014-08-04 19:11:17 +00:00
|
|
|
lCell.ContentType := cctUTF8String;
|
|
|
|
lCell.UTF8StringValue := VarToStrDef(value, '');
|
|
|
|
end else
|
2014-09-24 13:59:51 +00:00
|
|
|
if VarIsBool(value) then
|
|
|
|
begin
|
2014-08-04 19:11:17 +00:00
|
|
|
lCell.ContentType := cctBool;
|
|
|
|
lCell.BoolValue := value <> 0;
|
|
|
|
end;
|
2015-03-05 10:35:32 +00:00
|
|
|
WriteCellToStream(AStream, @lCell);
|
2014-08-04 19:11:17 +00:00
|
|
|
varClear(value);
|
|
|
|
end;
|
|
|
|
AppendToStream(AStream,
|
|
|
|
'</row>');
|
|
|
|
end;
|
|
|
|
end else
|
|
|
|
begin
|
|
|
|
// The cells need to be written in order, row by row, cell by cell
|
2014-08-07 22:24:22 +00:00
|
|
|
for r := r1 to r2 do begin
|
2014-08-04 19:11:17 +00:00
|
|
|
// If the row has a custom height add this value to the <row> specification
|
|
|
|
row := AWorksheet.FindRow(r);
|
|
|
|
if row <> nil then
|
|
|
|
rh := Format(' ht="%g" customHeight="1"', [
|
2014-11-17 23:44:13 +00:00
|
|
|
(row^.Height + ROW_HEIGHT_CORRECTION)*h0], FPointSeparatorSettings)
|
2014-08-04 19:11:17 +00:00
|
|
|
else
|
|
|
|
rh := '';
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<row r="%d" spans="%d:%d"%s>', [r+1, c1+1, c2+1, rh]));
|
|
|
|
// Write cells belonging to this row.
|
2015-03-16 00:05:56 +00:00
|
|
|
|
|
|
|
{ // Strange: the RowEnumerator is very slow here... ?!
|
2015-03-05 10:35:32 +00:00
|
|
|
for cell in AWorksheet.Cells.GetRowEnumerator(r) do
|
|
|
|
WriteCellToStream(AStream, cell);
|
2015-03-16 00:05:56 +00:00
|
|
|
}
|
|
|
|
|
2014-08-04 19:11:17 +00:00
|
|
|
for c := c1 to c2 do begin
|
2015-03-04 17:30:59 +00:00
|
|
|
cell := AWorksheet.FindCell(r, c);
|
2015-03-16 00:05:56 +00:00
|
|
|
if Assigned(cell) then
|
|
|
|
WriteCellToStream(AStream, cell);
|
2014-08-04 19:11:17 +00:00
|
|
|
end;
|
2015-03-16 00:05:56 +00:00
|
|
|
|
2014-08-04 19:11:17 +00:00
|
|
|
AppendToStream(AStream,
|
|
|
|
'</row>');
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
AppendToStream(AStream,
|
|
|
|
'</sheetData>');
|
|
|
|
end;
|
|
|
|
|
2015-04-30 21:55:55 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteSheetPr(AStream: TStream; AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
s: String;
|
|
|
|
begin
|
|
|
|
s := '';
|
|
|
|
if (AWorksheet.PageLayout.FitWidthToPages > 0) or
|
|
|
|
(AWorksheet.PageLayout.FitHeightToPages > 0) then
|
|
|
|
s := s + ' fitToPage="1"';
|
|
|
|
if s <> '' then s := '<pageSetUpPr' + s + ' />';
|
|
|
|
|
|
|
|
if s <> '' then
|
|
|
|
AppendToStream(AStream,
|
|
|
|
'<sheetPr>' + s + '</sheetPr>');
|
|
|
|
end;
|
|
|
|
|
2014-08-04 19:11:17 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteSheetViews(AStream: TStream;
|
|
|
|
AWorksheet: TsWorksheet);
|
|
|
|
var
|
|
|
|
showGridLines: String;
|
|
|
|
showHeaders: String;
|
|
|
|
topRightCell: String;
|
|
|
|
bottomLeftCell: String;
|
|
|
|
bottomRightCell: String;
|
|
|
|
begin
|
|
|
|
// Show gridlines ?
|
2014-08-10 17:59:30 +00:00
|
|
|
showGridLines := StrUtils.IfThen(soShowGridLines in AWorksheet.Options, ' ', 'showGridLines="0" ');
|
2014-08-04 19:11:17 +00:00
|
|
|
|
|
|
|
// Show headers?
|
2014-08-10 17:59:30 +00:00
|
|
|
showHeaders := StrUtils.IfThen(soShowHeaders in AWorksheet.Options, ' ', 'showRowColHeaders="0" ');
|
2014-08-04 19:11:17 +00:00
|
|
|
|
|
|
|
// No frozen panes
|
|
|
|
if not (soHasFrozenPanes in AWorksheet.Options) or
|
|
|
|
((AWorksheet.LeftPaneWidth = 0) and (AWorksheet.TopPaneHeight = 0))
|
|
|
|
then
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<sheetViews>' +
|
|
|
|
'<sheetView workbookViewId="0" %s%s/>' +
|
|
|
|
'</sheetViews>', [
|
|
|
|
showGridLines, showHeaders
|
|
|
|
]))
|
|
|
|
else
|
|
|
|
begin // Frozen panes
|
|
|
|
topRightCell := GetCellString(0, AWorksheet.LeftPaneWidth, [rfRelRow, rfRelCol]);
|
|
|
|
bottomLeftCell := GetCellString(AWorksheet.TopPaneHeight, 0, [rfRelRow, rfRelCol]);
|
|
|
|
bottomRightCell := GetCellString(AWorksheet.TopPaneHeight, AWorksheet.LeftPaneWidth, [rfRelRow, rfRelCol]);
|
|
|
|
if (AWorksheet.LeftPaneWidth > 0) and (AWorksheet.TopPaneHeight > 0) then
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<sheetViews>' +
|
|
|
|
'<sheetView workbookViewId="0" %s%s>'+
|
|
|
|
'<pane xSplit="%d" ySplit="%d" topLeftCell="%s" activePane="bottomRight" state="frozen" />' +
|
|
|
|
'<selection pane="topRight" activeCell="%s" sqref="%s" />' +
|
|
|
|
'<selection pane="bottomLeft" activeCell="%s" sqref="%s" />' +
|
|
|
|
'<selection pane="bottomRight" activeCell="%s" sqref="%s" />' +
|
|
|
|
'</sheetView>' +
|
|
|
|
'</sheetViews>', [
|
|
|
|
showGridLines, showHeaders,
|
|
|
|
AWorksheet.LeftPaneWidth, AWorksheet.TopPaneHeight, bottomRightCell,
|
|
|
|
topRightCell, topRightCell,
|
|
|
|
bottomLeftCell, bottomLeftCell,
|
|
|
|
bottomRightCell, bottomrightCell
|
|
|
|
]))
|
|
|
|
else
|
|
|
|
if (AWorksheet.LeftPaneWidth > 0) then
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<sheetViews>' +
|
|
|
|
'<sheetView workbookViewId="0" %s%s>'+
|
|
|
|
'<pane xSplit="%d" topLeftCell="%s" activePane="topRight" state="frozen" />' +
|
|
|
|
'<selection pane="topRight" activeCell="%s" sqref="%s" />' +
|
|
|
|
'</sheetView>' +
|
|
|
|
'</sheetViews>', [
|
|
|
|
showGridLines, showHeaders,
|
|
|
|
AWorksheet.LeftPaneWidth, topRightCell,
|
|
|
|
topRightCell, topRightCell
|
|
|
|
]))
|
|
|
|
else
|
|
|
|
if (AWorksheet.TopPaneHeight > 0) then
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<sheetViews>'+
|
|
|
|
'<sheetView workbookViewId="0" %s%s>'+
|
|
|
|
'<pane ySplit="%d" topLeftCell="%s" activePane="bottomLeft" state="frozen" />'+
|
|
|
|
'<selection pane="bottomLeft" activeCell="%s" sqref="%s" />' +
|
|
|
|
'</sheetView>'+
|
|
|
|
'</sheetViews>', [
|
|
|
|
showGridLines, showHeaders,
|
|
|
|
AWorksheet.TopPaneHeight, bottomLeftCell,
|
|
|
|
bottomLeftCell, bottomLeftCell
|
|
|
|
]));
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2015-01-23 21:54:23 +00:00
|
|
|
{ Writes the style list which the workbook has collected in its FormatList }
|
2014-07-12 22:12:38 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteStyleList(AStream: TStream; ANodeName: String);
|
|
|
|
var
|
2015-01-23 21:54:23 +00:00
|
|
|
// styleCell: TCell;
|
2014-07-13 15:23:07 +00:00
|
|
|
s, sAlign: String;
|
2014-07-12 22:12:38 +00:00
|
|
|
fontID: Integer;
|
2015-04-18 14:58:38 +00:00
|
|
|
numFmtParams: TsNumFormatParams;
|
|
|
|
numFmtStr: String;
|
2014-07-12 22:12:38 +00:00
|
|
|
fillId: Integer;
|
|
|
|
borderId: Integer;
|
2014-07-14 20:47:53 +00:00
|
|
|
idx: Integer;
|
2015-01-23 21:54:23 +00:00
|
|
|
fmt: PsCellFormat;
|
|
|
|
i: Integer;
|
2014-07-12 22:12:38 +00:00
|
|
|
begin
|
|
|
|
AppendToStream(AStream, Format(
|
2015-01-23 21:54:23 +00:00
|
|
|
'<%s count="%d">', [ANodeName, FWorkbook.GetNumCellFormats]));
|
2014-07-12 22:12:38 +00:00
|
|
|
|
2015-01-23 21:54:23 +00:00
|
|
|
for i:=0 to FWorkbook.GetNumCellFormats-1 do
|
|
|
|
begin
|
|
|
|
fmt := FWorkbook.GetPointerToCellFormat(i);
|
2014-07-12 22:12:38 +00:00
|
|
|
s := '';
|
2014-07-13 15:23:07 +00:00
|
|
|
sAlign := '';
|
2014-07-12 22:12:38 +00:00
|
|
|
|
|
|
|
{ Number format }
|
2015-01-23 21:54:23 +00:00
|
|
|
if (uffNumberFormat in fmt^.UsedFormattingFields) then
|
|
|
|
begin
|
2015-04-18 14:58:38 +00:00
|
|
|
numFmtParams := Workbook.GetNumberFormat(fmt^.NumberFormatIndex);
|
|
|
|
if numFmtParams <> nil then
|
|
|
|
begin
|
2015-04-19 22:03:33 +00:00
|
|
|
numFmtStr := numFmtParams.NumFormatStr;
|
2015-04-18 14:58:38 +00:00
|
|
|
idx := NumFormatList.IndexOf(numFmtStr);
|
|
|
|
end else
|
|
|
|
idx := 0; // "General" format is at index 0
|
|
|
|
s := s + Format('numFmtId="%d" applyNumberFormat="1" ', [idx]);
|
2014-07-14 20:47:53 +00:00
|
|
|
end;
|
2014-07-12 22:12:38 +00:00
|
|
|
|
|
|
|
{ Font }
|
|
|
|
fontId := 0;
|
2015-01-23 21:54:23 +00:00
|
|
|
if (uffFont in fmt^.UsedFormattingFields) then
|
|
|
|
fontID := fmt^.FontIndex;
|
2014-07-12 22:12:38 +00:00
|
|
|
s := s + Format('fontId="%d" ', [fontId]);
|
|
|
|
if fontID > 0 then s := s + 'applyFont="1" ';
|
|
|
|
|
|
|
|
if ANodeName = 'cellXfs' then s := s + 'xfId="0" ';
|
|
|
|
|
2014-07-13 14:58:12 +00:00
|
|
|
{ Text rotation }
|
2015-01-23 21:54:23 +00:00
|
|
|
if (uffTextRotation in fmt^.UsedFormattingFields) then
|
|
|
|
case fmt^.TextRotation of
|
|
|
|
trHorizontal : ;
|
2014-07-13 15:23:07 +00:00
|
|
|
rt90DegreeClockwiseRotation : sAlign := sAlign + Format('textRotation="%d" ', [180]);
|
|
|
|
rt90DegreeCounterClockwiseRotation: sAlign := sAlign + Format('textRotation="%d" ', [90]);
|
|
|
|
rtStacked : sAlign := sAlign + Format('textRotation="%d" ', [255]);
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ Text alignment }
|
2015-01-23 21:54:23 +00:00
|
|
|
if (uffHorAlign in fmt^.UsedFormattingFields) and (fmt^.HorAlignment <> haDefault)
|
|
|
|
then
|
2015-05-01 15:14:25 +00:00
|
|
|
case fmt^.HorAlignment of
|
2014-07-13 15:23:07 +00:00
|
|
|
haLeft : sAlign := sAlign + 'horizontal="left" ';
|
|
|
|
haCenter: sAlign := sAlign + 'horizontal="center" ';
|
|
|
|
haRight : sAlign := sAlign + 'horizontal="right" ';
|
|
|
|
end;
|
|
|
|
|
2015-01-23 21:54:23 +00:00
|
|
|
if (uffVertAlign in fmt^.UsedFormattingFields) and (fmt^.VertAlignment <> vaDefault)
|
|
|
|
then
|
2015-05-01 15:14:25 +00:00
|
|
|
case fmt^.VertAlignment of
|
2014-07-13 15:23:07 +00:00
|
|
|
vaTop : sAlign := sAlign + 'vertical="top" ';
|
|
|
|
vaCenter: sAlign := sAlign + 'vertical="center" ';
|
|
|
|
vaBottom: sAlign := sAlign + 'vertical="bottom" ';
|
2014-07-13 14:58:12 +00:00
|
|
|
end;
|
|
|
|
|
2015-01-23 21:54:23 +00:00
|
|
|
if (uffWordWrap in fmt^.UsedFormattingFields) then
|
2014-07-14 07:58:45 +00:00
|
|
|
sAlign := sAlign + 'wrapText="1" ';
|
|
|
|
|
2014-07-12 22:12:38 +00:00
|
|
|
{ Fill }
|
2015-05-01 15:14:25 +00:00
|
|
|
if (uffBackground in fmt^.UsedFormattingFields) then
|
2015-01-23 21:54:23 +00:00
|
|
|
begin
|
|
|
|
fillID := FindFillInList(fmt);
|
2014-07-14 07:58:45 +00:00
|
|
|
if fillID = -1 then fillID := 0;
|
|
|
|
s := s + Format('fillId="%d" applyFill="1" ', [fillID]);
|
|
|
|
end;
|
2014-07-12 22:12:38 +00:00
|
|
|
|
|
|
|
{ Border }
|
2015-01-23 21:54:23 +00:00
|
|
|
if (uffBorder in fmt^.UsedFormattingFields) then
|
|
|
|
begin
|
|
|
|
borderID := FindBorderInList(fmt);
|
2014-07-14 07:58:45 +00:00
|
|
|
if borderID = -1 then borderID := 0;
|
|
|
|
s := s + Format('borderId="%d" applyBorder="1" ', [borderID]);
|
|
|
|
end;
|
2014-07-12 22:12:38 +00:00
|
|
|
|
|
|
|
{ Write everything to stream }
|
2014-07-13 15:23:07 +00:00
|
|
|
if sAlign = '' then
|
2014-07-13 14:58:12 +00:00
|
|
|
AppendToStream(AStream,
|
|
|
|
'<xf ' + s + '/>')
|
|
|
|
else
|
|
|
|
AppendToStream(AStream,
|
2014-07-13 15:23:07 +00:00
|
|
|
'<xf ' + s + 'applyAlignment="1">',
|
|
|
|
'<alignment ' + sAlign + ' />',
|
2014-07-13 14:58:12 +00:00
|
|
|
'</xf>');
|
2014-07-12 22:12:38 +00:00
|
|
|
end;
|
2014-07-13 14:58:12 +00:00
|
|
|
|
2014-07-12 22:12:38 +00:00
|
|
|
AppendToStream(FSStyles, Format(
|
|
|
|
'</%s>', [ANodeName]));
|
|
|
|
end;
|
|
|
|
|
2015-02-02 18:51:13 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteVmlDrawings(AWorksheet: TsWorksheet);
|
2015-03-05 10:35:32 +00:00
|
|
|
// My xml viewer does not format vml files property --> format in code.
|
|
|
|
var
|
|
|
|
comment: PsComment;
|
|
|
|
index: Integer;
|
|
|
|
id: Integer;
|
2015-02-02 18:51:13 +00:00
|
|
|
begin
|
2015-02-24 16:57:36 +00:00
|
|
|
if AWorksheet.Comments.Count = 0 then
|
|
|
|
exit;
|
|
|
|
|
2015-02-02 18:51:13 +00:00
|
|
|
SetLength(FSVmlDrawings, FCurSheetNum + 1);
|
|
|
|
if (boBufStream in Workbook.Options) then
|
|
|
|
FSVmlDrawings[FCurSheetNum] := TBufStream.Create(GetTempFileName('', Format('fpsVMLD%d', [FCurSheetNum])))
|
|
|
|
else
|
|
|
|
FSVmlDrawings[FCurSheetNum] := TMemoryStream.Create;
|
|
|
|
|
|
|
|
// Header
|
|
|
|
AppendToStream(FSVmlDrawings[FCurSheetNum],
|
|
|
|
'<xml xmlns:v="urn:schemas-microsoft-com:vml" '+
|
|
|
|
'xmlns:o="urn:schemas-microsoft-com:office:office" '+
|
|
|
|
'xmlns:x="urn:schemas-microsoft-com:office:excel">' + LineEnding);
|
|
|
|
// My xml viewer does not format vml files property --> format in code.
|
|
|
|
AppendToStream(FSVmlDrawings[FCurSheetNum],
|
2015-02-07 22:04:03 +00:00
|
|
|
' <o:shapelayout v:ext="edit">' + LineEnding +
|
2015-02-06 20:11:39 +00:00
|
|
|
' <o:idmap v:ext="edit" data="1" />' + LineEnding +
|
|
|
|
// "data" is a comma-separated list with the ids of groups of 1024 comments -- really?
|
2015-02-02 18:51:13 +00:00
|
|
|
' </o:shapelayout>' + LineEnding);
|
|
|
|
AppendToStream(FSVmlDrawings[FCurSheetNum],
|
|
|
|
' <v:shapetype id="_x0000_t202" coordsize="21600,21600" o:spt="202" path="m,l,21600r21600,l21600,xe">'+LineEnding+
|
2015-02-06 20:11:39 +00:00
|
|
|
' <v:stroke joinstyle="miter"/>' + LineEnding +
|
|
|
|
' <v:path gradientshapeok="t" o:connecttype="rect"/>' + LineEnding +
|
2015-02-02 18:51:13 +00:00
|
|
|
' </v:shapetype>' + LineEnding);
|
|
|
|
|
|
|
|
// Write vmlDrawings for each comment (formatting and position of comment box)
|
2015-03-05 10:35:32 +00:00
|
|
|
index := 1;
|
|
|
|
for comment in AWorksheet.Comments do
|
|
|
|
begin
|
|
|
|
id := 1024 + index; // if more than 1024 comments then use data="1,2,etc" above! -- not implemented yet
|
|
|
|
AppendToStream(FSVmlDrawings[FCurSheetNum], LineEnding + Format(
|
|
|
|
' <v:shape id="_x0000_s%d" type="#_x0000_t202" ', [id]) + LineEnding + Format(
|
|
|
|
' style="position:absolute; width:108pt; height:52.5pt; z-index:%d; visibility:hidden" ', [index]) + LineEnding +
|
|
|
|
// it is not necessary to specify margin-left and margin-top here!
|
|
|
|
|
|
|
|
// 'style=''position:absolute; margin-left:71.25pt; margin-top:1.5pt; ' + Format(
|
|
|
|
// 'width:108pt; height:52.5pt; z-index:%d; visibility:hidden'' ', [FDrawingCounter+1]) +
|
|
|
|
// 'width:108pt; height:52.5pt; z-index:1; visibility:hidden'' ' +
|
|
|
|
|
|
|
|
' fillcolor="#ffffe1" o:insetmode="auto"> '+ LineEnding +
|
|
|
|
' <v:fill color2="#ffffe1" />'+LineEnding+
|
|
|
|
' <v:shadow on="t" color="black" obscured="t" />'+LineEnding+
|
|
|
|
' <v:path o:connecttype="none" />'+LineEnding+
|
|
|
|
' <v:textbox style="mso-direction-alt:auto">'+LineEnding+
|
|
|
|
' <div style="text-align:left"></div>'+LineEnding+
|
|
|
|
' </v:textbox>' + LineEnding +
|
|
|
|
' <x:ClientData ObjectType="Note">'+LineEnding+
|
|
|
|
' <x:MoveWithCells />'+LineEnding+
|
|
|
|
' <x:SizeWithCells />'+LineEnding+
|
|
|
|
' <x:Anchor> 1, 15, 0, 2, 2, 79, 4, 4</x:Anchor>'+LineEnding+
|
|
|
|
' <x:AutoFill>False</x:AutoFill>'+LineEnding + Format(
|
|
|
|
' <x:Row>%d</x:Row>', [comment^.Row]) + LineEnding + Format(
|
|
|
|
' <x:Column>%d</x:Column>', [comment^.Col]) + LineEnding +
|
|
|
|
' </x:ClientData>'+ LineEnding+
|
|
|
|
' </v:shape>' + LineEnding);
|
|
|
|
end;
|
|
|
|
|
2015-02-02 18:51:13 +00:00
|
|
|
// Footer
|
|
|
|
AppendToStream(FSVmlDrawings[FCurSheetNum],
|
|
|
|
'</xml>');
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TsSpreadOOXMLWriter.WriteWorksheetRels(AWorksheet: TsWorksheet);
|
2015-02-24 16:57:36 +00:00
|
|
|
var
|
|
|
|
AVLNode: TAVLTreeNode;
|
|
|
|
hyperlink: PsHyperlink;
|
|
|
|
s: String;
|
2015-03-08 00:50:10 +00:00
|
|
|
target, bookmark: String;
|
2015-02-02 18:51:13 +00:00
|
|
|
begin
|
2015-02-24 16:57:36 +00:00
|
|
|
// Extend stream array
|
|
|
|
SetLength(FSSheetRels, FCurSheetNum + 1);
|
|
|
|
|
|
|
|
// Anything to write?
|
|
|
|
if (AWorksheet.Comments.Count = 0) and (AWorksheet.Hyperlinks.Count = 0) then
|
|
|
|
exit;
|
2015-02-04 18:15:19 +00:00
|
|
|
|
2015-02-02 18:51:13 +00:00
|
|
|
// Create stream
|
|
|
|
if (boBufStream in Workbook.Options) then
|
|
|
|
FSSheetRels[FCurSheetNum] := TBufStream.Create(GetTempFileName('', Format('fpsWSR%d', [FCurSheetNum])))
|
|
|
|
else
|
|
|
|
FSSheetRels[FCurSheetNum] := TMemoryStream.Create;
|
|
|
|
|
|
|
|
// Header
|
|
|
|
AppendToStream(FSSheetRels[FCurSheetNum],
|
|
|
|
XML_HEADER);
|
|
|
|
AppendToStream(FSSheetRels[FCurSheetNum], Format(
|
|
|
|
'<Relationships xmlns="%s">', [SCHEMAS_RELS]));
|
2015-02-24 16:57:36 +00:00
|
|
|
|
|
|
|
FNext_rId := 1;
|
|
|
|
|
|
|
|
// Relationships for comments
|
|
|
|
if AWorksheet.Comments.Count > 0 then
|
|
|
|
begin
|
|
|
|
AppendToStream(FSSheetRels[FCurSheetNum], Format(
|
2015-02-02 18:51:13 +00:00
|
|
|
'<Relationship Id="rId1" Type="%s" Target="../drawings/vmlDrawing%d.vml" />',
|
|
|
|
[SCHEMAS_DRAWINGS, FCurSheetNum+1]));
|
2015-02-24 16:57:36 +00:00
|
|
|
AppendToStream(FSSheetRels[FCurSheetNum], Format(
|
|
|
|
'<Relationship Id="rId2" Type="%s" Target="../comments%d.xml" />',
|
|
|
|
[SCHEMAS_COMMENTS, FCurSheetNum+1]));
|
|
|
|
FNext_rId := 3;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Relationships for hyperlinks
|
|
|
|
if AWorksheet.Hyperlinks.Count > 0 then
|
|
|
|
begin
|
|
|
|
AVLNode := AWorksheet.Hyperlinks.FindLowest;
|
|
|
|
while Assigned(AVLNode) do
|
|
|
|
begin
|
|
|
|
hyperlink := PsHyperlink(AVLNode.Data);
|
2015-03-08 00:50:10 +00:00
|
|
|
SplitHyperlink(hyperlink^.Target, target, bookmark);
|
|
|
|
if target <> '' then
|
2015-02-24 16:57:36 +00:00
|
|
|
begin
|
2015-03-08 00:50:10 +00:00
|
|
|
if (pos('file:', target) = 0) and FileNameIsAbsolute(target) then
|
2015-03-08 17:27:52 +00:00
|
|
|
FileNameToURI(target);
|
|
|
|
// target := 'file:///' + target;
|
2015-02-28 23:46:08 +00:00
|
|
|
s := Format('Id="rId%d" Type="%s" Target="%s" TargetMode="External"',
|
2015-03-08 00:50:10 +00:00
|
|
|
[FNext_rId, SCHEMAS_HYPERLINKS, target]);
|
2015-02-24 16:57:36 +00:00
|
|
|
AppendToStream(FSSheetRels[FCurSheetNum],
|
|
|
|
'<Relationship ' + s + ' />');
|
|
|
|
inc(FNext_rId);
|
|
|
|
end;
|
|
|
|
AVLNode := AWorksheet.Hyperlinks.FindSuccessor(AVLNode);
|
|
|
|
end;
|
|
|
|
end;
|
2015-02-07 22:04:03 +00:00
|
|
|
|
2015-02-02 18:51:13 +00:00
|
|
|
// Footer
|
|
|
|
AppendToStream(FSSheetRels[FCurSheetNum],
|
|
|
|
'</Relationships>');
|
|
|
|
end;
|
|
|
|
|
2014-04-23 22:29:32 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteGlobalFiles;
|
2008-02-24 13:18:34 +00:00
|
|
|
begin
|
2014-07-10 15:55:40 +00:00
|
|
|
{ --- Content Types --- }
|
2015-02-03 18:13:13 +00:00
|
|
|
// Will be written at the end of WriteToStream when all Sheet.rels files are
|
|
|
|
// known
|
2014-07-10 15:55:40 +00:00
|
|
|
|
|
|
|
{ --- RelsRels --- }
|
|
|
|
AppendToStream(FSRelsRels,
|
|
|
|
XML_HEADER);
|
|
|
|
AppendToStream(FSRelsRels, Format(
|
|
|
|
'<Relationships xmlns="%s">', [SCHEMAS_RELS]));
|
|
|
|
AppendToStream(FSRelsRels, Format(
|
2014-08-04 19:11:17 +00:00
|
|
|
'<Relationship Type="%s" Target="xl/workbook.xml" Id="rId1" />', [SCHEMAS_DOCUMENT]));
|
2014-07-10 15:55:40 +00:00
|
|
|
AppendToStream(FSRelsRels,
|
|
|
|
'</Relationships>');
|
|
|
|
|
|
|
|
{ --- Styles --- }
|
|
|
|
AppendToStream(FSStyles,
|
|
|
|
XML_Header);
|
|
|
|
AppendToStream(FSStyles, Format(
|
|
|
|
'<styleSheet xmlns="%s">', [SCHEMAS_SPREADML]));
|
2014-07-11 22:43:00 +00:00
|
|
|
|
2014-07-14 20:47:53 +00:00
|
|
|
// Number formats
|
|
|
|
WriteNumFormatList(FSStyles);
|
|
|
|
|
2014-07-12 22:12:38 +00:00
|
|
|
// Fonts
|
|
|
|
WriteFontList(FSStyles);
|
2014-07-11 22:43:00 +00:00
|
|
|
|
2014-07-12 22:12:38 +00:00
|
|
|
// Fill patterns
|
2014-07-13 22:09:27 +00:00
|
|
|
WriteFillList(FSStyles);
|
2014-07-12 22:12:38 +00:00
|
|
|
|
|
|
|
// Borders
|
2014-07-13 22:09:27 +00:00
|
|
|
WriteBorderList(FSStyles);
|
2014-07-14 20:47:53 +00:00
|
|
|
|
2014-07-12 22:12:38 +00:00
|
|
|
// Style records
|
2014-07-10 15:55:40 +00:00
|
|
|
AppendToStream(FSStyles,
|
2014-07-18 22:48:38 +00:00
|
|
|
'<cellStyleXfs count="1">' +
|
|
|
|
'<xf numFmtId="0" fontId="0" fillId="0" borderId="0" />' +
|
2014-07-12 22:12:38 +00:00
|
|
|
'</cellStyleXfs>'
|
|
|
|
);
|
|
|
|
WriteStyleList(FSStyles, 'cellXfs');
|
|
|
|
|
|
|
|
// Cell style records
|
2014-07-10 15:55:40 +00:00
|
|
|
AppendToStream(FSStyles,
|
2014-07-18 22:48:38 +00:00
|
|
|
'<cellStyles count="1">' +
|
|
|
|
'<cellStyle name="Normal" xfId="0" builtinId="0" />' +
|
2014-07-10 15:55:40 +00:00
|
|
|
'</cellStyles>');
|
2014-07-12 22:12:38 +00:00
|
|
|
|
|
|
|
// Misc
|
2014-07-10 15:55:40 +00:00
|
|
|
AppendToStream(FSStyles,
|
|
|
|
'<dxfs count="0" />');
|
|
|
|
AppendToStream(FSStyles,
|
|
|
|
'<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleLight16" />');
|
2014-07-12 22:12:38 +00:00
|
|
|
|
2014-07-29 21:02:14 +00:00
|
|
|
// Palette
|
|
|
|
WritePalette(FSStyles);
|
|
|
|
|
2014-07-10 15:55:40 +00:00
|
|
|
AppendToStream(FSStyles,
|
|
|
|
'</styleSheet>');
|
2009-02-02 09:58:51 +00:00
|
|
|
end;
|
|
|
|
|
2014-04-23 22:29:32 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteContent;
|
2009-02-02 09:58:51 +00:00
|
|
|
var
|
2015-02-07 22:04:03 +00:00
|
|
|
i, counter: Integer;
|
2009-02-02 09:58:51 +00:00
|
|
|
begin
|
2015-05-01 15:14:25 +00:00
|
|
|
{ --- WorkbookRels --- }
|
2009-02-02 09:58:51 +00:00
|
|
|
{ Workbook relations - Mark relation to all sheets }
|
2015-02-07 22:04:03 +00:00
|
|
|
counter := 0;
|
2014-07-10 15:55:40 +00:00
|
|
|
AppendToStream(FSWorkbookRels,
|
|
|
|
XML_HEADER);
|
|
|
|
AppendToStream(FSWorkbookRels,
|
|
|
|
'<Relationships xmlns="' + SCHEMAS_RELS + '">');
|
2015-02-07 22:04:03 +00:00
|
|
|
while counter <= Workbook.GetWorksheetCount do begin
|
|
|
|
inc(counter);
|
2014-07-10 15:55:40 +00:00
|
|
|
AppendToStream(FSWorkbookRels, Format(
|
|
|
|
'<Relationship Type="%s" Target="worksheets/sheet%d.xml" Id="rId%d" />',
|
2015-02-07 22:04:03 +00:00
|
|
|
[SCHEMAS_WORKSHEET, counter, counter]));
|
|
|
|
end;
|
|
|
|
AppendToStream(FSWorkbookRels, Format(
|
|
|
|
'<Relationship Id="rId%d" Type="%s" Target="styles.xml" />',
|
|
|
|
[counter+1, SCHEMAS_STYLES]));
|
|
|
|
AppendToStream(FSWorkbookRels, Format(
|
|
|
|
'<Relationship Id="rId%d" Type="%s" Target="sharedStrings.xml" />',
|
|
|
|
[counter+2, SCHEMAS_STRINGS]));
|
2014-07-18 22:48:38 +00:00
|
|
|
AppendToStream(FSWorkbookRels,
|
2014-07-10 15:55:40 +00:00
|
|
|
'</Relationships>');
|
|
|
|
|
|
|
|
{ --- Workbook --- }
|
|
|
|
{ Global workbook data - Mark all sheets }
|
|
|
|
AppendToStream(FSWorkbook,
|
|
|
|
XML_HEADER);
|
|
|
|
AppendToStream(FSWorkbook, Format(
|
|
|
|
'<workbook xmlns="%s" xmlns:r="%s">', [SCHEMAS_SPREADML, SCHEMAS_DOC_RELS]));
|
|
|
|
AppendToStream(FSWorkbook,
|
|
|
|
'<fileVersion appName="fpspreadsheet" />');
|
|
|
|
AppendToStream(FSWorkbook,
|
|
|
|
'<workbookPr defaultThemeVersion="124226" />');
|
|
|
|
AppendToStream(FSWorkbook,
|
2014-08-04 19:11:17 +00:00
|
|
|
'<bookViews>' +
|
|
|
|
'<workbookView xWindow="480" yWindow="90" windowWidth="15195" windowHeight="12525" />' +
|
2014-07-10 15:55:40 +00:00
|
|
|
'</bookViews>');
|
|
|
|
AppendToStream(FSWorkbook,
|
|
|
|
'<sheets>');
|
2015-02-07 22:04:03 +00:00
|
|
|
for counter:=1 to Workbook.GetWorksheetCount do
|
2014-07-10 15:55:40 +00:00
|
|
|
AppendToStream(FSWorkbook, Format(
|
2015-02-07 22:04:03 +00:00
|
|
|
'<sheet name="%s" sheetId="%d" r:id="rId%d" />',
|
|
|
|
[Workbook.GetWorksheetByIndex(counter-1).Name, counter, counter]));
|
2014-07-10 15:55:40 +00:00
|
|
|
AppendToStream(FSWorkbook,
|
|
|
|
'</sheets>');
|
|
|
|
AppendToStream(FSWorkbook,
|
|
|
|
'<calcPr calcId="114210" />');
|
|
|
|
AppendToStream(FSWorkbook,
|
|
|
|
'</workbook>');
|
|
|
|
|
|
|
|
// Preparation for shared strings
|
2009-02-02 09:58:51 +00:00
|
|
|
FSharedStringsCount := 0;
|
|
|
|
|
2015-02-02 18:51:13 +00:00
|
|
|
// Write all worksheets which fills also the shared strings.
|
|
|
|
// Also: write comments and related files
|
2015-02-24 16:57:36 +00:00
|
|
|
FNext_rId := 1;
|
2014-04-23 22:29:32 +00:00
|
|
|
for i := 0 to Workbook.GetWorksheetCount - 1 do
|
2015-02-02 18:51:13 +00:00
|
|
|
begin
|
|
|
|
FWorksheet := Workbook.GetWorksheetByIndex(i);
|
|
|
|
WriteWorksheet(FWorksheet);
|
2015-02-24 16:57:36 +00:00
|
|
|
WriteComments(FWorksheet);
|
|
|
|
WriteVmlDrawings(FWorksheet);
|
|
|
|
WriteWorksheetRels(FWorksheet);
|
2015-02-02 18:51:13 +00:00
|
|
|
end;
|
2009-02-02 09:58:51 +00:00
|
|
|
|
|
|
|
// Finalization of the shared strings document
|
2014-07-10 15:55:40 +00:00
|
|
|
AppendToStream(FSSharedStrings_complete,
|
|
|
|
XML_HEADER, Format(
|
|
|
|
'<sst xmlns="%s" count="%d" uniqueCount="%d">', [SCHEMAS_SPREADML, FSharedStringsCount, FSharedStringsCount]
|
|
|
|
));
|
2014-07-18 22:48:38 +00:00
|
|
|
ResetStream(FSSharedStrings);
|
2014-07-10 15:55:40 +00:00
|
|
|
FSSharedStrings_complete.CopyFrom(FSSharedStrings, FSSharedStrings.Size);
|
|
|
|
AppendToStream(FSSharedStrings_complete,
|
|
|
|
'</sst>');
|
2009-02-02 09:58:51 +00:00
|
|
|
end;
|
|
|
|
|
2015-02-02 18:51:13 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteContentTypes;
|
|
|
|
var
|
|
|
|
i: Integer;
|
2009-02-02 09:58:51 +00:00
|
|
|
begin
|
2015-02-02 18:51:13 +00:00
|
|
|
AppendToStream(FSContentTypes,
|
|
|
|
XML_HEADER);
|
|
|
|
AppendToStream(FSContentTypes,
|
|
|
|
'<Types xmlns="' + SCHEMAS_TYPES + '">');
|
2015-04-18 14:58:38 +00:00
|
|
|
|
2015-02-02 18:51:13 +00:00
|
|
|
AppendToStream(FSContentTypes, Format(
|
|
|
|
'<Default Extension="rels" ContentType="%s" />', [MIME_RELS]));
|
|
|
|
AppendToStream(FSContentTypes, Format(
|
|
|
|
'<Default Extension="xml" ContentType="%s" />', [MIME_XML]));
|
|
|
|
AppendToStream(FSContentTypes, Format(
|
|
|
|
'<Default Extension="vml" ContentType="%s" />', [MIME_VMLDRAWING]));
|
2014-08-18 13:48:46 +00:00
|
|
|
|
2015-02-02 18:51:13 +00:00
|
|
|
AppendToStream(FSContentTypes,
|
|
|
|
'<Override PartName="/xl/workbook.xml" ContentType="' + MIME_SHEET + '" />');
|
|
|
|
|
|
|
|
for i:=1 to Workbook.GetWorksheetCount do
|
|
|
|
AppendToStream(FSContentTypes, Format(
|
|
|
|
'<Override PartName="/xl/worksheets/sheet%d.xml" ContentType="%s" />',
|
|
|
|
[i, MIME_WORKSHEET]));
|
|
|
|
|
|
|
|
for i:=1 to Length(FSComments) do
|
|
|
|
AppendToStream(FSContentTypes, Format(
|
|
|
|
'<Override PartName="/xl/comments%d.xml" ContentType="%s" />',
|
|
|
|
[i, MIME_COMMENTS]));
|
|
|
|
|
|
|
|
AppendToStream(FSContentTypes,
|
|
|
|
'<Override PartName="/xl/styles.xml" ContentType="' + MIME_STYLES + '" />');
|
|
|
|
AppendToStream(FSContentTypes,
|
|
|
|
'<Override PartName="/xl/sharedStrings.xml" ContentType="' + MIME_STRINGS + '" />');
|
|
|
|
AppendToStream(FSContentTypes,
|
|
|
|
'</Types>');
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TsSpreadOOXMLWriter.WriteWorksheet(AWorksheet: TsWorksheet);
|
|
|
|
begin
|
2014-07-10 15:55:40 +00:00
|
|
|
FCurSheetNum := Length(FSSheets);
|
|
|
|
SetLength(FSSheets, FCurSheetNum + 1);
|
2011-08-29 10:55:22 +00:00
|
|
|
|
2014-07-10 20:43:46 +00:00
|
|
|
// Create the stream
|
2014-07-22 15:59:29 +00:00
|
|
|
if (boBufStream in Workbook.Options) then
|
2014-07-18 22:48:38 +00:00
|
|
|
FSSheets[FCurSheetNum] := TBufStream.Create(GetTempFileName('', Format('fpsSH%d', [FCurSheetNum])))
|
|
|
|
else
|
2014-07-10 20:43:46 +00:00
|
|
|
FSSheets[FCurSheetNum] := TMemoryStream.Create;
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2011-08-29 10:55:22 +00:00
|
|
|
// Header
|
2014-07-10 15:55:40 +00:00
|
|
|
AppendToStream(FSSheets[FCurSheetNum],
|
|
|
|
XML_HEADER);
|
|
|
|
AppendToStream(FSSheets[FCurSheetNum], Format(
|
|
|
|
'<worksheet xmlns="%s" xmlns:r="%s">', [SCHEMAS_SPREADML, SCHEMAS_DOC_RELS]));
|
2011-08-29 10:55:22 +00:00
|
|
|
|
2015-04-30 21:55:55 +00:00
|
|
|
WriteSheetPr(FSSheets[FCurSheetNum], AWorksheet);
|
2015-02-02 18:51:13 +00:00
|
|
|
WriteDimension(FSSheets[FCurSheetNum], AWorksheet);
|
2014-08-04 19:11:17 +00:00
|
|
|
WriteSheetViews(FSSheets[FCurSheetNum], AWorksheet);
|
|
|
|
WriteCols(FSSheets[FCurSheetNum], AWorksheet);
|
|
|
|
WriteSheetData(FSSheets[FCurSheetNum], AWorksheet);
|
2015-02-24 16:57:36 +00:00
|
|
|
WriteHyperlinks(FSSheets[FCurSheetNum], AWorksheet);
|
2014-09-10 16:48:34 +00:00
|
|
|
WriteMergedCells(FSSheets[FCurSheetNum], AWorksheet);
|
2015-04-30 21:55:55 +00:00
|
|
|
WritePrintOptions(FSSheets[FCurSheetNum], AWorksheet);
|
2015-04-29 20:00:07 +00:00
|
|
|
WritePageMargins(FSSheets[FCurSheetNum], AWorksheet);
|
2015-04-30 21:55:55 +00:00
|
|
|
WritePageSetup(FSSheets[FCurSheetNum], AWorksheet);
|
2015-05-01 15:14:25 +00:00
|
|
|
WriteHeaderFooter(FSSheets[FCurSheetNum], AWorksheet);
|
2011-08-29 10:55:22 +00:00
|
|
|
|
|
|
|
// Footer
|
2015-02-15 11:45:08 +00:00
|
|
|
if AWorksheet.Comments.Count > 0 then
|
2015-02-02 18:51:13 +00:00
|
|
|
AppendToStream(FSSheets[FCurSheetNum],
|
|
|
|
'<legacyDrawing r:id="rId1" />');
|
2014-07-10 15:55:40 +00:00
|
|
|
AppendToStream(FSSheets[FCurSheetNum],
|
|
|
|
'</worksheet>');
|
2009-01-28 22:36:41 +00:00
|
|
|
end;
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Adds the built-in number formats to the NumFormatList.
|
|
|
|
-------------------------------------------------------------------------------}
|
|
|
|
procedure TsSpreadOOXMLWriter.AddBuiltinNumFormats;
|
2009-02-02 09:58:51 +00:00
|
|
|
begin
|
2015-04-18 14:58:38 +00:00
|
|
|
FFirstNumFormatIndexInFile := 164;
|
|
|
|
AddBuiltInBiffFormats(
|
2015-04-19 22:03:33 +00:00
|
|
|
FNumFormatList, Workbook.FormatSettings, FFirstNumFormatIndexInFile-1
|
2015-04-18 14:58:38 +00:00
|
|
|
);
|
2014-07-10 15:55:40 +00:00
|
|
|
end;
|
2009-02-02 09:58:51 +00:00
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Creates the streams for the individual data files. Will be zipped into a
|
|
|
|
single xlsx file.
|
|
|
|
-------------------------------------------------------------------------------}
|
2014-07-10 15:55:40 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.CreateStreams;
|
|
|
|
begin
|
2014-07-22 15:59:29 +00:00
|
|
|
if (boBufStream in Workbook.Options) then begin
|
2014-07-18 22:48:38 +00:00
|
|
|
FSContentTypes := TBufStream.Create(GetTempFileName('', 'fpsCT'));
|
|
|
|
FSRelsRels := TBufStream.Create(GetTempFileName('', 'fpsRR'));
|
|
|
|
FSWorkbookRels := TBufStream.Create(GetTempFileName('', 'fpsWBR'));
|
|
|
|
FSWorkbook := TBufStream.Create(GetTempFileName('', 'fpsWB'));
|
|
|
|
FSStyles := TBufStream.Create(GetTempFileName('', 'fpsSTY'));
|
|
|
|
FSSharedStrings := TBufStream.Create(GetTempFileName('', 'fpsSS'));
|
|
|
|
FSSharedStrings_complete := TBufStream.Create(GetTempFileName('', 'fpsSSC'));
|
2014-07-10 20:43:46 +00:00
|
|
|
end else begin;
|
|
|
|
FSContentTypes := TMemoryStream.Create;
|
|
|
|
FSRelsRels := TMemoryStream.Create;
|
|
|
|
FSWorkbookRels := TMemoryStream.Create;
|
|
|
|
FSWorkbook := TMemoryStream.Create;
|
|
|
|
FSStyles := TMemoryStream.Create;
|
|
|
|
FSSharedStrings := TMemoryStream.Create;
|
|
|
|
FSSharedStrings_complete := TMemoryStream.Create;
|
|
|
|
end;
|
2014-07-10 15:55:40 +00:00
|
|
|
// FSSheets will be created when needed.
|
2009-02-02 09:58:51 +00:00
|
|
|
end;
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Destroys the streams that were created by the writer
|
|
|
|
-------------------------------------------------------------------------------}
|
2014-07-10 15:55:40 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.DestroyStreams;
|
2014-07-10 20:43:46 +00:00
|
|
|
|
|
|
|
procedure DestroyStream(AStream: TStream);
|
|
|
|
var
|
|
|
|
fn: String;
|
|
|
|
begin
|
|
|
|
if AStream is TFileStream then begin
|
|
|
|
fn := TFileStream(AStream).Filename;
|
|
|
|
DeleteFile(fn);
|
|
|
|
end;
|
|
|
|
AStream.Free;
|
|
|
|
end;
|
|
|
|
|
2014-07-11 13:20:14 +00:00
|
|
|
var
|
|
|
|
stream: TStream;
|
2014-05-15 12:53:56 +00:00
|
|
|
begin
|
2014-07-10 20:43:46 +00:00
|
|
|
DestroyStream(FSContentTypes);
|
|
|
|
DestroyStream(FSRelsRels);
|
|
|
|
DestroyStream(FSWorkbookRels);
|
|
|
|
DestroyStream(FSWorkbook);
|
|
|
|
DestroyStream(FSStyles);
|
|
|
|
DestroyStream(FSSharedStrings);
|
|
|
|
DestroyStream(FSSharedStrings_complete);
|
2014-07-11 13:20:14 +00:00
|
|
|
for stream in FSSheets do DestroyStream(stream);
|
2014-07-10 15:55:40 +00:00
|
|
|
SetLength(FSSheets, 0);
|
2015-02-02 18:51:13 +00:00
|
|
|
for stream in FSComments do DestroyStream(stream);
|
|
|
|
SetLength(FSComments, 0);
|
|
|
|
for stream in FSSheetRels do DestroyStream(stream);
|
|
|
|
SetLength(FSSheetRels, 0);
|
|
|
|
for stream in FSVmlDrawings do DestroyStream(stream);
|
|
|
|
SetLength(FSVmlDrawings, 0);
|
2014-05-15 12:53:56 +00:00
|
|
|
end;
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Prepares a string formula for writing: Deletes the leading = sign and makes
|
|
|
|
sure that it is a valid xml string.
|
|
|
|
-------------------------------------------------------------------------------}
|
2014-08-30 18:03:22 +00:00
|
|
|
function TsSpreadOOXMLWriter.PrepareFormula(const AFormula: String): String;
|
2014-08-08 19:00:27 +00:00
|
|
|
begin
|
2014-08-30 18:03:22 +00:00
|
|
|
Result := AFormula;
|
2014-08-08 19:00:27 +00:00
|
|
|
if (Result <> '') and (Result[1] = '=') then Delete(Result, 1, 1);
|
2014-08-30 18:03:22 +00:00
|
|
|
Result := UTF8TextToXMLText(Result)
|
2014-08-08 19:00:27 +00:00
|
|
|
end;
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Is called before zipping the individual file parts. Rewinds the streams.
|
|
|
|
-------------------------------------------------------------------------------}
|
2014-07-10 20:43:46 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.ResetStreams;
|
|
|
|
var
|
2015-02-03 12:56:58 +00:00
|
|
|
i: Integer;
|
2014-07-10 20:43:46 +00:00
|
|
|
begin
|
2014-07-18 22:48:38 +00:00
|
|
|
ResetStream(FSContentTypes);
|
|
|
|
ResetStream(FSRelsRels);
|
|
|
|
ResetStream(FSWorkbookRels);
|
|
|
|
ResetStream(FSWorkbook);
|
|
|
|
ResetStream(FSStyles);
|
|
|
|
ResetStream(FSSharedStrings_complete);
|
2015-02-03 12:56:58 +00:00
|
|
|
for i:=0 to High(FSSheets) do ResetStream(FSSheets[i]);
|
|
|
|
for i:=0 to High(FSSheetRels) do ResetStream(FSSheetRels[i]);
|
|
|
|
for i:=0 to High(FSComments) do ResetStream(FSComments[i]);
|
|
|
|
for i:=0 to High(FSVmlDrawings) do ResetStream(FSVmlDrawings[i]);
|
2014-07-10 20:43:46 +00:00
|
|
|
end;
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
2009-02-02 09:58:51 +00:00
|
|
|
Writes a string to a file. Helper convenience method.
|
2015-04-18 14:58:38 +00:00
|
|
|
-------------------------------------------------------------------------------}
|
2009-01-28 22:36:41 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteStringToFile(AFileName, AString: string);
|
|
|
|
var
|
2015-04-18 14:58:38 +00:00
|
|
|
stream : TFileStream;
|
2009-01-28 22:36:41 +00:00
|
|
|
S : String;
|
|
|
|
begin
|
2015-04-18 14:58:38 +00:00
|
|
|
stream := TFileStream.Create(AFileName, fmCreate);
|
|
|
|
try
|
|
|
|
S := AString;
|
|
|
|
stream.WriteBuffer(Pointer(S)^, Length(S));
|
|
|
|
finally
|
|
|
|
stream.Free;
|
|
|
|
end;
|
2009-01-28 22:36:41 +00:00
|
|
|
end;
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Writes an OOXML document to the file
|
|
|
|
-------------------------------------------------------------------------------}
|
2009-11-08 19:21:23 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteToFile(const AFileName: string;
|
2014-04-23 22:29:32 +00:00
|
|
|
const AOverwriteExisting: Boolean);
|
2009-01-28 22:36:41 +00:00
|
|
|
var
|
2014-07-19 13:23:12 +00:00
|
|
|
lStream: TStream;
|
2014-06-20 15:58:22 +00:00
|
|
|
lMode: word;
|
2009-01-28 22:36:41 +00:00
|
|
|
begin
|
2014-06-20 15:58:22 +00:00
|
|
|
if AOverwriteExisting
|
|
|
|
then lMode := fmCreate or fmOpenWrite
|
|
|
|
else lMode := fmCreate;
|
|
|
|
|
2014-07-22 15:59:29 +00:00
|
|
|
if (boBufStream in Workbook.Options) then
|
2014-07-19 13:23:12 +00:00
|
|
|
lStream := TBufStream.Create(AFileName, lMode)
|
|
|
|
else
|
|
|
|
lStream := TFileStream.Create(AFileName, lMode);
|
2009-02-02 09:58:51 +00:00
|
|
|
try
|
2014-04-23 22:29:32 +00:00
|
|
|
WriteToStream(lStream);
|
2009-02-02 09:58:51 +00:00
|
|
|
finally
|
2011-08-29 15:10:10 +00:00
|
|
|
FreeAndNil(lStream);
|
2009-02-02 09:58:51 +00:00
|
|
|
end;
|
2009-01-28 22:36:41 +00:00
|
|
|
end;
|
|
|
|
|
2014-04-23 22:29:32 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteToStream(AStream: TStream);
|
2011-08-29 11:59:47 +00:00
|
|
|
var
|
|
|
|
FZip: TZipper;
|
|
|
|
i: Integer;
|
2009-01-28 22:36:41 +00:00
|
|
|
begin
|
2014-07-12 22:12:38 +00:00
|
|
|
{ Analyze the workbook and collect all information needed }
|
2015-04-19 22:03:33 +00:00
|
|
|
ListAllNumFormats;
|
2014-07-13 22:09:27 +00:00
|
|
|
ListAllFills;
|
|
|
|
ListAllBorders;
|
2014-07-12 22:12:38 +00:00
|
|
|
|
2014-07-10 15:55:40 +00:00
|
|
|
{ Create the streams that will hold the file contents }
|
|
|
|
CreateStreams;
|
2011-08-29 11:59:47 +00:00
|
|
|
|
2014-07-10 15:55:40 +00:00
|
|
|
{ Fill the streams with the contents of the files }
|
2014-04-23 22:29:32 +00:00
|
|
|
WriteGlobalFiles;
|
|
|
|
WriteContent;
|
2015-02-02 18:51:13 +00:00
|
|
|
WriteContentTypes;
|
2011-08-29 11:59:47 +00:00
|
|
|
|
2014-07-19 13:23:12 +00:00
|
|
|
// Stream positions must be at beginning, they were moved to end during adding of xml strings.
|
2014-07-18 22:48:38 +00:00
|
|
|
ResetStreams;
|
|
|
|
|
2011-08-29 11:59:47 +00:00
|
|
|
{ Now compress the files }
|
|
|
|
FZip := TZipper.Create;
|
|
|
|
try
|
2014-07-18 22:48:38 +00:00
|
|
|
FZip.FileName := '__temp__.tmp';
|
2011-08-29 11:59:47 +00:00
|
|
|
FZip.Entries.AddFileEntry(FSContentTypes, OOXML_PATH_TYPES);
|
|
|
|
FZip.Entries.AddFileEntry(FSRelsRels, OOXML_PATH_RELS_RELS);
|
|
|
|
FZip.Entries.AddFileEntry(FSWorkbookRels, OOXML_PATH_XL_RELS_RELS);
|
|
|
|
FZip.Entries.AddFileEntry(FSWorkbook, OOXML_PATH_XL_WORKBOOK);
|
|
|
|
FZip.Entries.AddFileEntry(FSStyles, OOXML_PATH_XL_STYLES);
|
2014-07-10 15:55:40 +00:00
|
|
|
FZip.Entries.AddFileEntry(FSSharedStrings_complete, OOXML_PATH_XL_STRINGS);
|
2011-08-29 11:59:47 +00:00
|
|
|
|
2015-02-02 18:51:13 +00:00
|
|
|
for i:=0 to High(FSSheets) do begin
|
2014-07-10 15:55:40 +00:00
|
|
|
FSSheets[i].Position:= 0;
|
2015-02-02 18:51:13 +00:00
|
|
|
FZip.Entries.AddFileEntry(FSSheets[i], OOXML_PATH_XL_WORKSHEETS + Format('sheet%d.xml', [i+1]));
|
|
|
|
end;
|
|
|
|
|
|
|
|
for i:=0 to High(FSComments) do begin
|
2015-02-24 16:57:36 +00:00
|
|
|
if (FSComments[i] = nil) or (FSComments[i].Size = 0) then continue;
|
2015-02-02 18:51:13 +00:00
|
|
|
FSComments[i].Position := 0;
|
|
|
|
FZip.Entries.AddFileEntry(FSComments[i], OOXML_PATH_XL + Format('comments%d.xml', [i+1]));
|
|
|
|
end;
|
|
|
|
|
|
|
|
for i:=0 to High(FSSheetRels) do begin
|
2015-02-24 16:57:36 +00:00
|
|
|
if (FSSheetRels[i] = nil) or (FSSheetRels[i].Size = 0) then continue;
|
2015-02-02 18:51:13 +00:00
|
|
|
FSSheetRels[i].Position := 0;
|
|
|
|
FZip.Entries.AddFileEntry(FSSheetRels[i], OOXML_PATH_XL_WORKSHEETS_RELS + Format('sheet%d.xml.rels', [i+1]));
|
|
|
|
end;
|
|
|
|
|
|
|
|
for i:=0 to High(FSVmlDrawings) do begin
|
2015-02-24 16:57:36 +00:00
|
|
|
if (FSVmlDrawings[i] = nil) or (FSVmlDrawings[i].Size = 0) then continue;
|
2015-02-02 18:51:13 +00:00
|
|
|
FSVmlDrawings[i].Position := 0;
|
|
|
|
FZip.Entries.AddFileEntry(FSVmlDrawings[i], OOXML_PATH_XL_DRAWINGS + Format('vmlDrawing%d.vml', [i+1]));
|
2014-07-10 15:55:40 +00:00
|
|
|
end;
|
2011-08-29 11:59:47 +00:00
|
|
|
|
2014-07-10 15:55:40 +00:00
|
|
|
FZip.SaveToStream(AStream);
|
2011-08-29 11:59:47 +00:00
|
|
|
|
2014-07-10 15:55:40 +00:00
|
|
|
finally
|
|
|
|
DestroyStreams;
|
2011-08-29 11:59:47 +00:00
|
|
|
FZip.Free;
|
|
|
|
end;
|
2008-02-24 13:18:34 +00:00
|
|
|
end;
|
|
|
|
|
2014-07-11 20:00:49 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteBlank(AStream: TStream;
|
|
|
|
const ARow, ACol: Cardinal; ACell: PCell);
|
|
|
|
var
|
|
|
|
cellPosText: String;
|
|
|
|
lStyleIndex: Integer;
|
|
|
|
begin
|
|
|
|
cellPosText := TsWorksheet.CellPosToText(ARow, ACol);
|
|
|
|
lStyleIndex := GetStyleIndex(ACell);
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<c r="%s" s="%d">', [CellPosText, lStyleIndex]),
|
|
|
|
'<v></v>',
|
|
|
|
'</c>');
|
|
|
|
end;
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Writes a boolean value to the stream
|
|
|
|
-------------------------------------------------------------------------------}
|
2014-10-14 15:56:08 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteBool(AStream: TStream;
|
|
|
|
const ARow, ACol: Cardinal; const AValue: Boolean; ACell: PCell);
|
|
|
|
var
|
|
|
|
CellPosText: String;
|
|
|
|
CellValueText: String;
|
|
|
|
lStyleIndex: Integer;
|
|
|
|
begin
|
|
|
|
CellPosText := TsWorksheet.CellPosToText(ARow, ACol);
|
|
|
|
lStyleIndex := GetStyleIndex(ACell);
|
|
|
|
if AValue then CellValueText := '1' else CellValueText := '0';
|
|
|
|
AppendToStream(AStream, Format(
|
|
|
|
'<c r="%s" s="%d" t="b"><v>%s</v></c>', [CellPosText, lStyleIndex, CellValueText]));
|
|
|
|
end;
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Writes an error value to the specified cell.
|
|
|
|
-------------------------------------------------------------------------------}
|
2014-10-14 21:44:00 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteError(AStream: TStream;
|
|
|
|
const ARow, ACol: Cardinal; const AValue: TsErrorValue; ACell: PCell);
|
|
|
|
begin
|
2014-10-20 09:22:06 +00:00
|
|
|
Unused(AStream);
|
|
|
|
Unused(ARow, ACol);
|
|
|
|
Unused(AValue, ACell);
|
2014-10-14 21:44:00 +00:00
|
|
|
end;
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Writes a string formula to the given cell.
|
|
|
|
-------------------------------------------------------------------------------}
|
2014-08-08 19:00:27 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteFormula(AStream: TStream;
|
2014-08-17 22:25:46 +00:00
|
|
|
const ARow, ACol: Cardinal; ACell: PCell);
|
2014-08-08 19:00:27 +00:00
|
|
|
var
|
|
|
|
cellPosText: String;
|
|
|
|
lStyleIndex: Integer;
|
2014-08-30 18:03:22 +00:00
|
|
|
t, v: String;
|
2014-08-08 19:00:27 +00:00
|
|
|
begin
|
|
|
|
cellPosText := TsWorksheet.CellPosToText(ARow, ACol);
|
|
|
|
lStyleIndex := GetStyleIndex(ACell);
|
|
|
|
|
2014-09-04 09:19:45 +00:00
|
|
|
case ACell^.ContentType of
|
|
|
|
cctFormula:
|
|
|
|
begin
|
|
|
|
t := '';
|
|
|
|
v := '';
|
|
|
|
end;
|
|
|
|
cctUTF8String:
|
|
|
|
begin
|
|
|
|
t := ' t="str"';
|
|
|
|
v := Format('<v>%s</v>', [UTF8TextToXMLText(ACell^.UTF8StringValue)]);
|
|
|
|
end;
|
|
|
|
cctNumber:
|
|
|
|
begin
|
|
|
|
t := '';
|
|
|
|
v := Format('<v>%g</v>', [ACell^.NumberValue], FPointSeparatorSettings);
|
|
|
|
end;
|
|
|
|
cctDateTime:
|
|
|
|
begin
|
|
|
|
t := '';
|
|
|
|
v := Format('<v>%g</v>', [ACell^.DateTimeValue], FPointSeparatorSettings);
|
|
|
|
end;
|
|
|
|
cctBool:
|
|
|
|
begin
|
|
|
|
t := ' t="b"';
|
|
|
|
if ACell^.BoolValue then
|
|
|
|
v := '<v>1</v>'
|
|
|
|
else
|
|
|
|
v := '<v>0</v>';
|
|
|
|
end;
|
|
|
|
cctError:
|
|
|
|
begin
|
|
|
|
t := ' t="e"';
|
|
|
|
v := Format('<v>%s</v>', [GetErrorValueStr(ACell^.ErrorValue)]);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2015-03-06 12:12:45 +00:00
|
|
|
AppendToStream(AStream, Format(
|
2014-08-30 18:03:22 +00:00
|
|
|
'<c r="%s" s="%d"%s>' +
|
2014-08-18 13:48:46 +00:00
|
|
|
'<f>%s</f>' +
|
2014-08-30 18:03:22 +00:00
|
|
|
'%s' +
|
2014-08-18 13:48:46 +00:00
|
|
|
'</c>', [
|
2014-08-30 18:03:22 +00:00
|
|
|
CellPosText, lStyleIndex, t,
|
|
|
|
PrepareFormula(ACell^.FormulaValue),
|
|
|
|
v
|
2015-03-06 12:12:45 +00:00
|
|
|
]));
|
2014-08-08 19:00:27 +00:00
|
|
|
end;
|
2014-07-11 20:00:49 +00:00
|
|
|
|
2015-02-24 16:57:36 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Writes a string to the stream
|
|
|
|
|
|
|
|
If the string length exceeds 32767 bytes, the string will be truncated and a
|
|
|
|
warning will be written to the workbook's log.
|
|
|
|
-------------------------------------------------------------------------------}
|
2008-02-24 13:18:34 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteLabel(AStream: TStream; const ARow,
|
2013-12-07 13:42:22 +00:00
|
|
|
ACol: Cardinal; const AValue: string; ACell: PCell);
|
|
|
|
const
|
2014-07-15 21:00:49 +00:00
|
|
|
MAXBYTES = 32767; //limit for this format
|
2011-08-29 10:55:22 +00:00
|
|
|
var
|
|
|
|
CellPosText: string;
|
2011-09-01 07:55:12 +00:00
|
|
|
lStyleIndex: Cardinal;
|
2013-12-07 13:42:22 +00:00
|
|
|
ResultingValue: string;
|
2008-02-24 13:18:34 +00:00
|
|
|
begin
|
2013-12-07 13:42:22 +00:00
|
|
|
// Office 2007-2010 (at least) support no more characters in a cell;
|
2014-08-10 17:59:30 +00:00
|
|
|
if Length(AValue) > MAXBYTES then
|
|
|
|
begin
|
2014-07-15 21:00:49 +00:00
|
|
|
ResultingValue := Copy(AValue, 1, MAXBYTES); //may chop off multicodepoint UTF8 characters but well...
|
2014-10-08 09:23:34 +00:00
|
|
|
Workbook.AddErrorMsg(rsTruncateTooLongCellText, [
|
2014-08-10 17:59:30 +00:00
|
|
|
MAXBYTES, GetCellString(ARow, ACol)
|
|
|
|
]);
|
2013-12-07 13:42:22 +00:00
|
|
|
end
|
|
|
|
else
|
2014-10-08 09:23:34 +00:00
|
|
|
ResultingValue := AValue;
|
2013-12-07 13:42:22 +00:00
|
|
|
|
2014-09-29 22:27:03 +00:00
|
|
|
if not ValidXMLText(ResultingValue) then
|
|
|
|
Workbook.AddErrorMsg(
|
2014-10-14 15:56:08 +00:00
|
|
|
rsInvalidCharacterInCell, [
|
2014-09-29 22:27:03 +00:00
|
|
|
GetCellString(ARow, ACol)
|
|
|
|
]);
|
|
|
|
|
2014-07-10 15:55:40 +00:00
|
|
|
AppendToStream(FSSharedStrings,
|
2014-07-15 21:00:49 +00:00
|
|
|
'<si>' +
|
2014-09-29 22:27:03 +00:00
|
|
|
'<t>' + ResultingValue + '</t>' +
|
2014-07-15 21:00:49 +00:00
|
|
|
'</si>');
|
2011-08-29 13:24:16 +00:00
|
|
|
|
2011-08-29 10:55:22 +00:00
|
|
|
CellPosText := TsWorksheet.CellPosToText(ARow, ACol);
|
2011-09-01 07:55:12 +00:00
|
|
|
lStyleIndex := GetStyleIndex(ACell);
|
2014-07-11 20:00:49 +00:00
|
|
|
AppendToStream(AStream, Format(
|
2015-07-09 11:10:15 +00:00
|
|
|
'<c r="%s" s="%d" t="s">'+
|
|
|
|
'<v>%d</v>'+
|
|
|
|
'</c>',
|
|
|
|
[CellPosText, lStyleIndex, FSharedStringsCount]
|
|
|
|
));
|
2014-07-12 22:12:38 +00:00
|
|
|
inc(FSharedStringsCount);
|
2008-02-24 13:18:34 +00:00
|
|
|
end;
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Writes a number (64-bit IEE 754 floating point) to the stream
|
|
|
|
-------------------------------------------------------------------------------}
|
2008-02-24 13:18:34 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteNumber(AStream: TStream; const ARow,
|
2010-12-08 10:24:15 +00:00
|
|
|
ACol: Cardinal; const AValue: double; ACell: PCell);
|
2011-08-29 10:55:22 +00:00
|
|
|
var
|
|
|
|
CellPosText: String;
|
2012-04-27 08:01:15 +00:00
|
|
|
CellValueText: String;
|
2014-07-14 20:47:53 +00:00
|
|
|
lStyleIndex: Integer;
|
2008-02-24 13:18:34 +00:00
|
|
|
begin
|
2011-08-29 10:55:22 +00:00
|
|
|
CellPosText := TsWorksheet.CellPosToText(ARow, ACol);
|
2014-07-14 20:47:53 +00:00
|
|
|
lStyleIndex := GetStyleIndex(ACell);
|
2014-07-27 10:17:14 +00:00
|
|
|
CellValueText := FloatToStr(AValue, FPointSeparatorSettings);
|
2014-07-11 20:00:49 +00:00
|
|
|
AppendToStream(AStream, Format(
|
2014-07-14 20:47:53 +00:00
|
|
|
'<c r="%s" s="%d" t="n"><v>%s</v></c>', [CellPosText, lStyleIndex, CellValueText]));
|
2008-02-24 13:18:34 +00:00
|
|
|
end;
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Writes a date/time value as a number
|
|
|
|
|
|
|
|
Respects DateMode of the file
|
|
|
|
-------------------------------------------------------------------------------}
|
2013-12-22 14:02:04 +00:00
|
|
|
procedure TsSpreadOOXMLWriter.WriteDateTime(AStream: TStream;
|
|
|
|
const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell);
|
2014-07-15 15:16:46 +00:00
|
|
|
var
|
|
|
|
ExcelDateSerial: double;
|
2013-12-22 14:02:04 +00:00
|
|
|
begin
|
2014-07-27 10:17:14 +00:00
|
|
|
ExcelDateSerial := ConvertDateTimeToExcelDateTime(AValue, FDateMode);
|
2014-07-15 15:16:46 +00:00
|
|
|
WriteNumber(AStream, ARow, ACol, ExcelDateSerial, ACell);
|
2013-12-22 14:02:04 +00:00
|
|
|
end;
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
|
2008-02-24 13:18:34 +00:00
|
|
|
initialization
|
|
|
|
|
2015-04-18 14:58:38 +00:00
|
|
|
// Registers this reader / writer on fpSpreadsheet
|
2014-07-24 22:22:26 +00:00
|
|
|
RegisterSpreadFormat(TsSpreadOOXMLReader, TsSpreadOOXMLWriter, sfOOXML);
|
2015-04-18 14:58:38 +00:00
|
|
|
|
2008-02-24 13:18:34 +00:00
|
|
|
end.
|
|
|
|
|