diff --git a/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi b/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi index 3c024608f..891ce1207 100644 --- a/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi +++ b/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi @@ -139,8 +139,8 @@ - - + + @@ -150,11 +150,10 @@ - - - + + @@ -218,10 +217,11 @@ + - - + + @@ -622,123 +622,123 @@ - + - + - + - - + + - - + + - + - - - - - - - - - - - - - - - - - - - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - + - + - - + + - - + + - - + + diff --git a/components/fpspreadsheet/fpsopendocument.pas b/components/fpspreadsheet/fpsopendocument.pas index 7df37f752..fbc0eaa91 100755 --- a/components/fpspreadsheet/fpsopendocument.pas +++ b/components/fpspreadsheet/fpsopendocument.pas @@ -58,8 +58,12 @@ type TsSpreadOpenDocReader = class(TsCustomSpreadReader) private + FStyleList: TFPList; FDateMode: TDateMode; - FWorksheet: TsWorksheet; + // Applies a style to a cell + procedure ApplyStyleToCell(ARow, ACol: Cardinal; AStyleName: String); + // Searches a style by its name in the StyleList + function FindStyleByName(AStyleName: String): integer; // Gets value for the specified attribute. Returns empty string if attribute // not found. function GetAttrValue(ANode : TDOMNode; AAttrName : string) : string; @@ -68,6 +72,7 @@ type protected procedure CreateNumFormatList; override; procedure ReadNumFormats(AStylesNode: TDOMNode); + procedure ReadStyles(AStylesNode: TDOMNode); { Record writing methods } procedure ReadFormula(ARow : Word; ACol : Word; ACellNode: TDOMNode); procedure ReadLabel(ARow : Word; ACol : Word; ACellNode: TDOMNode); @@ -75,6 +80,8 @@ type procedure ReadDate(ARow : Word; ACol : Word; ACellNode: TDOMNode); public { General reading methods } + constructor Create(AWorkbook: TsWorkbook); override; + destructor Destroy; override; procedure ReadFromFile(AFileName: string; AData: TsWorkbook); override; end; @@ -173,6 +180,22 @@ const DATEMODE_1900_BASE=2; //StarCalc compatibility, 1900-01-01 in FPC DateTime DATEMODE_1904_BASE=1462; //1/1/1904 in FPC TDateTime +type + { Style items relevant to FPSpreadsheet. Stored in the StylesList of the reader. } + TStyleData = class + public + Name: String; + FontIndex: Integer; + NumFormatIndex: Integer; + HorAlignment: TsHorAlignment; + VertAlignment: TsVertAlignment; + WordWrap: Boolean; + TextRotation: TsTextRotation; + Borders: TsCellBorders; + BorderStyles: TsCellBorderStyles; + BackgroundColor: TsColor; + end; + { TsSpreadOpenDocNumFormatList } @@ -181,8 +204,100 @@ begin // there are no built-in number formats which are silently assumed to exist. end; + { TsSpreadOpenDocReader } +constructor TsSpreadOpenDocReader.Create(AWorkbook: TsWorkbook); +begin + inherited Create(AWorkbook); + FStyleList := TFPList.Create; + // Initial base date in case it won't be read from file + FDateMode := dm1899; +end; + +destructor TsSpreadOpenDocReader.Destroy; +var + j: integer; +begin + for j := FStyleList.Count-1 downto 0 do TObject(FStyleList[j]).Free; + FStyleList.Free; + inherited Destroy; +end; + +{ Applies the style data referred to by the style name to the specified cell } +procedure TsSpreadOpenDocReader.ApplyStyleToCell(ARow, ACol: Cardinal; + AStyleName: String); +var + cell: PCell; + styleData: TStyleData; + styleIndex: Integer; + numFmtData: TsNumFormatData; +begin + cell := FWorksheet.GetCell(ARow, ACol); + if Assigned(cell) then begin + styleIndex := FindStyleByName(AStyleName); + if styleIndex = -1 then + exit; + + styleData := TStyleData(FStyleList[styleIndex]); + + // Font + { + if style.FontIndex = 1 then + Include(cell^.UsedFormattingFields, uffBold) + else + if XFData.FontIndex > 1 then + Include(cell^.UsedFormattingFields, uffFont); + cell^.FontIndex := styleData.FontIndex; + } + + // Alignment + cell^.HorAlignment := styleData.HorAlignment; + cell^.VertAlignment := styleData.VertAlignment; + + // Word wrap + if styleData.WordWrap then + Include(cell^.UsedFormattingFields, uffWordWrap) + else + Exclude(cell^.UsedFormattingFields, uffWordWrap); + + // Text rotation + if styleData.TextRotation > trHorizontal then + Include(cell^.UsedFormattingFields, uffTextRotation) + else + Exclude(cell^.UsedFormattingFields, uffTextRotation); + cell^.TextRotation := styledata.TextRotation; + + // Borders + cell^.BorderStyles := styleData.BorderStyles; + if styleData.Borders <> [] then begin + Include(cell^.UsedFormattingFields, uffBorder); + cell^.Border := styleData.Borders; + end else + Exclude(cell^.UsedFormattingFields, uffBorder); + + // Background color + if styleData.BackgroundColor <> 0 then begin + Include(cell^.UsedFormattingFields, uffBackgroundColor); + cell^.BackgroundColor := styleData.BackgroundColor; + end; + + // Number format + if styleData.NumFormatIndex > -1 then + if cell^.ContentType = cctNumber then begin + numFmtData := NumFormatList[styleData.NumFormatIndex]; + if numFmtData <> nil then begin + Include(cell^.UsedFormattingFields, uffNumberFormat); + cell^.NumberFormat := numFmtData.NumFormat; + cell^.NumberFormatStr := numFmtData.FormatString; + cell^.Decimals := numFmtData.Decimals; + cell^.CurrencySymbol := numFmtData.CurrencySymbol; + end; + end; + + end; +end; + { Creates the correct version of the number format list suited for ODS file formats. } procedure TsSpreadOpenDocReader.CreateNumFormatList; @@ -191,6 +306,15 @@ begin FNumFormatList := TsSpreadOpenDocNumFormatList.Create(Workbook); end; +function TsSpreadOpenDocReader.FindStyleByName(AStyleName: String): Integer; +begin + for Result:=0 to FStyleList.Count-1 do begin + if TStyleData(FStyleList[Result]).Name = AStyleName then + exit; + end; + Result := -1; +end; + function TsSpreadOpenDocReader.GetAttrValue(ANode : TDOMNode; AAttrName : string) : string; var i : integer; @@ -245,10 +369,10 @@ var RowsCount, ColsCount : integer; begin //unzip content.xml into AFileName path - FilePath:=GetTempDir(false); - UnZip:=TUnZipper.Create; - UnZip.OutputPath:=FilePath; - FileList:=TStringList.Create; + FilePath := GetTempDir(false); + UnZip := TUnZipper.Create; + UnZip.OutputPath := FilePath; + FileList := TStringList.Create; FileList.Add('styles.xml'); FileList.Add('content.xml'); try @@ -266,6 +390,7 @@ begin StylesNode := Doc.DocumentElement.FindNode('office:styles'); ReadNumFormats(StylesNode); + ReadStyles(StylesNode); //process the content.xml file ReadXMLFile(Doc, FilePath+'content.xml'); @@ -273,6 +398,7 @@ begin StylesNode := Doc.DocumentElement.FindNode('office:automatic-styles'); ReadNumFormats(StylesNode); + ReadStyles(StylesNode); BodyNode := Doc.DocumentElement.FindNode('office:body'); if not Assigned(BodyNode) then Exit; @@ -348,10 +474,11 @@ var FSettings: TFormatSettings; Value, Str: String; lNumber: Double; + styleName: String; begin FSettings := DefaultFormatSettings; FSettings.DecimalSeparator:='.'; - Value:=GetAttrValue(ACellNode,'office:value'); + Value := GetAttrValue(ACellNode,'office:value'); if UpperCase(Value)='1.#INF' then begin FWorkSheet.WriteNumber(Arow,ACol,1.0/0.0); @@ -363,6 +490,9 @@ begin lNumber := StrToFloat(Str,FSettings); FWorkSheet.WriteNumber(ARow,ACol,lNumber); end; + + styleName := GetAttrValue(ACellNode, 'table:style-name'); + ApplyStyleToCell(ARow, ACol, stylename); end; procedure TsSpreadOpenDocReader.ReadDate(ARow: Word; ACol : Word; ACellNode : TDOMNode); @@ -596,6 +726,82 @@ begin end; end; +procedure TsSpreadOpenDocReader.ReadStyles(AStylesNode: TDOMNode); +var + style: TStyleData; + styleNode: TDOMNode; + styleChildNode: TDOMNode; + family: String; + styleName: String; + styleIndex: Integer; + numFmtName: String; + numFmtIndex: Integer; + numFmtIndexDefault: Integer; + wrap: Boolean; + borders: TsCellBorders; + s: String; +begin + if not Assigned(AStylesNode) then + exit; + + numFmtIndexDefault := NumFormatList.FindByName('N0'); + + styleNode := AStylesNode.FirstChild; + while Assigned(styleNode) do begin + if styleNode.NodeName = 'style:style' then begin + family := GetAttrValue(styleNode, 'style:family'); + if family = 'table-cell' then begin + styleName := GetAttrValue(styleNode, 'style:name'); + numFmtName := GetAttrValue(styleNode, 'style:data-style-name'); + numFmtIndex := -1; + if numFmtName <> '' then numFmtIndex := NumFormatList.FindByName(numFmtName); + if numFmtIndex = -1 then numFmtIndex := numFmtIndexDefault; + + borders := []; + wrap := false; + + styleChildNode := styleNode.FirstChild; + while Assigned(styleChildNode) do begin + if styleChildNode.NodeName = 'style:table-cell-properties' then begin + // Borders + s := GetAttrValue(styleChildNode, 'fo:border-top'); + if (s <> '') and (s <> 'none') then Include(borders, cbNorth); + s := GetAttrValue(styleChildNode, 'fo:border-right'); + if (s <> '') and (s <> 'none') then Include(borders, cbEast); + s := GetAttrValue(styleChildNode, 'fo:border-bottom'); + if (s <> '') and (s <> 'none') then Include(borders, cbSouth); + s := GetAttrValue(styleChildNode, 'fo:border-left'); + if (s <> '') and (s <> 'none') then Include(borders, cbWest); + + // Text wrap + s := GetAttrValue(styleChildNode, 'fo:wrap-option'); + wrap := (s='wrap'); + end else + if styleChildNode.NodeName = 'style:paragraph-properties' then begin + // + end; + styleChildNode := styleChildNode.NextSibling; + end; + + style := TStyleData.Create; + style.Name := stylename; + style.FontIndex := 0; + style.NumFormatIndex := numFmtIndex; + style.HorAlignment := haDefault; + style.VertAlignment := vaDefault; + style.WordWrap := wrap; + style.TextRotation := trHorizontal; + style.Borders := borders; + style.BorderStyles := DEFAULT_BORDERSTYLES; + style.BackgroundColor := scNotDefined; + + styleIndex := FStyleList.Add(style); + end; + end; + styleNode := styleNode.NextSibling; + end; +end; + { TsSpreadOpenDocWriter } diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 5a4cb13ae..240da2051 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -615,8 +615,9 @@ type procedure Delete(AIndex: Integer); function Find(ANumFormat: TsNumberFormat; AFormatString: String; ADecimals: Byte; ACurrencySymbol: String): Integer; overload; - function Find(AFormatIndex: Integer): Integer; overload; function Find(AFormatString: String): Integer; overload; + function FindByIndex(AFormatIndex: Integer): Integer; + function FindByName(AFormatName: String): Integer; function FindFormatOf(AFormatCell: PCell): integer; virtual; function FormatStringForWriting(AIndex: Integer): String; virtual; procedure Sort; @@ -3007,11 +3008,11 @@ function TsCustomNumFormatList.AddFormat(AFormatName, AFormatString: String; ANumFormat: TsNumberFormat; ADecimals: Byte = 0; ACurrencySymbol: String = ''): Integer; begin - if AFormatString = '' then begin + if (AFormatString = '') and (ANumFormat <> nfGeneral) then begin Result := 0; exit; end; - Result := AddFormat(FNextFormatIndex, '', AFormatString, ANumFormat, + Result := AddFormat(FNextFormatIndex, AFormatName, AFormatString, ANumFormat, ADecimals, ACurrencySymbol); inc(FNextFormatIndex); end; @@ -3071,7 +3072,7 @@ var i: Integer; nf: TsNumberFormat; begin - i := Find(AFormatIndex); + i := FindByIndex(AFormatIndex); if i > 0 then begin lFormatData := Items[i]; fmt := lFormatData.FormatString; @@ -3131,7 +3132,7 @@ var decs: Byte; currsym: String; begin - if Find(AFormatIndex) > -1 then + if FindByIndex(AFormatIndex) > -1 then exit; // Analyze & convert the format string, extract infos for internal formatting @@ -3224,20 +3225,6 @@ begin Result := -1; end; -{ Finds the item with the given format index and returns its index in - the format list. } -function TsCustomNumFormatList.Find(AFormatIndex: Integer): integer; -var - item: TsNumFormatData; -begin - for Result := 0 to Count-1 do begin - item := Items[Result]; - if item.Index = AFormatIndex then - exit; - end; - Result := -1; -end; - { Finds the item with the given format string and returns its index in the format list. } function TsCustomNumFormatList.Find(AFormatString: String): integer; @@ -3254,6 +3241,36 @@ begin Result := -1; end; +{ Finds the item with the given format index and returns its index in + the format list. + Is used by BIFF file formats. } +function TsCustomNumFormatList.FindByIndex(AFormatIndex: Integer): integer; +var + item: TsNumFormatData; +begin + for Result := 0 to Count-1 do begin + item := Items[Result]; + if item.Index = AFormatIndex then + exit; + end; + Result := -1; +end; + +{ Finds the item with the given format name and returns its index in + the format list. + To be used by XML file formats. } +function TsCustomNumFormatList.FindByName(AFormatName: String): integer; +var + item: TsNumFormatData; +begin + for Result := 0 to Count-1 do begin + item := Items[Result]; + if item.Name = AFormatName then + exit; + end; + Result := -1; +end; + { Determines whether the format attributed to the given cell is already contained in the list and returns its list index. } function TsCustomNumFormatList.FindFormatOf(AFormatCell: PCell): integer; diff --git a/components/fpspreadsheet/xlscommon.pas b/components/fpspreadsheet/xlscommon.pas index ef92088a0..887ba96c4 100644 --- a/components/fpspreadsheet/xlscommon.pas +++ b/components/fpspreadsheet/xlscommon.pas @@ -762,8 +762,6 @@ end; { TsSpreadBIFFReader } constructor TsSpreadBIFFReader.Create(AWorkbook: TsWorkbook); -var - i: Integer; begin inherited Create(AWorkbook); FXFList := TFPList.Create; @@ -914,7 +912,7 @@ var begin Result := nil; lXFData := TXFListData(FXFList.Items[AXFIndex]); - i := NumFormatList.Find(lXFData.FormatIndex); + i := NumFormatList.FindByIndex(lXFData.FormatIndex); if i <> -1 then Result := NumFormatList[i]; end; @@ -1773,7 +1771,7 @@ var item: TsNumFormatData; begin ListAllNumFormats; - i := NumFormatList.Find(NumFormatList.FirstFormatIndexInFile); + i := NumFormatList.FindByIndex(NumFormatList.FirstFormatIndexInFile); if i > -1 then while i < NumFormatList.Count do begin item := NumFormatList[i];