fpspreadsheet: Read page layout from ods file (some simplifications, though)

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4107 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2015-05-01 22:51:01 +00:00
parent 7c5d6d88be
commit 3902f75dfc
3 changed files with 332 additions and 78 deletions

View File

@ -65,8 +65,9 @@ type
FColumnList: TFPList; FColumnList: TFPList;
FRowStyleList: TFPList; FRowStyleList: TFPList;
FRowList: TFPList; FRowList: TFPList;
FPageLayoutList: TFPList;
FMasterPageList: TFPList;
FDateMode: TDateMode; FDateMode: TDateMode;
FPageLayout: TsPageLayout;
// Applies internally stored column widths to current worksheet // Applies internally stored column widths to current worksheet
procedure ApplyColWidths; procedure ApplyColWidths;
// Applies a style to a cell // Applies a style to a cell
@ -86,6 +87,7 @@ type
// Figures out the base year for times in this file (dates are unambiguous) // Figures out the base year for times in this file (dates are unambiguous)
procedure ReadDateMode(SpreadSheetNode: TDOMNode); procedure ReadDateMode(SpreadSheetNode: TDOMNode);
function ReadFont(ANode: TDOMnode; APreferredIndex: Integer = -1): Integer; function ReadFont(ANode: TDOMnode; APreferredIndex: Integer = -1): Integer;
function ReadHeaderFooterAsXMLString(ANode: TDOMNode): String;
procedure ReadRowsAndCells(ATableNode: TDOMNode); procedure ReadRowsAndCells(ATableNode: TDOMNode);
procedure ReadRowStyle(AStyleNode: TDOMNode); procedure ReadRowStyle(AStyleNode: TDOMNode);
@ -93,7 +95,9 @@ type
FPointSeparatorSettings: TFormatSettings; FPointSeparatorSettings: TFormatSettings;
procedure AddBuiltinNumFormats; override; procedure AddBuiltinNumFormats; override;
procedure ReadAutomaticStyles(AStylesNode: TDOMNode); procedure ReadAutomaticStyles(AStylesNode: TDOMNode);
procedure ReadMasterStyles(AStylesNode: TDOMNode);
procedure ReadNumFormats(AStylesNode: TDOMNode); procedure ReadNumFormats(AStylesNode: TDOMNode);
function ReadPageLayout(AStylesNode: TDOMNode; ATableStyleName: String): PsPageLayout;
procedure ReadSettings(AOfficeSettingsNode: TDOMNode); procedure ReadSettings(AOfficeSettingsNode: TDOMNode);
procedure ReadStyles(AStylesNode: TDOMNode); procedure ReadStyles(AStylesNode: TDOMNode);
{ Record writing methods } { Record writing methods }
@ -282,6 +286,20 @@ type
AutoRowHeight: Boolean; AutoRowHeight: Boolean;
end; end;
{ PageLayout items stored in PageLayoutList }
TPageLayoutData = class
public
Name: String;
PageLayout: TsPageLayout;
end;
{ MasterPage items stored in MasterPageList }
TMasterPageData = class
public
Name: String;
PageLayoutName: String;
end;
(* --- presently not used, but this may change... --- (* --- presently not used, but this may change... ---
{ Row data items stored in the RowList of the reader } { Row data items stored in the RowList of the reader }
@ -605,23 +623,24 @@ end;
constructor TsSpreadOpenDocReader.Create(AWorkbook: TsWorkbook); constructor TsSpreadOpenDocReader.Create(AWorkbook: TsWorkbook);
begin begin
inherited Create(AWorkbook); inherited Create(AWorkbook);
FPointSeparatorSettings := DefaultFormatSettings; FPointSeparatorSettings := DefaultFormatSettings;
FPointSeparatorSettings.DecimalSeparator := '.'; FPointSeparatorSettings.DecimalSeparator := '.';
FPointSeparatorSettings.ListSeparator := ';'; // for formulas FPointSeparatorSettings.ListSeparator := ';'; // for formulas
FCellFormatList := TsCellFormatList.Create(true); FCellFormatList := TsCellFormatList.Create(true);
// Allow duplicates because style names used in cell records will not be found any more. // true = allow duplicates because style names used in cell records will not be found any more.
FColumnStyleList := TFPList.Create; FColumnStyleList := TFPList.Create;
FColumnList := TFPList.Create; FColumnList := TFPList.Create;
FRowStyleList := TFPList.Create; FRowStyleList := TFPList.Create;
FRowList := TFPList.Create; FRowList := TFPList.Create;
// FVolatileNumFmtList := TStringList.Create; FPageLayoutList := TFPList.Create;
FMasterPageList := TFPList.Create;
// Set up the default palette in order to have the default color names correct. // Set up the default palette in order to have the default color names correct.
Workbook.UseDefaultPalette; Workbook.UseDefaultPalette;
// Initial base date in case it won't be read from file // Initial base date in case it won't be read from file
FDateMode := dm1899; FDateMode := dm1899;
// Initialize internal PageLayout record
InitPageLayout(FPageLayout);
end; end;
destructor TsSpreadOpenDocReader.Destroy; destructor TsSpreadOpenDocReader.Destroy;
@ -640,7 +659,11 @@ begin
for j := FRowStyleList.Count-1 downto 0 do TObject(FRowStyleList[j]).Free; for j := FRowStyleList.Count-1 downto 0 do TObject(FRowStyleList[j]).Free;
FRowStyleList.Free; FRowStyleList.Free;
// FVolatileNumFmtList.Free; for j := FPageLayoutList.Count-1 downto 0 do TObject(FPageLayoutList[j]).Free;
FPageLayoutList.Free;
for j := FMasterPageList.Count-1 downto 0 do TObject(FMasterPageList[j]).Free;
FMasterPageList.Free;
inherited Destroy; inherited Destroy;
end; end;
@ -860,6 +883,7 @@ var
layoutNode: TDOMNode; layoutNode: TDOMNode;
node: TDOMNode; node: TDOMNode;
s: String; s: String;
data: TPageLayoutData;
begin begin
if not Assigned(AStylesNode) then if not Assigned(AStylesNode) then
exit; exit;
@ -868,79 +892,234 @@ begin
begin begin
nodeName := layoutNode.NodeName; nodeName := layoutNode.NodeName;
if nodeName = 'style:page-layout' then begin if nodeName = 'style:page-layout' then begin
s := GetAttrValue(layoutNode, 'style:name'); data := TPageLayoutData.Create;
if s = 'Mpm1' then InitPageLayout(data.PageLayout);
data.Name := GetAttrValue(layoutNode, 'style:name');
node := layoutNode.FirstChild;
while node <> nil do
begin begin
node := layoutNode.FirstChild; nodeName := node.NodeName;
while node <> nil do if nodeName = 'style:page-layout-properties' then
begin begin
nodeName := node.NodeName; s := GetAttrValue(node, 'style:print-orientation');
if nodeName = 'style:page-layout-properties' then if s = 'landscape' then
data.PageLayout.Orientation := spoLandscape
else if s = 'portrait' then
data.PageLayout.Orientation := spoPortrait;
s := GetAttrValue(node, 'fo:page-width');
if s <> '' then
data.PageLayout.PageWidth := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'fo:page-height');
if s <> '' then
data.PageLayout.PageHeight := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'fo:margin-top');
if s <> '' then
data.PageLayout.TopMargin := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'fo:margin-bottom');
if s <> '' then
data.PageLayout.BottomMargin := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'fo:margin-left');
if s <> '' then
data.PageLayout.LeftMargin := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'fo:margin-right');
if s <> '' then
data.PageLayout.RightMargin := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'style:scale-to');
if (s <> '') then
begin begin
s := GetAttrValue(node, 'style:print-orientation'); if s[Length(s)] = '%' then Delete(s, Length(s), 1);
if s = 'landscape' then data.PageLayout.ScalingFactor := StrToFloat(s, FPointSeparatorSettings);
FPageLayout.Orientation := spoLandscape
else if s = 'portrait' then
FPageLayout.Orientation := spoPortrait;
s := GetAttrValue(node, 'fo:page-width');
if s <> '' then
FPageLayout.PageWidth := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'fo:page-height');
if s <> '' then
FPageLayout.PageHeight := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'fo:margin-top');
if s <> '' then
FPageLayout.TopMargin := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'fo:margin-bottom');
if s <> '' then
FPageLayout.BottomMargin := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'fo:margin-left');
if s <> '' then
FPageLayout.LeftMargin := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'fo:margin-right');
if s <> '' then
FPageLayout.RightMargin := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'style:scale-to');
if (s <> '') then
begin
if s[Length(s)] = '%' then Delete(s, Length(s), 1);
FPageLayout.ScalingFactor := StrToFloat(s, FPointSeparatorSettings);
end;
s := GetAttrValue(node, 'style:print');
if pos('grid', s) > 0 then
Include(FPageLayout.Options, poPrintGridLines);
if pos('headers', s) > 0 then
Include(FPageLayout.Options, poPrintHeaders);
if pos('annotations', s) > 0 then
Include(FPageLayout.Options, poPrintCellComments);
s := GetAttrValue(node, 'style:print-page-order');
if s = 'ltr' then // "left-to-right", the other option is "ttb = top-to-bottom"
Include(FPageLayout.Options, poPrintPagesByRows);
s := GetAttrValue(node, 'style:first-page-number');
if s = 'continue' then
Exclude(FPageLayout.Options, poUseStartPageNumber)
else
if TryStrToInt(s, FPageLayout.StartPageNumber) then
Include(FPageLayout.Options, poUseStartPageNumber);
end; end;
node := node.NextSibling;
s := GetAttrValue(node, 'style:print');
if pos('grid', s) > 0 then
Include(data.PageLayout.Options, poPrintGridLines);
if pos('headers', s) > 0 then
Include(data.PageLayout.Options, poPrintHeaders);
if pos('annotations', s) > 0 then
Include(data.PageLayout.Options, poPrintCellComments);
s := GetAttrValue(node, 'style:print-page-order');
if s = 'ltr' then // "left-to-right", the other option is "ttb = top-to-bottom"
Include(data.PageLayout.Options, poPrintPagesByRows);
s := GetAttrValue(node, 'style:first-page-number');
if s = 'continue' then
Exclude(data.PageLayout.Options, poUseStartPageNumber)
else
if TryStrToInt(s, data.PageLayout.StartPageNumber) then
Include(data.PageLayout.Options, poUseStartPageNumber);
FPageLayoutList.Add(data);
end; end;
node := node.NextSibling;
end; end;
end; end;
layoutNode := layoutNode.NextSibling; layoutNode := layoutNode.NextSibling;
end; end;
end; end;
function TsSpreadOpenDocReader.ReadHeaderFooterAsXMLString(ANode: TDOMNode): String;
var
regionNode, textNode, spanNode: TDOMNode;
nodeName: String;
s: String;
begin
Result := '';
regionNode := ANode.FirstChild;
while regionNode <> nil do
begin
nodeName := regionNode.NodeName;
if nodeName = 'text:p' then
begin
if Result <> '' then Result := Result + LineEnding;
textNode := regionNode.FirstChild;
while textNode <> nil do
begin
nodeName := textNode.NodeName;
case nodeName of
'#text':
Result := Result + textNode.NodeValue;
'text:sheet-name':
Result := Result + '&A';
'text:page-number':
Result := Result + '&P';
'text:page-count':
Result := Result + '&N';
'text:date':
Result := Result + '&D';
'text:time':
Result := Result + '&T';
'text:file-name':
case GetAttrValue(textNode, 'text:display') of
'full': Result := Result + '&Z&F';
'path': Result := Result + '&Z';
else Result := Result + '&F';
end;
'text:span':
begin
spanNode := textNode.FirstChild;
while spanNode <> nil do
begin
nodeName := spanNode.NodeName;
case nodeName of
'#text': Result := Result + spanNode.NodeValue;
end;
spanNode := spanNode.NextSibling;
end;
end;
end;
textNode := textNode.NextSibling;
end;
end else
if (nodeName = 'style:region-left') then
begin
s := ReadHeaderFooterAsXMLString(regionNode);
Result := Result + '&L' + s;
end else
if (nodeName = 'style:region-center') then
begin
s := ReadHeaderFooterAsXMLString(regionNode);
Result := Result + '&C' + s;
end else
if (nodeName = 'style:region-right') then
begin
s := ReadHeaderFooterAsXMLString(regionNode);
Result := Result + '&R' + s;
end;
regionNode := regionNode.NextSibling;
end;
end;
procedure TsSpreadOpenDocReader.ReadMasterStyles(AStylesNode: TDOMNode);
var
masternode, stylenode, regionnode: TDOMNode;
nodeName: String;
s: String;
data: TMasterPageData;
pagelayout: PsPageLayout;
j: Integer;
begin
if AStylesNode = nil then
exit;
masterNode := AStylesNode.FirstChild;
while (masterNode <> nil) do
begin
nodeName := masterNode.NodeName;
if nodeName = 'style:master-page' then begin
s := GetAttrvalue(masterNode, 'style:page-layout-name');
pageLayout := nil;
for j:=0 to FPageLayoutList.Count-1 do
if TPageLayoutData(FPageLayoutList[j]).Name = s then
begin
pageLayout := @TPageLayoutData(FPageLayoutList[j]).PageLayout;
break;
end;
if pagelayout = nil then
exit;
data := TMasterPageData.create;
data.Name := GetAttrValue(masternode, 'style:name');
data.PageLayoutName := s;
FMasterPageList.Add(data);
styleNode := masterNode.FirstChild;
while styleNode <> nil do begin
nodeName := styleNode.NodeName;
if nodeName = 'style:header' then
begin
s := ReadHeaderFooterAsXMLString(styleNode);
if s <> '' then
pageLayout^.Headers[HEADER_FOOTER_INDEX_ODD] := s;
end else
if nodeName = 'style:header-left' then
begin
s := ReadHeaderFooterAsXMLString(styleNode);
if s <> '' then
begin
pageLayout^.Headers[HEADER_FOOTER_INDEX_EVEN] := s;
Include(pageLayout^.Options, poDifferentOddEven);
end;
s := GetAttrValue(styleNode, 'style:display');
if s = 'false' then
Exclude(pagelayout^.Options, poDifferentOddEven);
end else
if nodeName = 'style:footer' then
begin
s := ReadHeaderFooterAsXMLString(styleNode);
if s <> '' then
pageLayout^.Footers[HEADER_FOOTER_INDEX_ODD] := s;
end else
if nodeName = 'style:footer-left' then
begin
s := ReadHeaderFooterAsXMLString(styleNode);
if s <> '' then
begin
pageLayout^.Footers[HEADER_FOOTER_INDEX_EVEN] := s;
Include(pageLayout^.Options, poDifferentOddEven);
end;
s := GetAttrValue(styleNode, 'style:display');
if s = 'false' then
Exclude(pagelayout^.Options, poDifferentOddEven);
end;
styleNode := styleNode.NextSibling;
end;
end;
masterNode := masterNode.NextSibling;
end;
end;
procedure TsSpreadOpenDocReader.ReadBlank(ARow, ACol: Cardinal; procedure TsSpreadOpenDocReader.ReadBlank(ARow, ACol: Cardinal;
ACellNode: TDOMNode); ACellNode: TDOMNode);
var var
@ -1382,6 +1561,7 @@ var
StylesNode: TDOMNode; StylesNode: TDOMNode;
OfficeSettingsNode: TDOMNode; OfficeSettingsNode: TDOMNode;
nodename: String; nodename: String;
pageLayout: PsPageLayout;
begin begin
//unzip files into AFileName path //unzip files into AFileName path
FilePath := GetTempDir(false); FilePath := GetTempDir(false);
@ -1411,6 +1591,9 @@ begin
StylesNode := Doc.DocumentElement.FindNode('office:automatic-styles'); StylesNode := Doc.DocumentElement.FindNode('office:automatic-styles');
ReadAutomaticStyles(StylesNode); ReadAutomaticStyles(StylesNode);
StylesNode := Doc.DocumentElement.FindNode('office:master-styles');
ReadMasterStyles(StylesNode);
Doc.Free; Doc.Free;
//process the content.xml file //process the content.xml file
@ -1442,14 +1625,18 @@ begin
TableNode := TableNode.NextSibling; TableNode := TableNode.NextSibling;
continue; continue;
end; end;
FWorkSheet := FWorkbook.AddWorksheet(GetAttrValue(TableNode,'table:name'), true); FWorkSheet := FWorkbook.AddWorksheet(GetAttrValue(TableNode, 'table:name'), true);
FWorksheet.PageLayout := FPageLayout;
// Collect column styles used // Collect column styles used
ReadColumns(TableNode); ReadColumns(TableNode);
// Process each row inside the sheet and process each cell of the row // Process each row inside the sheet and process each cell of the row
ReadRowsAndCells(TableNode); ReadRowsAndCells(TableNode);
// Read page layout
pageLayout := ReadPageLayout(StylesNode, GetAttrValue(TableNode, 'table:style-name'));
if pageLayout <> nil then
FWorksheet.PageLayout := pagelayout^;
// Handle columns and rows // Handle columns and rows
ApplyColWidths; ApplyColWidths;
// Page layout
FixCols(FWorksheet); FixCols(FWorksheet);
FixRows(FWorksheet); FixRows(FWorksheet);
// Continue with next table // Continue with next table
@ -2011,6 +2198,71 @@ begin
end; end;
end; end;
{ Finds the PageLayout record for a given TableStyle name in the "styles" nodes.
First, seeks the TableStyle among the children of the "styles" node in the
contents.xml - this node contains the name of the used master page.
Then seeks the FMasterPageList for the entry with the determined master page
name. This entry contains the name of the associated PageLayoutData stored in
the PageLayoutList which, finally, contains the requested PageLayout record. }
function TsSpreadOpenDocReader.ReadPageLayout(AStylesNode: TDOMNode;
ATableStyleName: String): PsPageLayout;
var
nodeName, s: String;
node: TDOMNode;
masterPageName: String;
masterPageData: TMasterPageData;
pageLayoutData: TPageLayoutData;
i, j: Integer;
begin
Result := nil;
if AStylesNode = nil then
exit;
{ Looking through the "styles" node...}
node := AStylesNode.FirstChild;
while node <> nil do
begin
nodeName := node.NodeName;
{ ... for the node which is named like the requested TableStyle }
if nodeName = 'style:style' then
begin
s := GetAttrValue(node, 'style:name');
if s = ATableStyleName then
begin
{ Found: extract the name of the master page }
masterPageName := GetAttrValue(node, 'style:master-page-name');
if masterPageName = '' then
exit;
{ Looking through the MasterPage list...}
for i:=0 to FMasterPageList.Count-1 do
begin
masterPageData := TMasterPageData(FMasterPageList[i]);
{ ... for the entry with the found master page name }
if masterPageData.Name = masterPageName then
begin
{ Found: looking through the PageLayout list ...}
for j:=0 to FPageLayoutList.Count-1 do
begin
pageLayoutData := TPageLayoutData(FPageLayoutList[j]);
{ ... for the entry with the name specified by the master page }
if pageLayoutData.Name = masterPageData.PageLayoutName then
begin
{ Found: Return a pointer to the PageLayout record stored in the list }
Result := @pageLayoutData.PageLayout;
exit;
end;
end;
end;
end;
end;
end;
{ Not found: try next node in the styles list }
node := node.NextSibling;
end;
end;
{ Reads the cells in the given table. Loops through all rows, and then finds all { Reads the cells in the given table. Loops through all rows, and then finds all
cells of each row. } cells of each row. }
procedure TsSpreadOpenDocReader.ReadRowsAndCells(ATableNode: TDOMNode); procedure TsSpreadOpenDocReader.ReadRowsAndCells(ATableNode: TDOMNode);

