From 4570c4785ac5613adda863e0ff7fdd5a6dde21ed Mon Sep 17 00:00:00 2001 From: sekelsenmat Date: Thu, 26 May 2011 09:25:56 +0000 Subject: [PATCH] FPSpreadsheet: Finishes implementing a generic font style manager for biff formats. Finishes integrating it with biff8 git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1646 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/fpspreadsheet.pas | 16 +- .../fpspreadsheet/laz_fpspreadsheet.lpk | 13 +- .../fpspreadsheet/laz_fpspreadsheet.pas | 4 +- components/fpspreadsheet/xlsbiff8.pas | 186 ++++++++++-------- components/fpspreadsheet/xlsbiffcommon.pas | 106 ++++++++++ 5 files changed, 235 insertions(+), 90 deletions(-) create mode 100644 components/fpspreadsheet/xlsbiffcommon.pas diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 0e138b8da..dc630504f 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -94,7 +94,21 @@ type TsUsedFormattingFields = set of TsUsedFormattingField; - {@@ Text rotation formatting } + {@@ Text rotation formatting. The text is rotated relative to the standard + orientation, which is from left to right horizontal: ---> + ABC + + So 90 degrees clockwise means that the text will be: + | A + | B + \|/ C + + And 90 degree counter clockwise will be: + + /|\ C + | B + | A + } TsTextRotation = (trHorizontal, rt90DegreeClockwiseRotation, rt90DegreeCounterClockwiseRotation); diff --git a/components/fpspreadsheet/laz_fpspreadsheet.lpk b/components/fpspreadsheet/laz_fpspreadsheet.lpk index ee2de23b9..c301fcbdb 100644 --- a/components/fpspreadsheet/laz_fpspreadsheet.lpk +++ b/components/fpspreadsheet/laz_fpspreadsheet.lpk @@ -4,16 +4,21 @@ - + + + + + + - + @@ -90,6 +95,10 @@ + + + + diff --git a/components/fpspreadsheet/laz_fpspreadsheet.pas b/components/fpspreadsheet/laz_fpspreadsheet.pas index 7c8dcafbd..1d0f5c817 100644 --- a/components/fpspreadsheet/laz_fpspreadsheet.pas +++ b/components/fpspreadsheet/laz_fpspreadsheet.pas @@ -1,4 +1,4 @@ -{ This file was automatically created by Lazarus. do not edit! +{ This file was automatically created by Lazarus. Do not edit! This source is only used to compile and install the package. } @@ -11,7 +11,7 @@ uses xlsbiff5, xlsbiff8, xlsxooxml, fpsutils, fpszipper, uvirtuallayer_types, uvirtuallayer, uvirtuallayer_ole, uvirtuallayer_ole_helpers, uvirtuallayer_ole_types, uvirtuallayer_stream, fpolebasic, xlscommon, - fpsconvencoding, LazarusPackageIntf; + fpsconvencoding, xlsbiffcommon, LazarusPackageIntf; implementation diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index 99b0c7395..fe630c05c 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -53,7 +53,7 @@ interface uses Classes, SysUtils, fpcanvas, - fpspreadsheet, + fpspreadsheet, xlsbiffcommon, {$ifdef USE_NEW_OLE} fpolebasic, {$else} @@ -65,7 +65,7 @@ type { TsSpreadBIFF8Reader } - TsSpreadBIFF8Reader = class(TsCustomSpreadReader) + TsSpreadBIFF8Reader = class(TsSpreadBIFFReader) private RecordSize: Word; PendingRecordSize: SizeInt; @@ -101,13 +101,12 @@ type { TsSpreadBIFF8Writer } - TsSpreadBIFF8Writer = class(TsCustomSpreadWriter) + TsSpreadBIFF8Writer = class(TsSpreadBIFFWriter) private - FFormattingStyles: array of TCell; // An array with cells which are models for the used styles procedure WriteXFIndex(AStream: TStream; ACell: PCell); - procedure ListAllFormattingStylesCallback(ACell: PCell; AStream: TStream); - procedure ListAllFormattingStyles(AData: TsWorkbook); - procedure WriteXFFieldsForFormattingStyles(); + procedure WriteXFFieldsForFormattingStyles(AStream: TStream); + protected + procedure AddDefaultFormats(); override; public // constructor Create; // destructor Destroy; override; @@ -249,9 +248,19 @@ const { TsSpreadBIFF8Writer } -{@@ Index to XF record, according to formatting } +{ Index to XF record, according to formatting } procedure TsSpreadBIFF8Writer.WriteXFIndex(AStream: TStream; ACell: PCell); +var + lIndex: Integer; + lXFIndex: Word; begin + // First try the fast methods for default formats + if ACell^.UsedFormattingFields = [] then + begin + AStream.WriteWord(WordToLE(15)); + Exit; + end; + if ACell^.UsedFormattingFields = [uffTextRotation] then begin case ACell^.TextRotation of @@ -260,56 +269,93 @@ begin else AStream.WriteWord(WordToLE(15)); end; - end - else if ACell^.UsedFormattingFields = [uffBold] then - begin - AStream.WriteWord(WordToLE(18)); - end - else if ACell^.UsedFormattingFields = [uffBorder] then - begin - if ACell^.Border = [] then AStream.WriteWord(WordToLE(15)) - else if ACell^.Border = [cbNorth] then AStream.WriteWord(WordToLE(19)) - else if ACell^.Border = [cbWest] then AStream.WriteWord(WordToLE(20)) - else if ACell^.Border = [cbEast] then AStream.WriteWord(WordToLE(21)) - else if ACell^.Border = [cbSouth] then AStream.WriteWord(WordToLE(22)) - else if ACell^.Border = [cbNorth, cbWest] then AStream.WriteWord(WordToLE(23)) - else if ACell^.Border = [cbNorth, cbEast] then AStream.WriteWord(WordToLE(24)) - else if ACell^.Border = [cbNorth, cbSouth] then AStream.WriteWord(WordToLE(25)) - else if ACell^.Border = [cbWest, cbEast] then AStream.WriteWord(WordToLE(26)) - else if ACell^.Border = [cbWest, cbSouth] then AStream.WriteWord(WordToLE(27)) - else if ACell^.Border = [cbEast, cbSouth] then AStream.WriteWord(WordToLE(28)) - else if ACell^.Border = [cbNorth, cbWest, cbEast] then AStream.WriteWord(WordToLE(29)) - else if ACell^.Border = [cbNorth, cbWest, cbSouth] then AStream.WriteWord(WordToLE(30)) - else if ACell^.Border = [cbNorth, cbEast, cbSouth] then AStream.WriteWord(WordToLE(31)) - else if ACell^.Border = [cbWest, cbEast, cbSouth] then AStream.WriteWord(WordToLE(32)) - else if ACell^.Border = [cbNorth, cbWest, cbEast, cbSouth] then AStream.WriteWord(WordToLE(33)); - end - else - AStream.WriteWord(WordToLE(15)); -end; - -procedure TsSpreadBIFF8Writer.ListAllFormattingStylesCallback(ACell: PCell; AStream: TStream); -begin - if ACell^.UsedFormattingFields = [] then Exit; - - // Unfinished ... -end; - -procedure TsSpreadBIFF8Writer.ListAllFormattingStyles(AData: TsWorkbook); -var - i: Integer; -begin - for i := 0 to AData.GetWorksheetCount - 1 do - begin - IterateThroughCells(nil, AData.GetWorksheetByIndex(i).Cells, ListAllFormattingStylesCallback); + Exit; end; - // Unfinished ... + if ACell^.UsedFormattingFields = [uffBold] then + begin + AStream.WriteWord(WordToLE(18)); + Exit; + end; + + // If not, then we need to search in the list of dynamic formats + + lIndex := FindFormattingInList(ACell); + // Carefully check the index + if (lIndex < 0) or (lIndex > Length(FFormattingStyles)) then + raise Exception.Create('[TsSpreadBIFF8Writer.WriteXFIndex] Invalid Index, this should not happen!'); + + lXFIndex := FFormattingStyles[lIndex].Row; + + AStream.WriteWord(WordToLE(lXFIndex)); end; -procedure TsSpreadBIFF8Writer.WriteXFFieldsForFormattingStyles(); +procedure TsSpreadBIFF8Writer.WriteXFFieldsForFormattingStyles(AStream: TStream); +var + i: Integer; + lFontIndex: Word; + lTextRotation: Byte; + lBorders: TsCellBorders; begin - // Unfinished ... + // The first 4 styles were already added + for i := 4 to Length(FFormattingStyles) - 1 do + begin + // Default styles + lFontIndex := 0; + lTextRotation := XF_ROTATION_HORIZONTAL; + lBorders := []; + + // Now apply the modifications + if uffBorder in FFormattingStyles[i].UsedFormattingFields then + lBorders := FFormattingStyles[i].Border; + + if uffTextRotation in FFormattingStyles[i].UsedFormattingFields then + begin + case FFormattingStyles[i].TextRotation of + trHorizontal: lTextRotation := XF_ROTATION_HORIZONTAL; + rt90DegreeClockwiseRotation: lTextRotation := XF_ROTATION_90_DEGREE_CLOCKWISE; + rt90DegreeCounterClockwiseRotation: lTextRotation := XF_ROTATION_90_DEGREE_COUNTERCLOCKWISE; + end; + end; + + if uffBold in FFormattingStyles[i].UsedFormattingFields then + lFontIndex := 1; + +// if uffBackgroundColor in FFormattingStyles[i].UsedFormattingFields then +// lFontIndex := 1; + + // And finally write the style + WriteXF(AStream, lFontIndex, 0, lTextRotation, lBorders); + end; +end; + +{@@ + These are default formats which are added as XF fields regardless of being used + in the document or not. +} +procedure TsSpreadBIFF8Writer.AddDefaultFormats(); +begin + NextXFIndex := 19; + + SetLength(FFormattingStyles, 4); + + // XF15 - Default, no formatting + FFormattingStyles[0].UsedFormattingFields := []; + FFormattingStyles[0].Row := 15; + + // XF16 - Rotated + FFormattingStyles[1].UsedFormattingFields := [uffTextRotation]; + FFormattingStyles[1].Row := 16; + FFormattingStyles[1].TextRotation := rt90DegreeCounterClockwiseRotation; + + // XF17 - Rotated + FFormattingStyles[2].UsedFormattingFields := [uffTextRotation]; + FFormattingStyles[2].Row := 17; + FFormattingStyles[2].TextRotation := rt90DegreeClockwiseRotation; + + // XF18 - Bold + FFormattingStyles[3].UsedFormattingFields := [uffBold]; + FFormattingStyles[3].Row := 18; end; {******************************************************************* @@ -430,39 +476,9 @@ begin WriteXF(AStream, 0, 0, XF_ROTATION_90_DEGREE_CLOCKWISE, []); // XF18 - Bold WriteXF(AStream, 1, 0, XF_ROTATION_HORIZONTAL, []); - // XF19 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbNorth]); - // XF20 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbWest]); - // XF21 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbEast]); - // XF22 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbSouth]); - // XF23 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbNorth, cbWest]); - // XF24 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbNorth, cbEast]); - // XF25 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbNorth, cbSouth]); - // XF26 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbWest, cbEast]); - // XF27 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbWest, cbSouth]); - // XF28 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbEast, cbSouth]); - // XF29 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbNorth, cbWest, cbEast]); - // XF30 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbNorth, cbWest, cbSouth]); - // XF31 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbNorth, cbEast, cbSouth]); - // XF32 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbWest, cbEast, cbSouth]); - // XF33 - Border - WriteXF(AStream, 0, 0, XF_ROTATION_HORIZONTAL, [cbNorth, cbWest, cbEast, cbSouth]); // Add further all non-standard formatting styles -// ListAllFormattingStyles(AData); -// WriteXFFieldsForFormattingStyles(); + ListAllFormattingStyles(AData); + WriteXFFieldsForFormattingStyles(AStream); WriteStyle(AStream); diff --git a/components/fpspreadsheet/xlsbiffcommon.pas b/components/fpspreadsheet/xlsbiffcommon.pas new file mode 100644 index 000000000..5143a94d8 --- /dev/null +++ b/components/fpspreadsheet/xlsbiffcommon.pas @@ -0,0 +1,106 @@ +unit xlsbiffcommon; + +{$ifdef fpc} + {$mode delphi} +{$endif} + +interface + +uses + Classes, SysUtils, + fpspreadsheet, + fpsutils; + +type + + { TsSpreadBIFFReader } + + TsSpreadBIFFReader = class(TsCustomSpreadReader) + protected + end; + + { TsSpreadBIFFWriter } + + TsSpreadBIFFWriter = class(TsCustomSpreadWriter) + protected + { + An array with cells which are models for the used styles + In this array the Row property holds the Index to the corresponding XF field + } + FFormattingStyles: array of TCell; + NextXFIndex: Integer; // Indicates which should be the next XF Index when filling the styles list + function FindFormattingInList(AFormat: PCell): Integer; + procedure AddDefaultFormats(); virtual; + procedure ListAllFormattingStylesCallback(ACell: PCell; AStream: TStream); + procedure ListAllFormattingStyles(AData: TsWorkbook); + end; + +implementation + +{ + Checks if the style of a cell is in the list FFormattingStyles and returns the index + or -1 if it isn't +} +function TsSpreadBIFFWriter.FindFormattingInList(AFormat: PCell): Integer; +var + i: Integer; +begin + Result := -1; + + for i := 0 to Length(FFormattingStyles) - 1 do + begin + if (FFormattingStyles[i].UsedFormattingFields <> AFormat^.UsedFormattingFields) then Continue; + + if uffTextRotation in AFormat^.UsedFormattingFields then + if (FFormattingStyles[i].TextRotation <> AFormat^.TextRotation) then Continue; + + if uffBorder in AFormat^.UsedFormattingFields then + if (FFormattingStyles[i].Border <> AFormat^.Border) then Continue; + + if uffBackgroundColor in AFormat^.UsedFormattingFields then + if (FFormattingStyles[i].BackgroundColor <> AFormat^.BackgroundColor) then Continue; + + // If we arrived here it means that the styles match + Exit(i); + end; +end; + +{ Each descendent should define it's own default formats, if any. + Always add the normal, unformatted style first to speed up. } +procedure TsSpreadBIFFWriter.AddDefaultFormats(); +begin + SetLength(FFormattingStyles, 0); + NextXFIndex := 0; +end; + +procedure TsSpreadBIFFWriter.ListAllFormattingStylesCallback(ACell: PCell; AStream: TStream); +var + Len: Integer; +begin + if ACell^.UsedFormattingFields = [] then Exit; + + if FindFormattingInList(ACell) <> -1 then Exit; + + Len := Length(FFormattingStyles); + SetLength(FFormattingStyles, Len+1); + FFormattingStyles[Len] := ACell^; + FFormattingStyles[Len].Row := NextXFIndex; + Inc(NextXFIndex); +end; + +procedure TsSpreadBIFFWriter.ListAllFormattingStyles(AData: TsWorkbook); +var + i: Integer; +begin + SetLength(FFormattingStyles, 0); + + AddDefaultFormats(); + + for i := 0 to AData.GetWorksheetCount - 1 do + begin + IterateThroughCells(nil, AData.GetWorksheetByIndex(i).Cells, ListAllFormattingStylesCallback); + end; +end; + +end. +