diff --git a/components/fpspreadsheet/examples/visual/fpsctrls/demo_ctrls.lpr b/components/fpspreadsheet/examples/visual/fpsctrls/demo_ctrls.lpr index 40ada83fc..4314a1866 100644 --- a/components/fpspreadsheet/examples/visual/fpsctrls/demo_ctrls.lpr +++ b/components/fpspreadsheet/examples/visual/fpsctrls/demo_ctrls.lpr @@ -7,8 +7,7 @@ uses cthreads, {$ENDIF}{$ENDIF} Interfaces, // this includes the LCL widgetset - Forms, main, sHyperlinkForm, sNumFormatForm - { you can add units after this }; + Forms, main, sHyperlinkForm, sNumFormatForm; {$R *.res} diff --git a/components/fpspreadsheet/fpsheaderfooterparser.pas b/components/fpspreadsheet/fpsheaderfooterparser.pas new file mode 100644 index 000000000..26e68e44a --- /dev/null +++ b/components/fpspreadsheet/fpsheaderfooterparser.pas @@ -0,0 +1,456 @@ +unit fpsHeaderFooterParser; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, fpsTypes; + +type + TsHeaderFooterToken = (hftText, hftNewLine, + hftSheetName, hftPath, hftFileName, hftDate, hftTime, hftPage, hftPageCount); + + TsHeaderFooterFontStyle = (hfsBold, hfsItalic, hfsUnderline, hfsDblUnderline, + hfsStrikeout, hfsShadow, hfsOutline, hfsSubscript, hfsSuperScript); + + TsHeaderFooterFontStyles = set of TsHeaderFooterFontStyle; + + TsHeaderFooterFont = class(TObject) + FontName: String; + Size: Double; + Style: TsHeaderFooterFontStyles; + Color: TsColorValue; + constructor Create; overload; + constructor Create(AFont: TsFont); overload; + constructor Create(AFontName: String; ASize: Double; + AStyle: TsHeaderFooterFontStyles; AColor: TsColorValue); overload; + procedure Assign(AFont: TObject); + end; + + TsHeaderFooterFontClass = class of TsHeaderFooterFont; + + TsHeaderFooterElement = record + Token: TsHeaderFooterToken; + TextValue: String; + FontIndex: Integer; + end; + + TsHeaderFooterSectionIndex = (hfsLeft, hfsCenter, hfsRight); + + TsHeaderFooterSection = array of TsHeaderFooterElement; + + TsHeaderFooterSections = array[TsHeaderFooterSectionIndex] of TsHeaderFooterSection; + + TsHeaderFooterParser = class(TObject) + private + FParseText: String; + FToken: Char; + FCurrent: PChar; + FStart: PChar; + FEnd: PChar; + FCurrFont: TsHeaderFooterFont; + function NextToken: Char; + function PrevToken: Char; + procedure ScanFont; + procedure ScanFontColor; + procedure ScanFontSize; + procedure ScanNewLine; + procedure ScanSymbol; + protected + FSections: TsHeaderFooterSections; + FDefaultFont: TsHeaderFooterFont; + FCurrSection: TsHeaderFooterSectionIndex; + FStatus: Integer; + FFontList: TList; + FPointSeparatorSettings: TFormatSettings; + FCurrFontIndex: Integer; + FCurrText: String; + FFontClass: TsHeaderFooterFontClass; + procedure AddCurrTextElement; + procedure AddElement(AToken: TsHeaderFooterToken); + procedure AddFontStyle(AStyle: TsHeaderFooterFontStyle); + function FindCurrFont: Integer; + function GetCurrFontIndex: Integer; virtual; + procedure Parse; virtual; + procedure UseSection(AIndex: TsHeaderFooterSectionIndex); virtual; + public + constructor Create; overload; + constructor Create(AText: String; AFontList: TList; + ADefaultFont: TsHeaderFooterFont); overload; + destructor Destroy; override; + function BuildHeaderFooter: String; + end; + +const + hfpsOK = 0; + +implementation + +uses + Math, + fpsUtils; + +const + FONTSTYLE_SYMBOLS: array[TsHeaderFooterFontStyle] of char = + ('B', 'I', 'U', 'E', 'S', 'H', 'O', 'X', 'Y'); + +constructor TsHeaderFooterFont.Create; +begin + inherited; +end; + +constructor TsHeaderFooterFont.Create(AFontName: String; ASize: Double; + AStyle: TsHeaderFooterFontStyles; AColor: TsColorValue); +begin + FontName := AFontName; + Size := ASize; + Style := AStyle; + Color := AColor; +end; + +constructor TsHeaderFooterFont.Create(AFont: TsFont); +begin + Create; + Assign(AFont); +end; + +procedure TsHeaderFooterFont.Assign(AFont: TObject); +begin + if AFont is TsFont then + begin + FontName := TsFont(AFont).FontName; + Size := TsFont(AFont).Size; + Style := []; + if fssBold in TsFont(AFont).Style then Include(Style, hfsBold); + if fssItalic in TsFont(AFont).Style then Include(Style, hfsItalic); + if fssUnderline in TsFont(AFont).Style then Include(Style, hfsUnderline); + if fssStrikeout in TsFont(AFont).Style then Include(Style, hfsStrikeout); + Color := 0; // black --- to be replaced by TsFont.Color once it is no longer paletted + end else + if AFont is TsHeaderFooterFont then + begin + FontName := TsHeaderFooterFont(AFont).FontName; + Size := TsHeaderFooterFont(AFont).Size; + Style := TsHeaderFooterFont(AFont).Style; + Color := TsHeaderFooterFont(AFont).Color; + end else + raise Exception.Create('[TsHeaderFooterFont.Assign] Argument can only be a TsFont or a TsHeaderFooterFont'); +end; + + +{ TsHeaderFooterParser } + +constructor TsHeaderFooterParser.Create; +begin + FFontClass := TsHeaderFooterFont; + + FPointSeparatorSettings := DefaultFormatSettings; + FPointSeparatorSettings.DecimalSeparator := '.'; + + FCurrSection := hfsCenter; + FCurrText := ''; +end; + +constructor TsHeaderFooterParser.Create(AText: String; AFontList: TList; + ADefaultFont: TsHeaderFooterFont); +begin + if AFontList = nil then + raise Exception.Create('[TsHeaderFooterParser.Create] FontList must not be nil.'); + if ADefaultFont = nil then + raise Exception.Create('[TsHeaderFooterParser.Create] DefaultFont must not be nil.'); + + Create; + + FFontList := AFontList; + FDefaultFont := ADefaultFont; + FCurrFont := TsHeaderFooterFont.Create; + FCurrFont.Assign(ADefaultFont); + FParseText := AText; + + Parse; +end; + +destructor TsHeaderFooterParser.Destroy; +begin + FCurrFont.Free; + inherited Destroy; +end; + +procedure TsHeaderFooterParser.AddCurrTextElement; +begin + AddElement(hftText); +end; + +procedure TsHeaderFooterParser.AddElement(AToken: TsHeaderFooterToken); +var + n: Integer; +begin + n := Length(FSections[FCurrSection]); + SetLength(FSections[FCurrSection], n+1); + with FSections[FCurrSection][n] do + begin + Token := AToken; + if Token = hftText then + begin + TextValue := FCurrText; + FCurrText := ''; + end else + TextValue := ''; + FontIndex := GetCurrFontIndex; + end; +end; + +procedure TsHeaderFooterParser.AddFontStyle(AStyle: TsHeaderFooterFontStyle); +begin + if FCurrText <> '' then + AddCurrTextElement; + + if AStyle in FCurrFont.Style then + Exclude(FCurrFont.Style, AStyle) + else + Include(FCurrFont.Style, AStyle); +end; + +function TsHeaderFooterParser.BuildHeaderFooter: String; +var + sec: TsHeaderFooterSectionIndex; + element: TsHeaderFooterElement; + fnt, prevfnt: TsHeaderFooterFont; + fs: TsHeaderFooterFontStyle; + i: Integer; +begin + Result := ''; + for sec := hfsLeft to hfsRight do + begin + prevfnt := FDefaultFont; + if Length(FSections[sec]) > 0 then + case sec of + hfsLeft : Result := Result + '&L'; + hfsCenter : Result := Result + '&C'; + hfsRight : Result := Result + '&R'; + end; + for element in FSections[sec] do + begin + if (element.FontIndex > -1) and (element.FontIndex < FFontList.Count) then + begin + fnt := TsHeaderFooterFont(FFontList[element.FontIndex]); + if fnt.FontName = '' then fnt.FontName := FDefaultFont.FontName; + if not SameText(fnt.FontName, prevFnt.FontName) then + Result := Result + '&"' + fnt.FontName + '"'; + if not SameValue(fnt.Size, prevfnt.Size, 1e-2) then + Result := Result + '&' + Format('%d', [round(fnt.Size)]); // Excel wants only integers! + for fs in TsHeaderFooterFontStyle do + if ((fs in fnt.Style) and not (fs in prevfnt.Style)) or + (not (fs in fnt.Style) and (fs in prevfnt.Style)) + then + Result := Result + '&' + FONTSTYLE_SYMBOLS[fs]; + if fnt.Color <> prevfnt.Color then + Result := Result + '&K' + ColorToHTMLColorStr(fnt.Color, true); + prevfnt := fnt; + end; + case element.Token of + hftText : for i:=1 to length(element.TextValue) do + if element.TextValue[i]='&' + then Result := Result + '&&' + else Result := Result + element.TextValue[i]; + hftSheetName : Result := Result + '&A'; + hftPath : Result := Result + '&Z'; + hftFileName : Result := Result + '&F'; + hftDate : Result := Result + '&D'; + hftTime : Result := Result + '&T'; + hftPage : Result := Result + '&P'; + hftPageCount : Result := Result + '&N'; + hftNewLine : Result := Result + LineEnding; + end; + end; // for element + end; // for sesc +end; + +function TsHeaderFooterParser.FindCurrFont: Integer; +var + fnt: TsHeaderFooterFont; +begin + for Result := 0 to FFontList.Count-1 do + begin + fnt := TsHeaderFooterFont(FFontList[Result]); + if SameText(fnt.FontName, FCurrFont.FontName) and + SameValue(fnt.Size, FCurrFont.Size) and + (fnt.Style = FCurrFont.Style) and + (fnt.Color = FCurrFont.Color) + then + exit; + end; + Result := -1; +end; + +function TsHeaderFooterParser.GetCurrFontIndex: Integer; +var + fnt: TsHeaderFooterFont; +begin + Result := FindCurrFont; + if Result = -1 then + begin + fnt := FFontClass.Create; + fnt.Assign(FCurrFont); + Result := FFontList.Add(fnt); + end; +end; + +function TsHeaderFooterParser.NextToken: Char; +begin + if FCurrent < FEnd then begin + inc(FCurrent); + Result := FCurrent^; + end else + Result := #0; +end; + +function TsHeaderFooterParser.PrevToken: Char; +begin + if FCurrent > nil then begin + dec(FCurrent); + Result := FCurrent^; + end else + Result := #0; +end; + +procedure TsHeaderFooterParser.Parse; +begin + if FParseText = '' then + exit; + + FStart := @FParseText[1]; + FEnd := FStart + Length(FParseText); + FCurrent := FStart; + FToken := FCurrent^; + FCurrSection := hfsCenter; + + while (FCurrent < FEnd) and (FStatus = hfpsOK) do begin + case FToken of + '&': ScanSymbol; + #13, #10: ScanNewLine; + else FCurrText := FCurrText + FToken; + end; + FToken := NextToken; + end; + if Length(FCurrText) > 0 then + AddCurrTextElement; +end; + +procedure TsHeaderFooterParser.ScanFont; +var + s: String; +begin + s := ''; + FToken := NextToken; + while (FCurrent < FEnd) and (FStatus = hfpsOK) and not (FToken in ['"', ',']) do + begin + // Excel allows to add a font-style identifier to the font name, separated + // by a comma. We do not support this feature because the font style + // identifier is a localized string! --> Skip text after the comma + if FToken = ',' then + begin + while (FCurrent < FEnd) and (FToken <> '"') do + FToken := NextToken; + break; + end else + begin + s := s + FToken; + FToken := NextToken; + end; + end; + FCurrFont.FontName := s; +end; + +procedure TsHeaderFooterParser.ScanFontColor; +var + s: String; +begin + s := '#'; + FToken := NextToken; + while (FCurrent < FEnd) and (FStatus = hfpsOK) and (FToken in ['0'..'9', 'A'..'F']) do + begin + s := s + FToken; + FToken := NextToken; + end; + FToken := PrevToken; + FCurrFont.Color := HTMLColorStrToColor(s); +end; + +procedure TsHeaderFooterParser.ScanFontSize; +var + s: String; +begin + s := ''; + while (FCurrent < FEnd) and (FStatus = hfpsOK) and (FToken in ['0'..'9','.']) do + begin + s := s + FToken; + FToken := NextToken; + end; + FToken := PrevToken; + FCurrFont.Size := StrToFloat(s, FPointSeparatorSettings); +end; + +procedure TsHeaderFooterParser.ScanNewLine; +begin + case FToken of + #13: begin + AddElement(hftNewLine); + FToken := NextToken; + if FToken <> #10 then FToken := PrevToken; + end; + #10: AddElement(hftNewLine); + end; +end; + +procedure TsHeaderFooterParser.ScanSymbol; +begin + FToken := NextToken; + + if FToken = '&' then + FCurrText := FCurrText + '&' + else + begin + if FCurrText <> '' then + AddCurrTextElement; + case FToken of + 'L': UseSection(hfsLeft); + 'C': UseSection(hfsCenter); + 'R': UseSection(hfsRight); + 'A': AddElement(hftSheetName); + 'F': AddElement(hftFileName); + 'Z': AddElement(hftPath); + 'D': AddElement(hftDate); + 'T': AddElement(hftTime); + 'P': AddElement(hftPage); + 'N': AddElement(hftPageCount); + '"': ScanFont; + '0'..'9', '.': ScanFontSize; + 'K': ScanFontColor; + 'B': AddFontStyle(hfsBold); + 'I': AddFontStyle(hfsItalic); + 'U': AddFontStyle(hfsUnderline); + 'E': AddFontStyle(hfsDblUnderline); + 'S': AddFontStyle(hfsStrikeout); + 'H': AddFontStyle(hfsShadow); + 'O': AddFontStyle(hfsOutline); + 'X': AddFontStyle(hfsSuperscript); + 'Y': AddFontStyle(hfsSubscript); + end; + end; +end; + +procedure TsHeaderFooterParser.UseSection(AIndex: TsHeaderFooterSectionIndex); +begin + if FCurrText <> '' then + AddCurrTextElement; + FCurrFont.FontName := FDefaultFont.FontName; + FCurrFont.Size := FDefaultFont.Size; + FCurrFont.Style := FDefaultFont.Style; + FCurrFont.Color := FDefaultFont.Color; + FCurrSection := AIndex; +end; + +end. + diff --git a/components/fpspreadsheet/fpsopendocument.pas b/components/fpspreadsheet/fpsopendocument.pas index 1aaa24651..4b2ac07a5 100755 --- a/components/fpspreadsheet/fpsopendocument.pas +++ b/components/fpspreadsheet/fpsopendocument.pas @@ -32,14 +32,14 @@ interface uses Classes, SysUtils, laz2_xmlread, laz2_DOM, - AVL_Tree, math, dateutils, + AVL_Tree, math, dateutils, contnrs, {$IF FPC_FULLVERSION >= 20701} zipper, {$ELSE} fpszipper, {$ENDIF} - fpstypes, fpspreadsheet, fpsReaderWriter, - fpsutils, fpsNumFormat, fpsNumFormatParser, fpsxmlcommon; + fpstypes, fpspreadsheet, fpsReaderWriter, fpsutils, fpsHeaderFooterParser, + fpsNumFormat, fpsNumFormatParser, fpsxmlcommon; type TDateMode=( @@ -48,7 +48,25 @@ type dm1904 {e.g. Quattro Pro, Mac Excel compatibility} ); + { TsSpreadOpenDocHeaderFooterParser } + + TsSpreadOpenDocHeaderFooterParser = class(TsHeaderFooterParser) + private + FNode: TDOMNode; + XMLMode: Boolean; + protected + procedure AddNodeElement(ANode: TDOMNode); + function FindStyle(AStyleName: String): Integer; + function GetCurrFontIndex: Integer; override; + procedure Parse; override; + public + constructor Create(ANode: TDOMNode; AFontList: TList; + ADefaultFont: TsHeaderFooterFont); overload; + function BuildHeaderFooterAsXMLString: String; + end; + { TsSpreadOpenDocNumFormatParser } + TsSpreadOpenDocNumFormatParser = class(TsNumFormatParser) protected function BuildXMLAsStringFromSection(ASection: Integer; @@ -67,7 +85,7 @@ type FRowList: TFPList; FPageLayoutList: TFPList; FMasterPageList: TFPList; - FHeaderFooterFontList: TStringList; + FHeaderFooterFontList: TObjectList; FDateMode: TDateMode; // Applies internally stored column widths to current worksheet procedure ApplyColWidths; @@ -88,6 +106,9 @@ type // Figures out the base year for times in this file (dates are unambiguous) procedure ReadDateMode(SpreadSheetNode: TDOMNode); function ReadFont(ANode: TDOMnode; APreferredIndex: Integer = -1): Integer; + procedure ReadHeaderFooterFont(ANode: TDOMNode; var AFontName: String; + var AFontSize: Double; var AFontStyle: TsHeaderFooterFontStyles; + var AFontColor: TsColorValue); function ReadHeaderFooterText(ANode: TDOMNode): String; procedure ReadRowsAndCells(ATableNode: TDOMNode); procedure ReadRowStyle(AStyleNode: TDOMNode); @@ -125,6 +146,7 @@ type private FColumnStyleList: TFPList; FRowStyleList: TFPList; + FHeaderFooterFontList: TObjectList; // Routines to write parts of files procedure WriteAutomaticStyles(AStream: TStream); @@ -144,8 +166,11 @@ type function WriteBorderStyleXMLAsString(const AFormat: TsCellFormat): String; function WriteCommentXMLAsString(AComment: String): String; function WriteDefaultFontXMLAsString: String; - function WriteFontStyleXMLAsString(const AFormat: TsCellFormat): String; + function WriteFontStyleXMLAsString(const AFormat: TsCellFormat): String; overload; + function WriteFontStyleXMLAsString(AFont: TsFont): String; overload; + function WriteHeaderFooterFontXMLAsString(AFont: TsHeaderFooterFont): String; function WriteHorAlignmentStyleXMLAsString(const AFormat: TsCellFormat): String; + function WritePageLayoutAsXMLString(AStyleName: String; const APageLayout: TsPageLayout): String; function WriteTextRotationStyleXMLAsString(const AFormat: TsCellFormat): String; function WriteVertAlignmentStyleXMLAsString(const AFormat: TsCellFormat): String; function WriteWordwrapStyleXMLAsString(const AFormat: TsCellFormat): String; @@ -314,6 +339,204 @@ type end; *) +{******************************************************************************} +{ TXMLHeaderFooterFont } +{******************************************************************************} +type + TXMLHeaderFooterFont = class(TsHeaderFooterFont) + StyleName: String; + end; + +{******************************************************************************} +{ TsSpreadOpenDocHeaderFooterParser } +{******************************************************************************} + +constructor TsSpreadOpenDocHeaderFooterParser.Create(ANode: TDOMNode; + AFontList: TList; ADefaultFont: TsHeaderFooterFont); +begin + inherited Create; + XMLMode := true; + FNode := ANode; // this is a child of the "" etc nodes. + FFontList := AFontList; + FDefaultFont := ADefaultFont; + FFontClass := TXMLHeaderFooterFont; + Parse; +end; + +procedure TsSpreadOpenDocHeaderFooterParser.AddNodeElement(ANode: TDOMNode); +var + nodeName: String; +begin + nodeName := ANode.NodeName; + case nodeName of + 'text:sheet-name': + AddElement(hftSheetName); + 'text:file-name' : + case GetAttrValue(ANode, 'text:display') of + 'full': begin + AddElement(hftPath); + AddElement(hftFileName); + end; + 'path': AddElement(hftPath); + else AddElement(hftFileName); + end; + 'text:date': + AddElement(hftDate); + 'text:time': + AddElement(hftTime); + 'text:page-number': + AddElement(hftPage); + 'text:page-count': + AddElement(hftPageCount); + '#text': + begin + FCurrText := ANode.NodeValue; //GetNodeValue(ANode); + AddCurrTextElement; + end; + end; +end; + +function TsSpreadOpenDocHeaderFooterParser.BuildHeaderFooterAsXMLString: String; +var + list: TStringList; + regionStr: array[TsHeaderFooterSectionIndex] of String; + sec: TsHeaderFooterSectionIndex; + element: TsHeaderFooterElement; + styleName: String; + s: String; +begin + for sec := hfsLeft to hfsRight do + begin + if Length(FSections[sec]) = 0 then + Continue; + regionStr[sec] := ''; + for element in FSections[sec] do + begin + stylename := TXMLHeaderFooterFont(FFontList[element.FontIndex]).StyleName; + case element.Token of + hftText: + s := Format('%s', [ + stylename, UTF8TextToXMLText(element.TextValue)]); + hftNewLine: + s := ''; + hftSheetName: + s := '???'; + hftPath: + s := '???'; + hftFileName: + s := '???'; + hftDate: + s := Format('%s', [ + FormatDateTime('yyyy"-"mm"-"dd', date()), DateToStr(date())]); + hftTime: + s := Format('%s', [ + FormatDateTime('hh:nn:ss', time()) ]); + hftPage: + s := '1'; + hftPageCount: + s := '1'; + end; + regionStr[sec] := regionStr[sec] + s; + end; // for element + if regionStr[sec] <> '' then + regionStr[sec] := '' + regionStr[sec] + ''; + end; // for sec + + Result := ''; + for sec := hfsLeft to hfsRight do + begin + case sec of + hfsLeft : s := 'style:region-left'; + hfsCenter : s := 'style:region-center'; + hfsRight : s := 'style:region-right'; + end; + if regionStr[sec] = '' then + Result := Result + '<' + s + ' />' else + Result := Result + '<' + s + '>' + regionStr[sec] + ''; + end; +end; + +function TsSpreadOpenDocHeaderFooterParser.FindStyle(AStyleName: String): Integer; +var + fnt: TXMLHeaderFooterFont; +begin + for Result := 0 to FFontList.Count-1 do + begin + fnt := TXMLHeaderFooterFont(FFontList[Result]); + if SameText(fnt.StyleName, AStyleName) then + exit; + end; + Result := -1; +end; + +function TsSpreadOpenDocHeaderFooterParser.GetCurrFontIndex: Integer; +begin + if XMLMode then + Result := FCurrFontIndex + else + Result := inherited GetCurrFontIndex; +end; + +procedure TsSpreadOpenDocHeaderFooterParser.Parse; +var + node, pnode, childpnode, childspannode, textnode: TDOMNode; + nodeName: String; + s: String; + firstP: Boolean; +begin + FFontClass := TXMLHeaderFooterFont; + + if not XMLMode then + begin + inherited Parse; + exit; + end; + + node := FNode; + while Assigned(node) do + begin + nodeName := node.NodeName; + case nodeName of + 'style:region-left' : FCurrSection := hfsLeft; + 'style:region-center' : FCurrSection := hfsCenter; + 'style:region-right' : FCurrSection := hfsRight; + end; + firstP := true; + pnode := node.FirstChild; + while Assigned(pnode) do + begin + nodeName := pnode.NodeName; + if nodeName = 'text:p' then + begin + // if not firstP then AddElement(hftNewLine); + childpnode := pnode.FirstChild; + while Assigned(childpnode) do + begin + nodeName := childpnode.NodeName; + if nodeName = 'text:span' then begin + s := GetAttrValue(childpnode, 'text:style-name'); + if s <> '' then + FCurrFontIndex := FindStyle(s) else + FCurrFontIndex := -1; + childspannode := childpnode.FirstChild; + while Assigned(childspannode) do + begin + nodeName := childspannode.NodeName; + AddNodeElement(childspannode); + childspannode := childspannode.NextSibling; + end; + end else + AddNodeElement(childpnode); + childpnode := childpnode.NextSibling; + end; + firstP := false; + end; + pnode := pnode.NextSibling; + end; + node := node.NextSibling; + end; +end; + {******************************************************************************} { TsSpreadOpenDocNumFormatParser } @@ -640,7 +863,7 @@ begin FRowList := TFPList.Create; FPageLayoutList := TFPList.Create; FMasterPageList := TFPList.Create; - FHeaderFooterFontList := TStringList.Create; + FHeaderFooterFontList := TObjectList.Create; // frees objects // Set up the default palette in order to have the default color names correct. Workbook.UseDefaultPalette; @@ -893,27 +1116,41 @@ var isHeader: Boolean; h: Double; idx: Integer; + fnt: TXMLHeaderFooterFont; + defFnt: TsFont; + fntName: String; + fntSize: Double; + fntStyle: TsHeaderFooterFontStyles; + fntColor: TsColorValue; begin if not Assigned(AStylesNode) then exit; + defFnt := Workbook.GetDefaultFont; layoutNode := AStylesNode.FirstChild; while layoutNode <> nil do begin nodeName := layoutNode.NodeName; if nodeName = 'style:style' then begin + // Read fonts used by page layout's header/footer + fntName := defFnt.FontName; + fntSize := defFnt.Size; + fntColor := defFnt.Color; s := GetAttrValue(layoutNode, 'style:family'); if s = 'text' then begin s := GetAttrValue(layoutNode, 'style:name'); fontNode := layoutNode.FirstChild; - idx := ReadFont(fontNode); - FHeaderFooterFontList.AddObject(s, TObject(PtrInt(idx))); + ReadHeaderFooterFont(fontNode, fntName, fntSize, fntStyle, fntColor); + fnt := TXMLHeaderFooterFont.Create(fntName, fntSize, fntStyle, fntColor); + fnt.StyleName := s; + FHeaderFooterFontList.Add(fnt); end; end else if nodeName = 'style:page-layout' then begin + // Read page layout parameters data := TPageLayoutData.Create; InitPageLayout(data.PageLayout); data.Name := GetAttrValue(layoutNode, 'style:name'); @@ -1044,122 +1281,21 @@ end; function TsSpreadOpenDocReader.ReadHeaderFooterText(ANode: TDOMNode): String; var - regionNode, textNode, spanNode: TDOMNode; - nodeName: String; - s: String; - currFont: TsFont; - fnt: TsFont; - idx: Integer; + parser: TsSpreadOpenDocHeaderFooterParser; + defFnt: TsHeaderFooterFont; begin - Result := ''; - currFont := FWorkbook.GetDefaultFont; - 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': - if textNode.NodeValue = '&' - then Result := Result + '&&' - else 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 - // Extract font parameters used - s := GetAttrValue(textNode, 'text:style-name'); - if s <> '' then - begin - idx := FHeaderFooterFontList.IndexOf(s); - if idx > -1 then - begin - fnt := FWorkbook.GetFont(PtrInt(FHeaderFooterFontList.Objects[idx])); - if fnt <> nil then - begin - if (fnt.FontName <> currFont.FontName) then - begin - if (fnt.Size <> currFont.Size) then - Result := Format('%s&"%s,%.1f"', [Result, fnt.FontName, fnt.Size]) - else - Result := Format('%s&"%s"', [Result, fnt.FontName]); - end; - if ((fssBold in fnt.Style) and not (fssBold in currFont.Style)) or - (not (fssBold in fnt.Style) and (fssBold in currFont.Style)) - then - Result := Result + '&B'; - if ((fssItalic in fnt.Style) and not (fssItalic in currFont.Style)) or - (not (fssItalic in fnt.Style) and (fssItalic in currFont.Style)) - then - Result := Result + '&I'; - if ((fssUnderline in fnt.Style) and not (fssUnderline in currFont.Style)) or - (not (fssUnderline in fnt.Style) and (fssUnderline in currFont.Style)) - then - Result := Result + '&U'; - if ((fssStrikeout in fnt.Style) and not (fssStrikeout in currFont.Style)) or - (not (fssStrikeout in fnt.Style) and (fssStrikeout in currFont.Style)) - then - Result := Result + '&S'; - { Currently no support for &E double strikeout, &H shadowed text, &O outlined text, &X superscript, &Y subscript } - currFont := fnt; - end; - end; - end; - - // Extract text - 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 := ReadHeaderFooterText(regionNode); - Result := Result + '&L' + s; - end else - if (nodeName = 'style:region-center') then - begin - s := ReadHeaderFooterText(regionNode); - Result := Result + '&C' + s; - end else - if (nodeName = 'style:region-right') then - begin - s := ReadHeaderFooterText(regionNode); - Result := Result + '&R' + s; - end; - regionNode := regionNode.NextSibling; + defFnt := TsHeaderFooterFont.Create(Workbook.GetDefaultFont); + parser := TsSpreadOpenDocHeaderFooterParser.Create(ANode.FirstChild, + FHeaderFooterFontList, defFnt); + try + Result := parser.BuildHeaderFooter; + finally + parser.Free; + defFnt.Free; end; end; +{ Reads the master styles nodes which contain the header/footer texts } procedure TsSpreadOpenDocReader.ReadMasterStyles(AStylesNode: TDOMNode); var masternode, stylenode, regionnode: TDOMNode; @@ -1180,6 +1316,7 @@ begin if nodeName = 'style:master-page' then begin s := GetAttrvalue(masterNode, 'style:page-layout-name'); + { Find the page layout data belonging to the current node } pageLayout := nil; for j:=0 to FPageLayoutList.Count-1 do if TPageLayoutData(FPageLayoutList[j]).Name = s then @@ -1475,19 +1612,19 @@ var NullDateSetting: string; begin // Default datemode for ODF: - NullDateSetting:='1899-12-30'; - CalcSettingsNode:=SpreadsheetNode.FindNode('table:calculation-settings'); + NullDateSetting := '1899-12-30'; + CalcSettingsNode := SpreadsheetNode.FindNode('table:calculation-settings'); if Assigned(CalcSettingsNode) then begin - NullDateNode:=CalcSettingsNode.FindNode('table:null-date'); + NullDateNode := CalcSettingsNode.FindNode('table:null-date'); if Assigned(NullDateNode) then - NullDateSetting:=GetAttrValue(NullDateNode,'table:date-value'); + NullDateSetting := GetAttrValue(NullDateNode,'table:date-value'); end; - if NullDateSetting='1899-12-30' then + if NullDateSetting = '1899-12-30' then FDateMode := dm1899 - else if NullDateSetting='1900-01-01' then + else if NullDateSetting = '1900-01-01' then FDateMode := dm1900 - else if NullDateSetting='1904-01-01' then + else if NullDateSetting = '1904-01-01' then FDateMode := dm1904 else raise Exception.CreateFmt('Spreadsheet file corrupt: cannot handle null-date format %s', [NullDateSetting]); @@ -1539,7 +1676,7 @@ begin if s <> '' then fntColor := FWorkbook.AddColorToPalette(HTMLColorStrToColor(s)) else - fntColor := FWorkbook.GetFont(0).Color; + fntColor := FWorkbook.GetDefaultFont.Color; if APreferredIndex = 0 then begin @@ -1790,6 +1927,60 @@ begin 'Method not implemented. Use "ReadFromFile" instead.'); end; +procedure TsSpreadOpenDocReader.ReadHeaderFooterFont(ANode: TDOMNode; + var AFontName: String; var AFontSize: Double; + var AFontStyle: TsHeaderFooterFontStyles; var AFontColor: TsColorValue); +var + s: String; +begin + if ANode = nil then + exit; + + AFontName := GetAttrValue(ANode, 'style:font-name'); + + s := GetAttrValue(ANode, 'fo:font-size'); + if s <> '' then + AFontSize := HTMLLengthStrToPts(s); + + AFontStyle := []; + + if GetAttrValue(ANode, 'fo:font-style') = 'italic' then + Include(AFontStyle, hfsItalic); + + if GetAttrValue(ANode, 'fo:font-weight') = 'bold' then + Include(AFontStyle, hfsBold); + + s := GetAttrValue(ANode, 'style:text-underline-style'); + if not ((s = '') or (s = 'none')) then + begin + if GetAttrValue(ANode, 'style:text-underline-type') = 'double' then + Include(AFontStyle, hfsDblUnderline) + else + Include(AFontStyle, hfsUnderline); + end; + + s := GetAttrValue(ANode, 'style:text-strike-through-style'); + if not ((s = '') or (s = 'none')) then + Include(AFontStyle, hfsStrikeout); + + if GetAttrValue(ANode, 'style:text-outline') = 'true' then + Include(AFontStyle, hfsOutline); + + s := GetAttrValue(ANode, 'style:text-shadow'); + if not ((s = '') or (s = 'none')) then + Include(AFontStyle, hfsShadow); + + s := GetAttrValue(ANode, 'style:text-position'); + if pos('sub', s) = 1 then + Include(AFontStyle, hfsSubscript) + else if pos('super', s) = 1 then + Include(AFontStyle, hfsSuperscript); + + s := GetAttrValue(ANode, 'fo:color'); + if s <> '' then + AFontColor := HTMLColorStrToColor(s); +end; + procedure TsSpreadOpenDocReader.ReadLabel(ARow, ACol: Cardinal; ACellNode: TDOMNode); var @@ -3166,124 +3357,10 @@ begin end; procedure TsSpreadOpenDocWriter.WriteAutomaticStyles(AStream: TStream); - - function PageLayoutAsXMLString(AStyleName: String; - const APageLayout: TsPageLayout): String; - const - ORIENTATIONS: Array[TsPageOrientation] of string = ('portrait', 'landscape'); - PAGEORDERS: Array[boolean] of string = ('ttb', 'ltr'); - var - pageLayoutStr: String; - headerStyleStr: String; - footerStyleStr: String; - options: String; - i: Integer; - hasHeader, hasFooter: Boolean; - topmargin, bottommargin: Double; - h: Double; - begin - hasHeader := false; - hasFooter := false; - for i:=0 to High(APageLayout.Headers) do - begin - if APageLayout.Headers[i] <> '' then hasHeader := true; - if APageLayout.Footers[i] <> '' then hasFooter := true; - end; - - if hasHeader then - topMargin := APageLayout.HeaderMargin - else - topMargin := APageLayout.TopMargin; - - if hasFooter then - bottomMargin := APageLayout.FooterMargin - else - bottomMargin := APageLayout.BottomMargin; - - pageLayoutStr := Format( - 'fo:page-width="%.2fmm" fo:page-height="%.2fmm" '+ - 'fo:margin-top="%.2fmm" fo:margin-bottom="%.2fmm" '+ - 'fo:margin-left="%.2fmm" fo:margin-right="%.2fmm" ', [ - APageLayout.PageWidth, APageLayout.PageHeight, - topmargin, bottommargin, - APageLayout.LeftMargin, APageLayout.RightMargin - ], FPointSeparatorSettings); - - if APageLayout.Orientation = spoLandscape then - pageLayoutStr := pageLayoutStr + 'style:print-orientation="landscape" '; - - if poPrintPagesByRows in APageLayout.Options then - pageLayoutStr := pageLayoutStr + 'style:print-page-order="ltr" '; - - if poUseStartPageNumber in APageLayout.Options then - pageLayoutStr := pageLayoutStr + 'style:first-page-number="' + IntToStr(APageLayout.StartPageNumber) +'" ' - else - pageLayoutStr := pageLayoutStr + 'style:first-page-number="continue" '; - - if APageLayout.Options * [poHorCentered, poVertCentered] = [poHorCentered, poVertCentered] then - pageLayoutStr := pageLayoutStr + 'style:table-centering="both" ' - else if poHorCentered in APageLayout.Options then - pageLayoutStr := pageLayoutStr + 'style:table-centering="horizontal" ' - else if poVertCentered in APageLayout.Options then - pageLayoutStr := pageLayoutStr + 'style:table-centering="vertical" '; - - if poFitPages in APageLayout.Options then - begin - if APageLayout.FitWidthToPages > 0 then - pageLayoutStr := pageLayoutStr + 'style:scale-to-X="' + IntToStr(APageLayout.FitWidthToPages) + '" '; - if APageLayout.FitHeightToPages > 0 then - pageLayoutStr := pageLayoutStr + 'style:scale-to-Y="' + IntToStr(APageLayout.FitHeightToPages) + '" '; - end else - pageLayoutStr := pageLayoutStr + 'style:scale-to="' + IntToStr(APageLayout.ScalingFactor) + '%" '; - - options := 'charts drawings objects zero-values'; - if poPrintGridLines in APageLayout.Options then - options := options + ' grid'; - if poPrintHeaders in APageLayout.Options then - options := options + ' headers'; - if poPrintCellComments in APageLayout.Options then - options := options + ' annotations'; - - pageLayoutStr := pageLayoutStr + 'style:print="' + options + '" '; - - h := PtsToMM(FWorkbook.GetDefaultFontSize); - - if hasHeader then - headerStyleStr := Format( - ''+ - ''+ - '', [ - APageLayout.TopMargin - APageLayout.HeaderMargin, - APageLayout.TopMargin - APageLayout.HeaderMargin - h], FPointSeparatorSettings) - else - headerStyleStr := ''; - - if hasFooter then - footerStyleStr := Format( - ''+ - ''+ - '', [ - APageLayout.BottomMargin - APageLayout.FooterMargin, - APageLayout.BottomMargin - APageLayout.FooterMargin - h], FPointSeparatorSettings) - else - footerStyleStr := ''; - - Result := '' + - ''+ - headerStyleStr + - footerStyleStr + - ''; - end; - var i: Integer; sheet: TsWorksheet; + fnt: TXMLHeaderFooterFont; begin AppendToStream(AStream, @@ -3321,7 +3398,19 @@ begin for i:=0 to FWorkbook.GetWorksheetCount-1 do begin sheet := FWorkbook.GetWorksheetByIndex(i); AppendToStream(AStream, - PageLayoutAsXMLString('Mpm' + IntToStr(3+i), sheet.PageLayout)); + WritePageLayoutAsXMLString('Mpm' + IntToStr(3+i), sheet.PageLayout)); + end; + + for i:=0 to FHeaderFooterFontList.Count-1 do + begin + fnt := TXMLHeaderFooterFont(FHeaderFooterFontList[i]); + fnt.StyleName := 'MT' + IntToStr(i+1); + AppendToStream(AStream, Format( + '' + + '' + + '', [ + fnt.StyleName, WriteHeaderFooterFontXMLAsString(fnt) + ])); end; AppendToStream(AStream, @@ -3734,19 +3823,59 @@ procedure TsSpreadOpenDocWriter.WriteFontNames(AStream: TStream); var L: TStringList; fnt: TsFont; + hfFnt: TXMLHeaderFooterFont; i: Integer; + defFnt: TsHeaderFooterFont; + sheet: TsWorksheet; + + { Add the fonts used in the specified header/footer line to the + HeaderFooterFontList. This is done while the HeaderFooterParser is created. } + procedure AddFontsOfHeaderFooter(AText: String; ADefaultFont: TsHeaderFooterFont); + begin + TsSpreadOpenDocHeaderFooterParser.Create(AText, FHeaderFooterFontList, ADefaultFont).Free; + end; + begin + // At first take care of the headers and footers, their fonts are not stored + // in the Workbook's FontList. Here were store the fonts in the + // FHeaderFooterFontList of the reader which is needed also when writing + // headers and footers. + defFnt := TsHeaderFooterFont.Create(Workbook.GetDefaultFont); + try + for i:=0 to Workbook.GetWorksheetCount-1 do + begin + sheet := Workbook.GetWorksheetByIndex(i); + AddFontsOfHeaderFooter(sheet.pageLayout.Headers[1], defFnt); + AddFontsOfHeaderFooter(sheet.PageLayout.Headers[2], defFnt); + AddFontsOfHeaderFooter(sheet.PageLayout.Footers[1], defFnt); + AddFontsOfHeaderFooter(sheet.PageLayout.Footers[2], defFnt); + end; + finally + defFnt.Free; + end; + + // Begin writing to stream AppendToStream(AStream, ''); + // Collect all unique font names in a string list L := TStringList.Create; try + // First collect the font names from the workbook's FontList for i:=0 to Workbook.GetFontCount-1 do begin fnt := Workbook.GetFont(i); if (fnt <> nil) and (L.IndexOf(fnt.FontName) = -1) then L.Add(fnt.FontName); end; + // Then collect the header/footer font names from the HeaderFooterFontList + for i:=0 to FHeaderFooterFontList.Count-1 do + begin + hfFnt := TXMLHeaderFooterFont(FHeaderFooterFontList[i]); + if (hfFnt <> nil) and (L.Indexof(hfFnt.FontName) = -1) then + L.Add(hfFnt.FontName); + end; + // Done. Now write all font names as xml nodes to the stream for i:=0 to L.Count-1 do AppendToStream(AStream, Format( '', [L[i], L[i]])); @@ -3754,82 +3883,34 @@ begin L.Free; end; + // Write end node AppendToStream(AStream, ''); end; procedure TsSpreadOpenDocWriter.WriteMasterStyles(AStream: TStream); +var + defFnt: TsHeaderFooterFont; + i: Integer; + sheet: TsWorksheet; function HeaderFooterAsString(AIndex: Integer; AIsHeader: Boolean; const APageLayout: TsPageLayout): String; var - p: Integer; + parser: TsSpreadOpenDocHeaderFooterParser; str: String; - region: Integer; // -1=default, 0=left, 1=center, 2=right begin - Result := ''; if AIsHeader then str := APageLayout.Headers[AIndex] else str := APageLayout.Footers[AIndex]; if str = '' then exit; - p := 1; - region := -1; - while p <= Length(str) do begin - if (str[p] = '&') and (p < Length(str)) then - begin - inc(p); - case str[p] of - 'L': begin - case region of - 0: Result := Result + ''; - 1: Result := Result + ''; - 2: Result := Result + ''; - end; - Result := Result + ''; - region := 0; - end; - 'C': begin - case region of - 0: Result := Result + ''; - 1: Result := Result + ''; - 2: Result := Result + ''; - end; - Result := Result + ''; - region := 1; - end; - 'R': begin - case region of - 0: Result := Result + ''; - 1: Result := Result + ''; - 2: Result := Result + ''; - end; - Result := Result + ''; - region := 2; - end; - 'A': Result := Result + '???'; - 'D': Result := Result + Format( - '%s', - [FormatDateTime('yyyy"-"mm"-"dd', date()), DateToStr(date())]); - 'F': Result := Result + '???'; - 'P': Result := Result + '1'; - 'N': Result := Result + '1'; - 'T': Result := Result + Format( - '%s', [FormatDateTime('hh:nn:ss', time())]); - 'Z': Result := Result + '???'; - '&': Result := Result + '&&'; - end; - end - else - Result := Result + str[p]; - inc(p); - end; // while - - case region of - -1: Result := '' + Result + ''; - 0: Result := Result + ''; - 1: Result := Result + ''; - 2: Result := Result + ''; + parser := TsSpreadOpenDocHeaderFooterParser.Create(str, FHeaderFooterFontList, + defFnt); + try + Result := parser.BuildHeaderFooterAsXMLString; + finally + parser.Free; end; end; @@ -3869,10 +3950,9 @@ procedure TsSpreadOpenDocWriter.WriteMasterStyles(AStream: TStream); Result := Result + ''; end; -var - i: Integer; - sheet: TsWorksheet; begin + defFnt := TsHeaderFooterFont.Create(Workbook.GetDefaultFont); + AppendToStream(AStream, ''); @@ -3893,6 +3973,8 @@ begin AppendToStream(AStream, ''); + + defFnt.Free; end; procedure TsSpreadOpenDocWriter.WriteNumFormats(AStream: TStream); @@ -3917,18 +3999,6 @@ begin finally parser.Free; end; - { - fmtItem := FNumFormatList.Items[i]; - parser := TsSpreadOpenDocNumFormatParser.Create(Workbook, fmtItem.FormatString, - fmtItem.NumFormat); - try - numFmtXML := parser.BuildXMLAsString(fmtItem.Name); - if numFmtXML <> '' then - AppendToStream(AStream, numFmtXML); - finally - parser.Free; - end; - } end; end; @@ -4072,7 +4142,6 @@ begin '', [colsRepeatedStr])); end else WriteCellToStream(AStream, cell); -// WriteCellCallback(cell, AStream); inc(c, colsRepeated); end; @@ -4126,6 +4195,7 @@ begin FColumnStyleList := TFPList.Create; FRowStyleList := TFPList.Create; + FHeaderFooterFontList := TObjectList.Create; FPointSeparatorSettings := SysUtils.DefaultFormatSettings; FPointSeparatorSettings.DecimalSeparator:='.'; @@ -4146,6 +4216,8 @@ begin for j:=FRowStyleList.Count-1 downto 0 do TObject(FRowStyleList[j]).Free; FRowStyleList.Free; + FHeaderFooterFontList.Free; + inherited Destroy; end; @@ -4473,7 +4545,7 @@ function TsSpreadOpenDocWriter.WriteDefaultFontXMLAsString: String; var fnt: TsFont; begin - fnt := Workbook.GetFont(0); + fnt := Workbook.GetDefaultFont; Result := Format( '', [fnt.FontName, fnt.Size], FPointSeparatorSettings @@ -4489,45 +4561,91 @@ begin // ?? end; -function TsSpreadOpenDocWriter.WriteFontStyleXMLAsString( - const AFormat: TsCellFormat): String; +function TsSpreadOpenDocWriter.WriteFontStyleXMLAsString(AFont: TsFont): String; var - fnt: TsFont; defFnt: TsFont; begin Result := ''; - if not (uffFont in AFormat.UsedFormattingFields) then - exit; + defFnt := Workbook.GetDefaultFont; + if AFont = nil then AFont := defFnt; - fnt := Workbook.GetFont(AFormat.FontIndex); - defFnt := Workbook.GetDefaultfont; - if fnt = nil then - fnt := defFnt; + if AFont.FontName <> defFnt.FontName then + Result := Result + Format('style:font-name="%s" ', [AFont.FontName]); - if fnt.FontName <> defFnt.FontName then - Result := Result + Format('style:font-name="%s" ', [fnt.FontName]); - - if fnt.Size <> defFnt.Size then + if AFont.Size <> defFnt.Size then Result := Result + Format('fo:font-size="%.1fpt" style:font-size-asian="%.1fpt" style:font-size-complex="%.1fpt" ', - [fnt.Size, fnt.Size, fnt.Size], FPointSeparatorSettings); + [AFont.Size, AFont.Size, AFont.Size], FPointSeparatorSettings); - if fssBold in fnt.Style then + if fssBold in AFont.Style then Result := Result + 'fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold" '; - if fssItalic in fnt.Style then + if fssItalic in AFont.Style then Result := Result + 'fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" '; - if fssUnderline in fnt.Style then + if fssUnderline in AFont.Style then Result := Result + 'style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" '; - if fssStrikeout in fnt.Style then + if fssStrikeout in AFont.Style then Result := Result + 'style:text-line-through-style="solid" '; - if fnt.Color <> defFnt.Color then - Result := Result + Format('fo:color="%s" ', [Workbook.GetPaletteColorAsHTMLStr(fnt.Color)]); + if AFont.Color <> defFnt.Color then + Result := Result + Format('fo:color="%s" ', [Workbook.GetPaletteColorAsHTMLStr(AFont.Color)]); end; +function TsSpreadOpenDocWriter.WriteFontStyleXMLAsString( + const AFormat: TsCellFormat): String; +begin + Result := ''; + if (uffFont in AFormat.UsedFormattingFields) then + Result := WriteFontStyleXMLAsString(Workbook.GetFont(AFormat.FontIndex)); +end; + +function TsSpreadOpenDocWriter.WriteHeaderFooterFontXMLAsString( + AFont: TsHeaderFooterFont): String; +begin + Result := Format('style:font-name="%s" fo:font-size="%dpt" ', [ + AFont.FontName, round(AFont.Size) + ]); + + if hfsBold in AFont.Style then + Result := Result + 'fo:font-weight="bold" '; + + if hfsItalic in AFont.Style then + Result := Result + 'fo:font-style="italic" '; + + if hfsUnderline in AFont.Style then + Result := Result + 'style:text-underline-style="solid" '+ + 'style:text-underline-width="auto" '+ + 'style:text-underline-color="font-color" '; + + if hfsDblUnderline in AFont.Style then + Result := Result + 'style:text-underline-style="solid" '+ + 'style:text-underline-type="double" ' + + 'style:text-underline-width="auto" '+ + 'style:text-underline-color="font-color" '; + + if hfsStrikeout in AFont.Style then + Result := Result + 'style:text-line-through-style="solid" '; + + if hfsOutline in AFont.Style then + Result := Result + 'style:text-outline="true" '; + + if hfsShadow in AFont.Style then + Result := Result + 'style:text-shadow="1pt 1pt" ' + + 'style:text-outline="none" '; + + if hfsSubscript in AFont.Style then + Result := Result + 'style:text-position="sub 58%" '; + + if hfsSuperscript in AFont.Style then + Result := Result + 'style:text-position="super 58%" '; + + if AFont.Color <> 0 then + Result := Result + Format('fo:color="%s" ', [ColorToHTMLColorStr(AFont.Color)]); +end; + + {@@ ---------------------------------------------------------------------------- Creates an XML string for inclusion of the horizontal alignment into the written file from the horizontal alignment setting in the format cell. @@ -4546,6 +4664,120 @@ begin end; end; +function TsSpreadOpenDocWriter.WritePageLayoutAsXMLString(AStyleName: String; + const APageLayout: TsPageLayout): String; +const + ORIENTATIONS: Array[TsPageOrientation] of string = ('portrait', 'landscape'); + PAGEORDERS: Array[boolean] of string = ('ttb', 'ltr'); +var + pageLayoutStr: String; + headerStyleStr: String; + footerStyleStr: String; + options: String; + i: Integer; + hasHeader, hasFooter: Boolean; + topmargin, bottommargin: Double; + h: Double; +begin + hasHeader := false; + hasFooter := false; + for i:=0 to High(APageLayout.Headers) do + begin + if APageLayout.Headers[i] <> '' then hasHeader := true; + if APageLayout.Footers[i] <> '' then hasFooter := true; + end; + + if hasHeader then + topMargin := APageLayout.HeaderMargin + else + topMargin := APageLayout.TopMargin; + + if hasFooter then + bottomMargin := APageLayout.FooterMargin + else + bottomMargin := APageLayout.BottomMargin; + + pageLayoutStr := Format( + 'fo:page-width="%.2fmm" fo:page-height="%.2fmm" '+ + 'fo:margin-top="%.2fmm" fo:margin-bottom="%.2fmm" '+ + 'fo:margin-left="%.2fmm" fo:margin-right="%.2fmm" ', [ + APageLayout.PageWidth, APageLayout.PageHeight, + topmargin, bottommargin, + APageLayout.LeftMargin, APageLayout.RightMargin + ], FPointSeparatorSettings); + + if APageLayout.Orientation = spoLandscape then + pageLayoutStr := pageLayoutStr + 'style:print-orientation="landscape" '; + + if poPrintPagesByRows in APageLayout.Options then + pageLayoutStr := pageLayoutStr + 'style:print-page-order="ltr" '; + + if poUseStartPageNumber in APageLayout.Options then + pageLayoutStr := pageLayoutStr + 'style:first-page-number="' + IntToStr(APageLayout.StartPageNumber) +'" ' + else + pageLayoutStr := pageLayoutStr + 'style:first-page-number="continue" '; + + if APageLayout.Options * [poHorCentered, poVertCentered] = [poHorCentered, poVertCentered] then + pageLayoutStr := pageLayoutStr + 'style:table-centering="both" ' + else if poHorCentered in APageLayout.Options then + pageLayoutStr := pageLayoutStr + 'style:table-centering="horizontal" ' + else if poVertCentered in APageLayout.Options then + pageLayoutStr := pageLayoutStr + 'style:table-centering="vertical" '; + + if poFitPages in APageLayout.Options then + begin + if APageLayout.FitWidthToPages > 0 then + pageLayoutStr := pageLayoutStr + 'style:scale-to-X="' + IntToStr(APageLayout.FitWidthToPages) + '" '; + if APageLayout.FitHeightToPages > 0 then + pageLayoutStr := pageLayoutStr + 'style:scale-to-Y="' + IntToStr(APageLayout.FitHeightToPages) + '" '; + end else + pageLayoutStr := pageLayoutStr + 'style:scale-to="' + IntToStr(APageLayout.ScalingFactor) + '%" '; + + options := 'charts drawings objects zero-values'; + if poPrintGridLines in APageLayout.Options then + options := options + ' grid'; + if poPrintHeaders in APageLayout.Options then + options := options + ' headers'; + if poPrintCellComments in APageLayout.Options then + options := options + ' annotations'; + + pageLayoutStr := pageLayoutStr + 'style:print="' + options + '" '; + + h := PtsToMM(FWorkbook.GetDefaultFontSize); + + if hasHeader then + headerStyleStr := Format( + ''+ + ''+ + '', [ + APageLayout.TopMargin - APageLayout.HeaderMargin, + APageLayout.TopMargin - APageLayout.HeaderMargin - h], FPointSeparatorSettings) + else + headerStyleStr := ''; + + if hasFooter then + footerStyleStr := Format( + ''+ + ''+ + '', [ + APageLayout.BottomMargin - APageLayout.FooterMargin, + APageLayout.BottomMargin - APageLayout.FooterMargin - h], FPointSeparatorSettings) + else + footerStyleStr := ''; + + Result := '' + + ''+ + headerStyleStr + + footerStyleStr + + ''; +end; + procedure TsSpreadOpenDocWriter.WriteTableSettings(AStream: TStream); var i: Integer; @@ -4625,6 +4857,7 @@ begin end; end; + {@@ ---------------------------------------------------------------------------- Creates an XML string for inclusion of the textrotation style option into the written file from the textrotation setting in the format cell. @@ -4776,7 +5009,6 @@ begin end else lCell.ContentType := cctEmpty; WriteCellToStream(AStream, @lCell); -// WriteCellCallback(@lCell, AStream); end; inc(c, colsRepeated); end; @@ -4852,14 +5084,7 @@ begin parser := TsSpreadsheetParser.Create(FWorksheet); try parser.Dialect := fdOpenDocument; - { - if ACell^.SharedFormulaBase <> nil then - begin - parser.ActiveCell := ACell; - parser.Expression := ACell^.SharedFormulaBase^.FormulaValue; - end else - } - parser.Expression := ACell^.FormulaValue; + parser.Expression := ACell^.FormulaValue; formula := Parser.LocalizedExpression[FPointSeparatorSettings]; finally parser.Free; diff --git a/components/fpspreadsheet/fpsxmlcommon.pas b/components/fpspreadsheet/fpsxmlcommon.pas index 7acabd6da..b40e1ba6a 100644 --- a/components/fpspreadsheet/fpsxmlcommon.pas +++ b/components/fpspreadsheet/fpsxmlcommon.pas @@ -15,11 +15,11 @@ uses type TsSpreadXMLReader = class(TsCustomSpreadReader) protected - function GetAttrValue(ANode : TDOMNode; AAttrName : string) : string; - function GetNodeValue(ANode: TDOMNode): String; procedure ReadXMLFile(out ADoc: TXMLDocument; AFileName: String); end; +function GetAttrValue(ANode : TDOMNode; AAttrName : string) : string; +function GetNodeValue(ANode: TDOMNode): String; procedure UnzipFile(AZipFileName, AZippedFile, ADestFolder: String); @@ -35,7 +35,7 @@ uses { Gets value for the specified attribute. Returns empty string if attribute not found. } -function TsSpreadXMLReader.GetAttrValue(ANode : TDOMNode; AAttrName : string) : string; +function {TsSpreadXMLReader.}GetAttrValue(ANode : TDOMNode; AAttrName : string) : string; var i: LongWord; Found: Boolean; @@ -58,7 +58,7 @@ end; { Returns the text value of a node. Normally it would be sufficient to call "ANode.NodeValue", but since the DOMParser needs to preserve white space (for the spaces in date/time formats), we have to go more into detail. } -function TsSpreadXMLReader.GetNodeValue(ANode: TDOMNode): String; +function {TsSpreadXMLReader.}GetNodeValue(ANode: TDOMNode): String; var child: TDOMNode; begin diff --git a/components/fpspreadsheet/laz_fpspreadsheet.lpk b/components/fpspreadsheet/laz_fpspreadsheet.lpk index b6089be57..695f264a8 100644 --- a/components/fpspreadsheet/laz_fpspreadsheet.lpk +++ b/components/fpspreadsheet/laz_fpspreadsheet.lpk @@ -28,7 +28,7 @@ This package is all you need if you don't want graphical components (like grids and charts)."/> - + @@ -161,6 +161,10 @@ This package is all you need if you don't want graphical components (like grids + + + + diff --git a/components/fpspreadsheet/laz_fpspreadsheet.pas b/components/fpspreadsheet/laz_fpspreadsheet.pas index 5cfb45055..9bfbb97f5 100644 --- a/components/fpspreadsheet/laz_fpspreadsheet.pas +++ b/components/fpspreadsheet/laz_fpspreadsheet.pas @@ -13,7 +13,7 @@ uses uvirtuallayer_ole_helpers, uvirtuallayer_ole_types, uvirtuallayer_stream, fpolebasic, wikitable, fpsNumFormatParser, fpsfunc, fpsRPN, fpsStrings, fpscsv, fpsCsvDocument, fpspatches, fpsTypes, xlsEscher, fpsReaderWriter, - fpsNumFormat, fpsclasses; + fpsNumFormat, fpsclasses, fpsHeaderFooterParser; implementation diff --git a/components/fpspreadsheet/tests/datetests.pas b/components/fpspreadsheet/tests/datetests.pas index 0013ad659..5c0794d41 100644 --- a/components/fpspreadsheet/tests/datetests.pas +++ b/components/fpspreadsheet/tests/datetests.pas @@ -480,7 +480,7 @@ begin TestWorkbook := TsWorkbook.Create; case UpperCase(ExtractFileExt(FileName)) of '.XLSX': TestWorkbook.ReadFromFile(FileName, sfOOXML); - '.ODS': TestWorkbook.ReadFromFile(FileName, sfOpenDocument); + '.ODS' : TestWorkbook.ReadFromFile(FileName, sfOpenDocument); // Excel XLS/BIFF else TestWorkbook.ReadFromFile(FileName, sfExcel8); end; diff --git a/components/fpspreadsheet/tests/pagelayouttests.pas b/components/fpspreadsheet/tests/pagelayouttests.pas index 947fcf793..7d5a3ae0a 100644 --- a/components/fpspreadsheet/tests/pagelayouttests.pas +++ b/components/fpspreadsheet/tests/pagelayouttests.pas @@ -50,6 +50,10 @@ type procedure TestWriteRead_BIFF2_HeaderFooterSymbols_2sheets; procedure TestWriteRead_BIFF2_HeaderFooterSymbols_3sheets; + procedure TestWriteRead_BIFF2_HeaderFooterFontSymbols_1sheet; + procedure TestWriteRead_BIFF2_HeaderFooterFontSymbols_2sheets; + procedure TestWriteRead_BIFF2_HeaderFooterFontSymbols_3sheets; + // no BIFF2 page orientation tests because this info is not readily available in the file @@ -99,6 +103,14 @@ type procedure TestWriteRead_BIFF5_HeaderFooterSymbols_2sheets; procedure TestWriteRead_BIFF5_HeaderFooterSymbols_3sheets; + procedure TestWriteRead_BIFF5_HeaderFooterFontSymbols_1sheet; + procedure TestWriteRead_BIFF5_HeaderFooterFontSymbols_2sheets; + procedure TestWriteRead_BIFF5_HeaderFooterFontSymbols_3sheets; + + procedure TestWriteRead_BIFF5_HeaderFooterFontColor_1sheet; + procedure TestWriteRead_BIFF5_HeaderFooterFontColor_2sheets; + procedure TestWriteRead_BIFF5_HeaderFooterFontColor_3sheets; + { BIFF8 page layout tests } procedure TestWriteRead_BIFF8_PageMargins_1sheet_0; procedure TestWriteRead_BIFF8_PageMargins_1sheet_1; @@ -145,6 +157,14 @@ type procedure TestWriteRead_BIFF8_HeaderFooterSymbols_2sheets; procedure TestWriteRead_BIFF8_HeaderFooterSymbols_3sheets; + procedure TestWriteRead_BIFF8_HeaderFooterFontSymbols_1sheet; + procedure TestWriteRead_BIFF8_HeaderFooterFontSymbols_2sheets; + procedure TestWriteRead_BIFF8_HeaderFooterFontSymbols_3sheets; + + procedure TestWriteRead_BIFF8_HeaderFooterFontColor_1sheet; + procedure TestWriteRead_BIFF8_HeaderFooterFontColor_2sheets; + procedure TestWriteRead_BIFF8_HeaderFooterFontColor_3sheets; + { OOXML page layout tests } procedure TestWriteRead_OOXML_PageMargins_1sheet_0; procedure TestWriteRead_OOXML_PageMargins_1sheet_1; @@ -191,6 +211,14 @@ type procedure TestWriteRead_OOXML_HeaderFooterSymbols_2sheets; procedure TestWriteRead_OOXML_HeaderFooterSymbols_3sheets; + procedure TestWriteRead_OOXML_HeaderFooterFontSymbols_1sheet; + procedure TestWriteRead_OOXML_HeaderFooterFontSymbols_2sheets; + procedure TestWriteRead_OOXML_HeaderFooterFontSymbols_3sheets; + + procedure TestWriteRead_OOXML_HeaderFooterFontColor_1sheet; + procedure TestWriteRead_OOXML_HeaderFooterFontColor_2sheets; + procedure TestWriteRead_OOXML_HeaderFooterFontColor_3sheets; + { OpenDocument page layout tests } procedure TestWriteRead_ODS_PageMargins_1sheet_0; procedure TestWriteRead_ODS_PageMargins_1sheet_1; @@ -237,6 +265,13 @@ type procedure TestWriteRead_ODS_HeaderFooterSymbols_2sheets; procedure TestWriteRead_ODS_HeaderFooterSymbols_3sheets; + procedure TestWriteRead_ODS_HeaderFooterFontSymbols_1sheet; + procedure TestWriteRead_ODS_HeaderFooterFontSymbols_2sheets; + procedure TestWriteRead_ODS_HeaderFooterFontSymbols_3sheets; + + procedure TestWriteRead_ODS_HeaderFooterFontColor_1sheet; + procedure TestWriteRead_ODS_HeaderFooterFontColor_2sheets; + procedure TestWriteRead_ODS_HeaderFooterFontColor_3sheets; end; implementation @@ -267,6 +302,8 @@ end; 3 ... header, footer } procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_PageMargins( AFormat: TsSpreadsheetFormat; ANumSheets, AHeaderFooterMode: Integer); +const + EPS = 1e-6; var MyWorksheet: TsWorksheet; MyWorkbook: TsWorkbook; @@ -324,16 +361,16 @@ begin fail('Error in test code. Failed to get worksheet by index'); actualPageLayout := MyWorksheet.PageLayout; - CheckEquals(sollPageLayout.TopMargin, actualPageLayout.TopMargin, 'Top margin mismatch, sheet "'+MyWorksheet.Name+'"'); - CheckEquals(sollPageLayout.BottomMargin, actualPageLayout.Bottommargin, 'Bottom margin mismatch, sheet "'+MyWorksheet.Name+'"'); - CheckEquals(sollPageLayout.LeftMargin, actualPageLayout.LeftMargin, 'Left margin mismatch, sheet "'+MyWorksheet.Name+'"'); - CheckEquals(sollPageLayout.RightMargin, actualPageLayout.RightMargin, 'Right margin mismatch, sheet "'+MyWorksheet.Name+'"'); + CheckEquals(sollPageLayout.TopMargin, actualPageLayout.TopMargin, EPS, 'Top margin mismatch, sheet "'+MyWorksheet.Name+'"'); + CheckEquals(sollPageLayout.BottomMargin, actualPageLayout.Bottommargin, EPS, 'Bottom margin mismatch, sheet "'+MyWorksheet.Name+'"'); + CheckEquals(sollPageLayout.LeftMargin, actualPageLayout.LeftMargin, EPS, 'Left margin mismatch, sheet "'+MyWorksheet.Name+'"'); + CheckEquals(sollPageLayout.RightMargin, actualPageLayout.RightMargin, EPS, 'Right margin mismatch, sheet "'+MyWorksheet.Name+'"'); if (AFormat <> sfExcel2) then // No header/footer margin in BIFF2 begin if AHeaderFooterMode in [1, 3] then - CheckEquals(sollPageLayout.HeaderMargin, actualPageLayout.HeaderMargin, 'Header margin mismatch, sheet "'+MyWorksheet.Name+'"'); + CheckEquals(sollPageLayout.HeaderMargin, actualPageLayout.HeaderMargin, EPS, 'Header margin mismatch, sheet "'+MyWorksheet.Name+'"'); if AHeaderFooterMode in [2, 3] then - CheckEquals(sollPageLayout.FooterMargin, actualPageLayout.FooterMargin, 'Footer margin mismatch, sheet "'+MyWorksheet.Name+'"'); + CheckEquals(sollPageLayout.FooterMargin, actualPageLayout.FooterMargin, EPS, 'Footer margin mismatch, sheet "'+MyWorksheet.Name+'"'); end; end; @@ -437,6 +474,38 @@ begin Footers[HEADER_FOOTER_INDEX_ALL] := '&LSheet "&A"&C100&&'; end; end; + 8: // Header/footer font symbol test + begin + Headers[HEADER_FOOTER_INDEX_ALL] := + '&LH&Y2&YO cm&X2&X'+ + '&C&"Times New Roman"&18This is big'+ + '&RThis is &Bbold&B,'+ LineEnding+'&Iitalic&I,'+LineEnding+ + '&Uunderlined&U,'+LineEnding+'&Edouble underlined&E,'+ + '&Sstriked-out&S,'+LineEnding+'&Ooutlined&O,'+LineEnding+ + '&Hshadow&S'; + Footers[HEADER_FOOTER_INDEX_ALL] := + '&L&"Arial"&8Arial small'+ + '&C&"Courier new"&32Courier big'+ + '&R&"Times New Roman"&10Times standard'; + case p of + 0: Footers[HEADER_FOOTER_INDEX_ALL] := ''; + 1: Headers[HEADER_FOOTER_INDEX_ALL] := ''; + end; + end; + 9: // Header/footer font color test + begin + Headers[HEADER_FOOTER_INDEX_ALL] := + '&L&KFF0000This is red'+ + '&C&K00FF00This is green'+ + '&R&K0000FFThis is blue'; + Footers[HEADER_FOOTER_INDEX_ALL] := + '<his is &"Times New Roman"&KFF0000red&K000000, &K00FF00green&K000000, &K0000FFblue&K000000.'; + case p of + 0: Footers[HEADER_FOOTER_INDEX_ALL] := ''; + 1: Headers[HEADER_FOOTER_INDEX_ALL] := ''; + end; + end; + end; end; end; @@ -508,7 +577,7 @@ begin CheckEquals(sollPageLayout[p].StartPageNumber, actualPageLayout.StartPageNumber, 'StartPageNumber value mismatch, sheet "' + MyWorksheet.Name + '"'); end; - 6, 7: // Header/footer tests + 6, 7, 8, 9: // Header/footer tests begin CheckEquals(sollPageLayout[p].Headers[1], actualPageLayout.Headers[1], 'Header value mismatch, sheet "' + MyWorksheet.Name + '"'); @@ -622,6 +691,22 @@ begin end; +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF2_HeaderFooterFontSymbols_1sheet; +begin + TestWriteRead_PageLayout(sfExcel2, 1, 8); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF2_HeaderFooterFontSymbols_2sheets; +begin + TestWriteRead_PageLayout(sfExcel2, 2, 8); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF2_HeaderFooterFontSymbols_3sheets; +begin + TestWriteRead_PageLayout(sfExcel2, 3, 8); +end; + + { Tests for BIFF5 file format } procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_PageMargins_1sheet_0; @@ -815,6 +900,38 @@ begin end; +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_HeaderFooterFontSymbols_1sheet; +begin + TestWriteRead_PageLayout(sfExcel5, 1, 8); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_HeaderFooterFontSymbols_2sheets; +begin + TestWriteRead_PageLayout(sfExcel5, 2, 8); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_HeaderFooterFontSymbols_3sheets; +begin + TestWriteRead_PageLayout(sfExcel5, 3, 8); +end; + + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_HeaderFooterFontColor_1sheet; +begin + TestWriteRead_PageLayout(sfExcel5, 1, 9); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_HeaderFooterFontColor_2sheets; +begin + TestWriteRead_PageLayout(sfExcel5, 2, 9); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF5_HeaderFooterFontColor_3sheets; +begin + TestWriteRead_PageLayout(sfExcel5, 3, 9); +end; + + { Tests for BIFF8 file format } procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_PageMargins_1sheet_0; @@ -1008,6 +1125,38 @@ begin end; +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_HeaderFooterFontSymbols_1sheet; +begin + TestWriteRead_PageLayout(sfExcel8, 1, 8); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_HeaderFooterFontSymbols_2sheets; +begin + TestWriteRead_PageLayout(sfExcel8, 2, 8); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_HeaderFooterFontSymbols_3sheets; +begin + TestWriteRead_PageLayout(sfExcel8, 3, 8); +end; + + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_HeaderFooterFontColor_1sheet; +begin + TestWriteRead_PageLayout(sfExcel8, 1, 9); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_HeaderFooterFontColor_2sheets; +begin + TestWriteRead_PageLayout(sfExcel8, 2, 9); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_BIFF8_HeaderFooterFontColor_3sheets; +begin + TestWriteRead_PageLayout(sfExcel8, 3, 9); +end; + + { Tests for OOXML file format } procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_OOXML_PageMargins_1sheet_0; @@ -1201,6 +1350,38 @@ begin end; +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_OOXML_HeaderFooterFontSymbols_1sheet; +begin + TestWriteRead_PageLayout(sfOOXML, 1, 8); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_OOXML_HeaderFooterFontSymbols_2sheets; +begin + TestWriteRead_PageLayout(sfOOXML, 2, 8); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_OOXML_HeaderFooterFontSymbols_3sheets; +begin + TestWriteRead_PageLayout(sfOOXML, 3, 8); +end; + + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_OOXML_HeaderFooterFontColor_1sheet; +begin + TestWriteRead_PageLayout(sfOOXML, 1, 9); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_OOXML_HeaderFooterFontColor_2sheets; +begin + TestWriteRead_PageLayout(sfOOXML, 2, 9); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_OOXML_HeaderFooterFontColor_3sheets; +begin + TestWriteRead_PageLayout(sfOOXML, 3, 9); +end; + + { Tests for Open Document file format } procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_ODS_PageMargins_1sheet_0; @@ -1394,6 +1575,38 @@ begin end; +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_ODS_HeaderFooterFontSymbols_1sheet; +begin + TestWriteRead_PageLayout(sfOpenDocument, 1, 8); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_ODS_HeaderFooterFontSymbols_2sheets; +begin + TestWriteRead_PageLayout(sfOpenDocument, 2, 8); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_ODS_HeaderFooterFontSymbols_3sheets; +begin + TestWriteRead_PageLayout(sfOpenDocument, 3, 8); +end; + + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_ODS_HeaderFooterFontColor_1sheet; +begin + TestWriteRead_PageLayout(sfOpenDocument, 1, 9); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_ODS_HeaderFooterFontColor_2sheets; +begin + TestWriteRead_PageLayout(sfOpenDocument, 2, 9); +end; + +procedure TSpreadWriteReadPageLayoutTests.TestWriteRead_ODS_HeaderFooterFontColor_3sheets; +begin + TestWriteRead_PageLayout(sfOpenDocument, 3, 9); +end; + + initialization RegisterTest(TSpreadWriteReadPageLayoutTests);