View File

@ -2925,16 +2925,16 @@ begin
AStrings.Add(Format(' Copies=%d', [ASheet.PageLayout.Copies])); AStrings.Add(Format(' Copies=%d', [ASheet.PageLayout.Copies]));
if (ASheet.PageLayout.Options * [poDifferentOddEven, poDifferentFirst] <> []) then if (ASheet.PageLayout.Options * [poDifferentOddEven, poDifferentFirst] <> []) then
begin begin
AStrings.Add(Format(' Header (first)=%s', [ASheet.PageLayout.Headers[0]])); AStrings.Add(Format(' Header (first)=%s', [StringReplace(ASheet.PageLayout.Headers[0], LineEnding, '\n', [rfReplaceAll])]));
AStrings.Add(Format(' Header (odd)=%s', [ASheet.PageLayout.Headers[1]])); AStrings.Add(Format(' Header (odd)=%s', [StringReplace(ASheet.PageLayout.Headers[1], LineEnding, '\n', [rfReplaceAll])]));
AStrings.Add(Format(' Header (even)=%s', [ASheet.PageLayout.Headers[2]])); AStrings.Add(Format(' Header (even)=%s', [StringReplace(ASheet.PageLayout.Headers[2], LineEnding, '\n', [rfReplaceAll])]));
AStrings.Add(Format(' Footer (first)=%s', [ASheet.PageLayout.Footers[0]])); AStrings.Add(Format(' Footer (first)=%s', [StringReplace(ASheet.PageLayout.Footers[0], LineEnding, '\n', [rfReplaceAll])]));
AStrings.Add(Format(' Footer (odd)=%s', [ASheet.PageLayout.Footers[1]])); AStrings.Add(Format(' Footer (odd)=%s', [StringReplace(ASheet.PageLayout.Footers[1], LineEnding, '\n', [rfReplaceall])]));
AStrings.Add(Format(' Footer (even)=%s', [ASheet.PageLayout.Footers[2]])); AStrings.Add(Format(' Footer (even)=%s', [StringReplace(ASheet.PageLayout.Footers[2], LineEnding, '\n', [rfReplaceAll])]));
end else end else
begin begin
AStrings.Add(Format(' Header=%s', [ASheet.PageLayout.Headers[1]])); AStrings.Add(Format(' Header=%s', [StringReplace(ASheet.PageLayout.Headers[1], LineEnding, '\n', [rfReplaceAll])]));
AStrings.Add(Format(' Footer=%s', [ASheet.PageLayout.Footers[1]])); AStrings.Add(Format(' Footer=%s', [StringReplace(ASheet.PageLayout.Footers[1], LineEnding, '\n', [rfReplaceAll])]));
end; end;
s := ''; s := '';
for po in TsPrintOption do for po in TsPrintOption do

View File

@ -714,6 +714,8 @@ type
Footers: array[0..2] of string; Footers: array[0..2] of string;
end; end;
PsPageLayout = ^TsPageLayout;
const const
{@@ Indexes to be used for the various headers and footers } {@@ Indexes to be used for the various headers and footers }
HEADER_FOOTER_INDEX_FIRST = 0; HEADER_FOOTER_INDEX_FIRST = 0;