From 68f1ccd49784abb45121d529e3d2ab4f2845cb8c Mon Sep 17 00:00:00 2001 From: sekelsenmat Date: Wed, 25 May 2011 15:50:18 +0000 Subject: [PATCH] FPSpreadsheet: Starts implementing biff2 formatting and also flexible support for biff formats, but all parts are unfinished git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1644 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/fpspreadsheet.pas | 26 ++--- components/fpspreadsheet/xlsbiff2.pas | 39 +++++++- components/fpspreadsheet/xlsbiff8.pas | 107 ++++++++++++++------- 3 files changed, 123 insertions(+), 49 deletions(-) diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index a5a331f46..0e138b8da 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -88,7 +88,7 @@ type {@@ List of possible formatting fields } - TsUsedFormattingField = (uffTextRotation, uffBold, uffBorder{, uffBackgroundColor}); + TsUsedFormattingField = (uffTextRotation, uffBold, uffBorder, uffBackgroundColor); {@@ Describes which formatting fields are active } @@ -109,7 +109,7 @@ type {.@@ Colors in FPSpreadsheet as given by a list of possible default values } - //TsColor = (); + TsColor = (scLtGrey); {@@ Cell structure for TsWorksheet @@ -133,7 +133,7 @@ type UsedFormattingFields: TsUsedFormattingFields; TextRotation: TsTextRotation; Border: TsCellBorders; - //BackgroundColor: TsColor; + BackgroundColor: TsColor; end; PCell = ^TCell; @@ -232,6 +232,8 @@ type TsSpreadWriterClass = class of TsCustomSpreadWriter; + TCellsCallback = procedure (ACell: PCell; AStream: TStream) of object; + { TsCustomSpreadWriter } TsCustomSpreadWriter = class @@ -239,8 +241,9 @@ type { Helper routines } function ExpandFormula(AFormula: TsFormula): TsExpandedFormula; { General writing methods } - procedure WriteCellCallback(data, arg: pointer); + procedure WriteCellCallback(ACell: PCell; AStream: TStream); procedure WriteCellsToStream(AStream: TStream; ACells: TAVLTree); + procedure IterateThroughCells(AStream: TStream; ACells: TAVLTree; ACallback: TCellsCallback); procedure WriteToFile(const AFileName: string; AData: TsWorkbook; const AOverwriteExisting: Boolean = False); virtual; procedure WriteToStream(AStream: TStream; AData: TsWorkbook); virtual; @@ -1000,14 +1003,8 @@ end; @see TsCustomSpreadWriter.WriteCellsToStream } -procedure TsCustomSpreadWriter.WriteCellCallback(data, arg: pointer); -var - ACell: PCell; - AStream: TStream; +procedure TsCustomSpreadWriter.WriteCellCallback(ACell: PCell; AStream: TStream); begin - ACell := PCell(data); - AStream := TStream(arg); - case ACell.ContentType of cctNumber: WriteNumber(AStream, ACell^.Row, ACell^.Col, ACell^.NumberValue, ACell); cctUTF8String: WriteLabel(AStream, ACell^.Row, ACell^.Col, ACell^.UTF8StringValue, ACell); @@ -1025,13 +1022,18 @@ end; @param ACells List of cells to be writeen } procedure TsCustomSpreadWriter.WriteCellsToStream(AStream: TStream; ACells: TAVLTree); +begin + IterateThroughCells(AStream, ACells, WriteCellCallback); +end; + +procedure TsCustomSpreadWriter.IterateThroughCells(AStream: TStream; ACells: TAVLTree; ACallback: TCellsCallback); var AVLNode: TAVLTreeNode; begin AVLNode := ACells.FindLowest; While Assigned(AVLNode) do begin - WriteCellCallback(AVLNode.Data, Pointer(AStream)); + ACallback(PCell(AVLNode.Data), AStream); AVLNode := ACells.FindSuccessor(AVLNode); end; end; diff --git a/components/fpspreadsheet/xlsbiff2.pas b/components/fpspreadsheet/xlsbiff2.pas index 39449079f..f1f8835b3 100755 --- a/components/fpspreadsheet/xlsbiff2.pas +++ b/components/fpspreadsheet/xlsbiff2.pas @@ -58,6 +58,7 @@ type TsSpreadBIFF2Writer = class(TsCustomSpreadWriter) private function FEKindToExcelID(AElement: TFEKind; var AParamsNum, AFuncNum: Byte): Byte; + procedure WriteCellFormatting(AStream: TStream; ACell: PCell); public { General writing methods } procedure WriteToStream(AStream: TStream; AData: TsWorkbook); override; @@ -120,6 +121,40 @@ begin end; end; +procedure TsSpreadBIFF2Writer.WriteCellFormatting(AStream: TStream; ACell: PCell); +var + BorderByte: Byte = 0; +begin + if ACell^.UsedFormattingFields = [] then + begin + AStream.WriteByte($0); + AStream.WriteByte($0); + AStream.WriteByte($0); + Exit; + end; + + AStream.WriteByte($0); + AStream.WriteByte($0); + + // The Border and Background + + BorderByte := 0; + + if uffBorder in ACell^.UsedFormattingFields then + begin + if cbNorth in ACell^.Border then BorderByte := BorderByte or $20; + if cbWest in ACell^.Border then BorderByte := BorderByte or $08; + if cbEast in ACell^.Border then BorderByte := BorderByte or $10; + if cbSouth in ACell^.Border then BorderByte := BorderByte or $40; + end; + + // BIFF2 does not support a background color, just a "shaded" option + if uffBackgroundColor in ACell^.UsedFormattingFields then + BorderByte := BorderByte or $80; + + AStream.WriteByte(BorderByte); +end; + { Writes an Excel 2 file to a stream @@ -298,9 +333,7 @@ begin AStream.WriteWord(WordToLE(ACol)); { BIFF2 Attributes } - AStream.WriteByte($0); - AStream.WriteByte($0); - AStream.WriteByte($0); + WriteCellFormatting(AStream, ACell); { String with 8-bit size } AStream.WriteByte(L); diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index 20139162f..99b0c7395 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -102,6 +102,12 @@ type { TsSpreadBIFF8Writer } TsSpreadBIFF8Writer = class(TsCustomSpreadWriter) + 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(); public // constructor Create; // destructor Destroy; override; @@ -243,6 +249,69 @@ const { TsSpreadBIFF8Writer } +{@@ Index to XF record, according to formatting } +procedure TsSpreadBIFF8Writer.WriteXFIndex(AStream: TStream; ACell: PCell); +begin + if ACell^.UsedFormattingFields = [uffTextRotation] then + begin + case ACell^.TextRotation of + rt90DegreeCounterClockwiseRotation: AStream.WriteWord(WordToLE(16)); + rt90DegreeClockwiseRotation: AStream.WriteWord(WordToLE(17)); + 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); + end; + + // Unfinished ... +end; + +procedure TsSpreadBIFF8Writer.WriteXFFieldsForFormattingStyles(); +begin + // Unfinished ... +end; + {******************************************************************* * TsSpreadBIFF8Writer.WriteToFile () * @@ -391,6 +460,9 @@ begin 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(); WriteStyle(AStream); @@ -770,40 +842,7 @@ begin AStream.WriteWord(WordToLE(ACol)); { Index to XF record, according to formatting } - if ACell^.UsedFormattingFields = [uffTextRotation] then - begin - case ACell^.TextRotation of - rt90DegreeCounterClockwiseRotation: AStream.WriteWord(WordToLE(16)); - rt90DegreeClockwiseRotation: AStream.WriteWord(WordToLE(17)); - 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)); + WriteXFIndex(AStream, ACell); { Byte String with 16-bit size } AStream.WriteWord(WordToLE(L));