fpspreadsheet: Reading/writing of boolean cell values for all file formats (BIFF not complete, yet).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3655 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2014-10-14 15:56:08 +00:00
parent 1262d95889
commit a4c5667dc9
7 changed files with 496 additions and 54 deletions

View File

@ -12,6 +12,7 @@ type
TsCSVReader = class(TsCustomSpreadReader) TsCSVReader = class(TsCustomSpreadReader)
private private
FWorksheetName: String; FWorksheetName: String;
function IsBool(AText: String; out AValue: Boolean): Boolean;
function IsDateTime(AText: String; out ADateTime: TDateTime): Boolean; function IsDateTime(AText: String; out ADateTime: TDateTime): Boolean;
function IsNumber(AText: String; out ANumber: Double): Boolean; function IsNumber(AText: String; out ANumber: Double): Boolean;
function IsQuotedText(var AText: String): Boolean; function IsQuotedText(var AText: String): Boolean;
@ -34,6 +35,8 @@ type
protected protected
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); override; ACell: PCell); override;
procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: Boolean; ACell: PCell); override;
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TDateTime; ACell: PCell); override; const AValue: TDateTime; ACell: PCell); override;
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
@ -52,22 +55,30 @@ type
TsCSVLineEnding = (leSystem, leCRLF, leCR, leLF); TsCSVLineEnding = (leSystem, leCRLF, leCR, leLF);
TsCSVParams = record TsCSVParams = record // W = writing, R = reading, RW = reading/writing
SheetIndex: Integer; SheetIndex: Integer; // W: Index of the sheet to be written
LineEnding: TsCSVLineEnding; LineEnding: TsCSVLineEnding; // W: Specification for line ending to be written
Delimiter: Char; Delimiter: Char; // RW: Column delimiter
QuoteChar: Char; QuoteChar: Char; // RW: Character for quoting texts
NumberFormat: String; NumberFormat: String; // W: if empty write numbers like in sheet, otherwise use this format
FormatSettings: TFormatSettings; DateTimeAsText: Boolean; // R: if false tries to convert text to date/time values
BoolAsText: Boolean; // R: if false tries to convert text to boolean values
TrueText: String; // RW: String for boolean TRUE
FalseText: String; // RW: String for boolean FALSE
FormatSettings: TFormatSettings; // RW: add'l parameters for conversion
end; end;
var var
CSVParams: TsCSVParams = ( CSVParams: TsCSVParams = (
SheetIndex: 0; // Store sheet #0 by default SheetIndex: 0;
LineEnding: leSystem; // Write system lineending, read any LineEnding: leSystem;
Delimiter: ';'; // Column delimiter Delimiter: ';';
QuoteChar: '"'; // for quoted strings QuoteChar: '"';
NumberFormat: ''; // if empty write numbers like in sheet, otherwise use this format NumberFormat: '';
DateTimeAsText: false;
BoolAsText: false;
TrueText: 'TRUE';
FalseText: 'FALSE';
); );
@ -174,6 +185,21 @@ begin
FWorksheetName := 'Sheet1'; // will be replaced by filename FWorksheetName := 'Sheet1'; // will be replaced by filename
end; end;
function TsCSVReader.IsBool(AText: String; out AValue: Boolean): Boolean;
begin
if SameText(AText, CSVParams.TrueText) then
begin
AValue := true;
Result := true;
end else
if SameText(AText, CSVParams.FalseText) then
begin
AValue := false;
Result := true;
end else
Result := false;
end;
function TsCSVReader.IsDateTime(AText: String; out ADateTime: TDateTime): Boolean; function TsCSVReader.IsDateTime(AText: String; out ADateTime: TDateTime): Boolean;
begin begin
Result := TryStrToDateTime(AText, ADateTime, CSVParams.FormatSettings); Result := TryStrToDateTime(AText, ADateTime, CSVParams.FormatSettings);
@ -204,8 +230,9 @@ end;
procedure TsCSVReader.ReadCellValue(ARow, ACol: Cardinal; AText: String); procedure TsCSVReader.ReadCellValue(ARow, ACol: Cardinal; AText: String);
var var
dbl: Double; dblValue: Double;
dt: TDateTime; dtValue: TDateTime;
boolValue: Boolean;
begin begin
// Empty strings are blank cells -- nothing to do // Empty strings are blank cells -- nothing to do
if AText = '' then if AText = '' then
@ -219,16 +246,23 @@ begin
end; end;
// Check for a NUMBER cell // Check for a NUMBER cell
if IsNumber(AText, dbl) then if IsNumber(AText, dblValue) then
begin begin
FWorksheet.WriteNumber(ARow, ACol, dbl); FWorksheet.WriteNumber(ARow, ACol, dblValue);
exit; exit;
end; end;
// Check for a DATE/TIME cell // Check for a DATE/TIME cell
if IsDateTime(AText, dt) then if not CSVParams.DateTimeAsText and IsDateTime(AText, dtValue) then
begin begin
FWorksheet.WriteDateTime(ARow, ACol, dt); FWorksheet.WriteDateTime(ARow, ACol, dtValue);
exit;
end;
// Check for a BOOLEAN cell
if not CSVParams.BoolAsText and IsBool(AText, boolValue) then
begin
FWorksheet.WriteBoolValue(ARow, aCol, boolValue);
exit; exit;
end; end;
@ -323,6 +357,7 @@ end;
{ -----------------------------------------------------------------------------} { -----------------------------------------------------------------------------}
{ TsCSVWriter } { TsCSVWriter }
{------------------------------------------------------------------------------} {------------------------------------------------------------------------------}
constructor TsCSVWriter.Create(AWorkbook: TsWorkbook); constructor TsCSVWriter.Create(AWorkbook: TsWorkbook);
begin begin
inherited Create(AWorkbook); inherited Create(AWorkbook);
@ -343,6 +378,16 @@ begin
// nothing to do // nothing to do
end; end;
procedure TsCSVWriter.WriteBool(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: Boolean; ACell: PCell);
begin
Unused(ARow, ACol, ACell);
if AValue then
AppendToStream(AStream, CSVParams.TrueText)
else
AppendToStream(AStream, CSVParams.FalseText);
end;
{ Write date/time values in the same way they are displayed in the sheet } { Write date/time values in the same way they are displayed in the sheet }
procedure TsCSVWriter.WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; procedure TsCSVWriter.WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TDateTime; ACell: PCell); const AValue: TDateTime; ACell: PCell);
@ -351,14 +396,24 @@ begin
AppendToStream(AStream, FWorksheet.ReadAsUTF8Text(ACell)); AppendToStream(AStream, FWorksheet.ReadAsUTF8Text(ACell));
end; end;
{ CSV does not support formulas, but we have to write the formula results to
to stream. }
procedure TsCSVWriter.WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; procedure TsCSVWriter.WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); ACell: PCell);
begin begin
// no formulas in CSV if ACell = nil then
Unused(AStream); exit;
Unused(ARow, ACol, AStream); case ACell^.ContentType of
cctBool : WriteBool(AStream, ARow, ACol, ACell^.BoolValue, ACell);
cctEmpty : ;
cctDateTime : WriteDateTime(AStream, ARow, ACol, ACell^.DateTimeValue, ACell);
cctNumber : WriteNumber(AStream, ARow, ACol, ACell^.NumberValue, ACell);
cctUTF8String: WriteLabel(AStream, ARow, ACol, ACell^.UTF8StringValue, ACell);
cctError : ;
end;
end; end;
{ Writes a LABEL cell to the stream. }
procedure TsCSVWriter.WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; procedure TsCSVWriter.WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: string; ACell: PCell); const AValue: string; ACell: PCell);
var var

View File

@ -85,6 +85,8 @@ type
// Applies a style to a cell // Applies a style to a cell
procedure ApplyStyleToCell(ARow, ACol: Cardinal; AStyleName: String); overload; procedure ApplyStyleToCell(ARow, ACol: Cardinal; AStyleName: String); overload;
procedure ApplyStyleToCell(ACell: PCell; AStyleName: String); overload; procedure ApplyStyleToCell(ACell: PCell; AStyleName: String); overload;
// Extracts a boolean value from the xml node
function ExtractBoolFromNode(ANode: TDOMNode): Boolean;
// Extracts the date/time value from the xml node // Extracts the date/time value from the xml node
function ExtractDateTimeFromNode(ANode: TDOMNode; function ExtractDateTimeFromNode(ANode: TDOMNode;
ANumFormat: TsNumberFormat; const AFormatStr: String): TDateTime; ANumFormat: TsNumberFormat; const AFormatStr: String): TDateTime;
@ -110,6 +112,7 @@ type
procedure ReadStyles(AStylesNode: TDOMNode); procedure ReadStyles(AStylesNode: TDOMNode);
{ Record writing methods } { Record writing methods }
procedure ReadBlank(ARow, ACol: Word; ACellNode: TDOMNode); reintroduce; procedure ReadBlank(ARow, ACol: Word; ACellNode: TDOMNode); reintroduce;
procedure ReadBoolean(ARow, ACol: Word; ACellNode: TDOMNode);
procedure ReadDateTime(ARow, ACol: Word; ACellNode: TDOMNode); procedure ReadDateTime(ARow, ACol: Word; ACellNode: TDOMNode);
procedure ReadFormula(ARow, ACol: Word; ACellNode: TDOMNode); reintroduce; procedure ReadFormula(ARow, ACol: Word; ACellNode: TDOMNode); reintroduce;
procedure ReadLabel(ARow, ACol: Word; ACellNode: TDOMNode); reintroduce; procedure ReadLabel(ARow, ACol: Word; ACellNode: TDOMNode); reintroduce;
@ -173,6 +176,8 @@ type
{ Record writing methods } { Record writing methods }
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); override; ACell: PCell); override;
procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: Boolean; ACell: PCell); override;
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); override; ACell: PCell); override;
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
@ -856,6 +861,19 @@ begin
FNumFormatList := TsSpreadOpenDocNumFormatList.Create(Workbook); FNumFormatList := TsSpreadOpenDocNumFormatList.Create(Workbook);
end; end;
{ Extracts a boolean value from a "boolean" cell node.
Is called from ReadBoolean }
function TsSpreadOpenDocReader.ExtractBoolFromNode(ANode: TDOMNode): Boolean;
var
value: String;
begin
value := GetAttrValue(ANode, 'office:boolean-value');
if (lowercase(value) = 'true') then
Result := true
else
Result := false;
end;
{ Extracts a date/time value from a "date-value" or "time-value" cell node. { Extracts a date/time value from a "date-value" or "time-value" cell node.
Requires the number format and format strings to optimize agreement with Requires the number format and format strings to optimize agreement with
fpc date/time values. fpc date/time values.
@ -998,6 +1016,28 @@ begin
Workbook.OnReadCellData(Workbook, ARow, ACol, cell); Workbook.OnReadCellData(Workbook, ARow, ACol, cell);
end; end;
procedure TsSpreadOpenDocReader.ReadBoolean(ARow, ACol: Word; ACellNode: TDOMNode);
var
styleName: String;
cell: PCell;
boolValue: Boolean;
begin
if FIsVirtualMode then begin
InitCell(ARow, ACol, FVirtualCell);
cell := @FVirtualCell;
end else
cell := FWorksheet.GetCell(ARow, ACol);
boolValue := ExtractBoolFromNode(ACellNode);
FWorkSheet.WriteBoolValue(cell, boolValue);
styleName := GetAttrValue(ACellNode, 'table:style-name');
ApplyStyleToCell(cell, stylename);
if FIsVirtualMode then
Workbook.OnReadCellData(Workbook, ARow, ACol, cell);
end;
{ Collection columns used in the given table. The columns contain links to { Collection columns used in the given table. The columns contain links to
styles that must be used when cells in that columns are without styles. } styles that must be used when cells in that columns are without styles. }
procedure TsSpreadOpenDocReader.ReadColumns(ATableNode: TDOMNode); procedure TsSpreadOpenDocReader.ReadColumns(ATableNode: TDOMNode);
@ -1192,6 +1232,7 @@ var
formula: String; formula: String;
stylename: String; stylename: String;
floatValue: Double; floatValue: Double;
boolValue: Boolean;
valueType: String; valueType: String;
valueStr: String; valueStr: String;
node: TDOMNode; node: TDOMNode;
@ -1272,6 +1313,11 @@ begin
FWorksheet.WriteUTF8Text(cell, valueStr); FWorksheet.WriteUTF8Text(cell, valueStr);
end; end;
end else end else
// (d) boolean
if (valuetype = 'boolean') then begin
boolValue := ExtractBoolFromNode(ACellNode);
FWorksheet.WriteBoolValue(cell, boolValue);
end else
// (e) Text // (e) Text
FWorksheet.WriteUTF8Text(cell, valueStr); FWorksheet.WriteUTF8Text(cell, valueStr);
@ -1868,6 +1914,8 @@ begin
ReadNumber(row, col, cellNode) ReadNumber(row, col, cellNode)
else if (paramValueType = 'date') or (paramValueType = 'time') then else if (paramValueType = 'date') or (paramValueType = 'time') then
ReadDateTime(row, col, cellNode) ReadDateTime(row, col, cellNode)
else if (paramValueType = 'boolean') then
ReadBoolean(row, col, cellNode)
else if (paramValueType = '') and (tableStyleName <> '') then else if (paramValueType = '') and (tableStyleName <> '') then
ReadBlank(row, col, cellNode); ReadBlank(row, col, cellNode);
@ -3174,9 +3222,7 @@ begin
end; end;
end; end;
{ { Writes an empty cell to the stream }
Writes an empty cell
}
procedure TsSpreadOpenDocWriter.WriteBlank(AStream: TStream; procedure TsSpreadOpenDocWriter.WriteBlank(AStream: TStream;
const ARow, ACol: Cardinal; ACell: PCell); const ARow, ACol: Cardinal; ACell: PCell);
var var
@ -3186,7 +3232,6 @@ var
spannedStr: String; spannedStr: String;
r1,c1,r2,c2: Cardinal; r1,c1,r2,c2: Cardinal;
begin begin
Unused(AStream, ACell);
Unused(ARow, ACol); Unused(ARow, ACol);
// Merged? // Merged?
@ -3208,6 +3253,58 @@ begin
'<table:table-cell ' + spannedStr + '/>'); '<table:table-cell ' + spannedStr + '/>');
end; end;
{ Writes a boolean cell to the stream }
procedure TsSpreadOpenDocWriter.WriteBool(AStream: TStream;
const ARow, ACol: Cardinal; const AValue: Boolean; ACell: PCell);
var
valType: String;
lIndex: Integer;
lStyle: String;
r1,c1,r2,c2: Cardinal;
rowsSpannedStr, colsSpannedStr, spannedStr: String;
strValue: String;
displayStr: String;
begin
Unused(ARow, ACol);
valType := 'boolean';
if ACell^.UsedFormattingFields <> [] then
begin
lIndex := FindFormattingInList(ACell);
lStyle := ' table:style-name="ce' + IntToStr(lIndex) + '" ';
end else
lStyle := '';
// Merged?
if FWorksheet.IsMergeBase(ACell) then
begin
FWorksheet.FindMergedRange(ACell, r1, c1, r2, c2);
rowsSpannedStr := Format('table:number-rows-spanned="%d"', [r2 - r1 + 1]);
colsSpannedStr := Format('table:number-columns-spanned="%d"', [c2 - c1 + 1]);
spannedStr := colsSpannedStr + ' ' + rowsSpannedStr;
end else
spannedStr := '';
// Displayed value
if AValue then
begin
StrValue := 'true';
DisplayStr := rsTRUE;
end else
begin
strValue := 'false';
DisplayStr := rsFALSE;
end;
AppendToStream(AStream, Format(
'<table:table-cell office:value-type="%s" office:boolean-value="%s" %s %s >' +
'<text:p>%s</text:p>' +
'</table:table-cell>', [
valType, StrValue, lStyle, spannedStr,
DisplayStr
]));
end;
{ Creates an XML string for inclusion of the background color into the { Creates an XML string for inclusion of the background color into the
written file from the backgroundcolor setting in the format cell. written file from the backgroundcolor setting in the format cell.
Is called from WriteStyles (via WriteStylesXMLAsString). } Is called from WriteStyles (via WriteStylesXMLAsString). }
@ -3593,7 +3690,7 @@ var
spannedStr: String; spannedStr: String;
r1,c1,r2,c2: Cardinal; r1,c1,r2,c2: Cardinal;
begin begin
Unused(AStream, ARow, ACol); Unused(ARow, ACol);
// Style // Style
if ACell^.UsedFormattingFields <> [] then begin if ACell^.UsedFormattingFields <> [] then begin
@ -3703,7 +3800,6 @@ var
r1,c1,r2,c2: Cardinal; r1,c1,r2,c2: Cardinal;
str: ansistring; str: ansistring;
begin begin
Unused(AStream, ACell);
Unused(ARow, ACol); Unused(ARow, ACol);
// Style // Style
@ -3753,7 +3849,6 @@ var
spannedStr: String; spannedStr: String;
r1,c1,r2,c2: Cardinal; r1,c1,r2,c2: Cardinal;
begin begin
Unused(AStream, ACell);
Unused(ARow, ACol); Unused(ARow, ACol);
valType := 'float'; valType := 'float';
@ -3818,7 +3913,6 @@ var
spannedStr: String; spannedStr: String;
r1,c1,r2,c2: Cardinal; r1,c1,r2,c2: Cardinal;
begin begin
Unused(AStream, ACell);
Unused(ARow, ACol); Unused(ARow, ACol);
// Merged? // Merged?

View File

@ -1111,8 +1111,12 @@ type
{ Record writing methods } { Record writing methods }
{@@ Abstract method for writing a blank cell. Must be overridden by descendent classes. } {@@ Abstract method for writing a blank cell. Must be overridden by descendent classes. }
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); virtual; abstract; procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); virtual; abstract;
{@@ Abstract method for writing a boolean cell. Must be overridden by descendent classes. }
procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal; const AValue: Boolean; ACell: PCell); virtual; abstract;
{@@ Abstract method for writing a date/time value to a cell. Must be overridden by descendent classes. } {@@ Abstract method for writing a date/time value to a cell. Must be overridden by descendent classes. }
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); virtual; abstract; procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); virtual; abstract;
{@@ Abstract method for writing an Excel error value to a cell. Must be overridden by descendent classes. }
procedure WriteError(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TsErrorValue; ACell: PCell); virtual; abstract;
{@@ Abstract method for writing a formula to a cell. Must be overridden by descendent classes. } {@@ Abstract method for writing a formula to a cell. Must be overridden by descendent classes. }
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); virtual; procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); virtual;
{@@ Abstract method for writing a string to a cell. Must be overridden by descendent classes. } {@@ Abstract method for writing a string to a cell. Must be overridden by descendent classes. }
@ -7718,10 +7722,14 @@ begin
WriteFormula(AStream, ACell^.Row, ACell^.Col, ACell) WriteFormula(AStream, ACell^.Row, ACell^.Col, ACell)
else else
case ACell.ContentType of case ACell.ContentType of
cctEmpty: cctBool:
WriteBlank(AStream, ACell^.Row, ACell^.Col, ACell); WriteBool(AStream, ACell^.Row, ACell^.Col, ACell^.BoolValue, ACell);
cctDateTime: cctDateTime:
WriteDateTime(AStream, ACell^.Row, ACell^.Col, ACell^.DateTimeValue, ACell); WriteDateTime(AStream, ACell^.Row, ACell^.Col, ACell^.DateTimeValue, ACell);
cctEmpty:
WriteBlank(AStream, ACell^.Row, ACell^.Col, ACell);
cctError:
WriteError(AStream, ACell^.Row, ACell^.Col, ACell^.ErrorValue, ACell);
cctNumber: cctNumber:
WriteNumber(AStream, ACell^.Row, ACell^.Col, ACell^.NumberValue, ACell); WriteNumber(AStream, ACell^.Row, ACell^.Col, ACell^.NumberValue, ACell);
cctUTF8String: cctUTF8String:

View File

@ -27,6 +27,7 @@ type
procedure ShowBlankCell; procedure ShowBlankCell;
procedure ShowBOF; procedure ShowBOF;
procedure ShowBookBool; procedure ShowBookBool;
procedure ShowBoolCell;
procedure ShowBottomMargin; procedure ShowBottomMargin;
procedure ShowCalcCount; procedure ShowCalcCount;
procedure ShowCalcMode; procedure ShowCalcMode;
@ -268,6 +269,8 @@ begin
ShowNumberCell; ShowNumberCell;
$0004, $0204: $0004, $0204:
ShowLabelCell; ShowLabelCell;
$0005, $0205:
ShowBoolCell;
$0006: $0006:
ShowFormula; ShowFormula;
$0007, $0207: $0007, $0207:
@ -678,6 +681,99 @@ begin
'Specifies some properties assosciated with a workbook'); 'Specifies some properties assosciated with a workbook');
end; end;
procedure TBIFFGrid.ShowBoolCell;
var
numBytes: Integer;
w: Word;
b: Byte;
begin
RowCount := FixedRows + 5;
ShowRowColData(FBufferIndex);
if FFormat = sfExcel2 then begin
numBytes := 1;
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Cell protection and XF index:'#13);
FDetails.Add(Format('Bits 5-0 = %d: XF Index', [b and $3F]));
case b and $40 of
0: FDetails.Add('Bit 6 = 0: Cell is NOT locked.');
1: FDetails.Add('Bit 6 = 1: Cell is locked.');
end;
case b and $80 of
0: FDetails.Add('Bit 7 = 0: Formula is NOT hidden.');
1: FDetails.Add('Bit 7 = 1: Formula is hidden.');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
'Cell protection and XF index');
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Indexes to format and font records:'#13);
FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f]));
FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
'Indexes of format and font records');
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Cell style:'#13);
case b and $07 of
0: FDetails.Add('Bits 2-0 = 0: Horizontal alignment is GENERAL');
1: FDetails.Add('Bits 2-0 = 1: Horizontal alignment is LEFT');
2: FDetails.Add('Bits 2-0 = 2: Horizontal alignment is CENTERED');
3: FDetails.Add('Bits 2-0 = 3: Horizontal alignment is RIGHT');
4: FDetails.Add('Bits 2-0 = 4: Horizontal alignment is FILLED');
end;
if b and $08 = 0
then FDetails.Add('Bit 3 = 0: Cell has NO left border')
else FDetails.Add('Bit 3 = 1: Cell has left black border');
if b and $10 = 0
then FDetails.Add('Bit 4 = 0: Cell has NO right border')
else FDetails.Add('Bit 4 = 1: Cell has right black border');
if b and $20 = 0
then FDetails.Add('Bit 5 = 0: Cell has NO top border')
else FDetails.Add('Bit 5 = 1: Cell has top black border');
if b and $40 = 0
then FDetails.Add('Bit 6 = 0: Cell has NO bottom border')
else FDetails.Add('Bit 6 = 1: Cell has bottom black border');
if b and $80 = 0
then FDetails.Add('Bit 7 = 0: Cell has NO shaded background')
else FDetails.Add('Bit 7 = 1: Cell has shaded background');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]),
'Cell style');
end else
begin // BIFF3 - BIFF 8
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrROw, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
'Index of XF record');
end;
// boolean value
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numbytes,
Format('%d (%s)', [b, Uppercase(BoolToStr(Boolean(b), true))]),
'Boolean value (0=FALSE, 1=TRUE)'
);
// bool/error flag
numBytes := 1;
b := FBuffer[FBufferIndex];
if b = 0 then
ShowInRow(FCurrRow, FBufferIndex, numbytes, '0 (boolean value)',
'Boolean/Error value flag (0=boolean, 1=error value)')
else
ShowInRow(FCurrRow, FBufferIndex, numbytes, '1 (error value)',
'Boolean/Error value flag (0=boolean, 1=error value)');
end;
procedure TBIFFGrid.ShowBottomMargin; procedure TBIFFGrid.ShowBottomMargin;
var var
numBytes: Integer; numBytes: Integer;

View File

@ -114,7 +114,12 @@ type
protected protected
procedure CreateNumFormatList; override; procedure CreateNumFormatList; override;
procedure ListAllNumFormats; override; procedure ListAllNumFormats; override;
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); override; procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); override;
procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: Boolean; ACell: PCell); override;
procedure WriteError(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TsErrorValue; ACell: PCell);
procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData; procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData;
AListIndex: Integer); override; AListIndex: Integer); override;
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
@ -167,6 +172,7 @@ const
INT_EXCEL_ID_INTEGER = $0002; INT_EXCEL_ID_INTEGER = $0002;
INT_EXCEL_ID_NUMBER = $0003; INT_EXCEL_ID_NUMBER = $0003;
INT_EXCEL_ID_LABEL = $0004; INT_EXCEL_ID_LABEL = $0004;
INT_EXCEL_ID_BOOLERROR = $0005;
INT_EXCEL_ID_ROW = $0008; INT_EXCEL_ID_ROW = $0008;
INT_EXCEL_ID_BOF = $0009; INT_EXCEL_ID_BOF = $0009;
{%H-}INT_EXCEL_ID_INDEX = $000B; {%H-}INT_EXCEL_ID_INDEX = $000B;
@ -185,6 +191,18 @@ const
{%H-}INT_EXCEL_MACRO_SHEET = $0040; {%H-}INT_EXCEL_MACRO_SHEET = $0040;
type type
TBIFF2BoolErrRecord = packed record
RecordID: Word;
RecordSize: Word;
Row: Word;
Col: Word;
Attrib1: Byte;
Attrib2: Byte;
Attrib3: Byte;
BoolErrValue: Byte;
ValueType: Byte;
end;
TBIFF2DimensionsRecord = packed record TBIFF2DimensionsRecord = packed record
RecordID: Word; RecordID: Word;
RecordSize: Word; RecordSize: Word;
@ -1688,6 +1706,81 @@ begin
AStream.WriteBuffer(s[1], len * SizeOf(Char)); AStream.WriteBuffer(s[1], len * SizeOf(Char));
end; end;
{ Writes a BOOLEAN cell record. }
procedure TsSpreadBIFF2Writer.WriteBool(AStream: TStream;
const ARow, ACol: Cardinal; const AValue: Boolean; ACell: PCell);
var
rec: TBIFF2BoolErrRecord;
xf: Integer;
begin
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
exit;
xf := FindXFIndex(ACell);
if xf >= 63 then
WriteIXFE(AStream, xf);
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_BOOLERROR);
rec.RecordSize := WordToLE(9);
{ Row and column index }
rec.Row := WordToLE(ARow);
rec.Col := WordToLE(ACol);
{ BIFF2 attributes }
GetCellAttributes(ACell, xf, rec.Attrib1, rec.Attrib2, rec.Attrib3);
{ Cell value }
rec.BoolErrValue := ord(AValue);
rec.ValueType := 1; // 0 = boolean value, 1 = error value
{ Write out }
AStream.WriteBuffer(rec, SizeOf(rec));
end;
{ Writes an ERROR cell record. }
procedure TsSpreadBIFF2Writer.WriteError(AStream: TStream;
const ARow, ACol: Cardinal; const AValue: TsErrorValue; ACell: PCell);
var
rec: TBIFF2BoolErrRecord;
xf: Integer;
begin
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
exit;
xf := FindXFIndex(ACell);
if xf >= 63 then
WriteIXFE(AStream, xf);
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_BOOLERROR);
rec.RecordSize := WordToLE(9);
{ Row and column index }
rec.Row := WordToLE(ARow);
rec.Col := WordToLE(ACol);
{ BIFF2 attributes }
GetCellAttributes(ACell, xf, rec.Attrib1, rec.Attrib2, rec.Attrib3);
{ Cell value }
case AValue of
errEmptyIntersection : rec.BoolErrValue := $00; // #NULL!
errDivideByZero : rec.BoolErrValue := $07; // #DIV/0!
errWrongType : rec.BoolErrValue := $0F; // #VALUE!
errIllegalRef : rec.BoolErrValue := $17; // #REF!
errWrongName : rec.BoolErrValue := $1D; // #NAME?
errOverflow : rec.BoolErrValue := $24; // #NUM!
errArgError : rec.BoolErrValue := $2A; // #N/A
else exit;
end;
rec.ValueType := 1; // 0 = boolean value, 1 = error value
{ Write out }
AStream.WriteBuffer(rec, SizeOf(rec));
end;
{******************************************************************* {*******************************************************************
* TsSpreadBIFF2Writer.WriteBlank () * TsSpreadBIFF2Writer.WriteBlank ()

View File

@ -44,6 +44,7 @@ const
INT_EXCEL_ID_BLANK = $0201; // BIFF2: $0001 INT_EXCEL_ID_BLANK = $0201; // BIFF2: $0001
INT_EXCEL_ID_NUMBER = $0203; // BIFF2: $0003 INT_EXCEL_ID_NUMBER = $0203; // BIFF2: $0003
INT_EXCEL_ID_LABEL = $0204; // BIFF2: $0004 INT_EXCEL_ID_LABEL = $0204; // BIFF2: $0004
INT_EXCEL_ID_BOOLERROR = $0205; // BIFF2: $0005
INT_EXCEL_ID_STRING = $0207; // BIFF2: $0007 INT_EXCEL_ID_STRING = $0207; // BIFF2: $0007
INT_EXCEL_ID_ROW = $0208; // BIFF2: $0008 INT_EXCEL_ID_ROW = $0208; // BIFF2: $0008
INT_EXCEL_ID_INDEX = $020B; // BIFF2: $000B INT_EXCEL_ID_INDEX = $020B; // BIFF2: $000B
@ -326,7 +327,10 @@ type
// Write out BLANK cell record // Write out BLANK cell record
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); override; ACell: PCell); override;
// Write out used codepage for character encoding // Write out BOOLEAN cell record
procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: Boolean; ACell: PCell); override;
// Writes out used codepage for character encoding
procedure WriteCodepage(AStream: TStream; AEncoding: TsEncoding); procedure WriteCodepage(AStream: TStream; AEncoding: TsEncoding);
// Writes out column info(s) // Writes out column info(s)
procedure WriteColInfo(AStream: TStream; ACol: PCol); procedure WriteColInfo(AStream: TStream; ACol: PCol);
@ -336,6 +340,9 @@ type
// Writes out a TIME/DATE/TIMETIME // Writes out a TIME/DATE/TIMETIME
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TDateTime; ACell: PCell); override; const AValue: TDateTime; ACell: PCell); override;
// Writes out ERROR cell record
procedure WriteError(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TsErrorValue; ACell: PCell); override;
// Writes out a FORMAT record // Writes out a FORMAT record
procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData; procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData;
AListIndex: Integer); virtual; AListIndex: Integer); virtual;
@ -447,6 +454,16 @@ type
XFIndex: Word; XFIndex: Word;
end; end;
TBIFF38BoolErrRecord = packed record
RecordID: Word;
RecordSize: Word;
Row: Word;
Col: Word;
XFIndex: Word;
BoolErrValue: Byte;
ValueType: Byte;
end;
TBIFF58NumberRecord = packed record TBIFF58NumberRecord = packed record
RecordID: Word; RecordID: Word;
RecordSize: Word; RecordSize: Word;
@ -1839,6 +1856,35 @@ begin
AStream.WriteBuffer(rec, SizeOf(rec)); AStream.WriteBuffer(rec, SizeOf(rec));
end; end;
{ Writes a BOOLEAN cell record.
Valie for BIFF3-BIFF8. Override for BIFF2. }
procedure TsSpreadBIFFWriter.WriteBool(AStream: TStream;
const ARow, ACol: Cardinal; const AValue: Boolean; ACell: PCell);
var
rec: TBIFF38BoolErrRecord;
begin
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
exit;
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_BOOLERROR);
rec.RecordSize := WordToLE(8);
{ Row and column index }
rec.Row := WordToLE(ARow);
rec.Col := WordToLE(ACol);
{ Index to XF record, according to formatting }
rec.XFIndex := WordToLE(FindXFIndex(ACell));
{ Cell value }
rec.BoolErrValue := ord(AValue);
rec.ValueType := 0; // 0 = boolean value, 1 = error value
{ Write out }
AStream.WriteBuffer(rec, SizeOf(rec));
end;
procedure TsSpreadBIFFWriter.WriteCodepage(AStream: TStream; procedure TsSpreadBIFFWriter.WriteCodepage(AStream: TStream;
AEncoding: TsEncoding); AEncoding: TsEncoding);
var var
@ -1948,6 +1994,45 @@ begin
WriteNumber(AStream, ARow, ACol, ExcelDateSerial, ACell); WriteNumber(AStream, ARow, ACol, ExcelDateSerial, ACell);
end; end;
{ Writes an ERROR cell record.
Valie for BIFF3-BIFF8. Override for BIFF2. }
procedure TsSpreadBIFFWriter.WriteError(AStream: TStream;
const ARow, ACol: Cardinal; const AValue: TsErrorValue; ACell: PCell);
var
rec: TBIFF38BoolErrRecord;
begin
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
exit;
{ BIFF record header }
rec.RecordID := WordToLE(INT_EXCEL_ID_BOOLERROR);
rec.RecordSize := WordToLE(8);
{ Row and column index }
rec.Row := WordToLE(ARow);
rec.Col := WordToLE(ACol);
{ Index to XF record, according to formatting }
rec.XFIndex := WordToLE(FindXFIndex(ACell));
{ Cell value }
case AValue of
errEmptyIntersection : rec.BoolErrValue := $00; // #NULL!
errDivideByZero : rec.BoolErrValue := $07; // #DIV/0!
errWrongType : rec.BoolErrValue := $0F; // #VALUE!
errIllegalRef : rec.BoolErrValue := $17; // #REF!
errWrongName : rec.BoolErrValue := $1D; // #NAME?
errOverflow : rec.BoolErrValue := $24; // #NUM!
errArgError : rec.BoolErrValue := $2A; // #N/A
else exit;
end;
rec.ValueType := 1; // 0 = boolean value, 1 = error value
{ Write out }
AStream.WriteBuffer(rec, SizeOf(rec));
end;
{ Writes a BIFF format record defined in AFormatData. AListIndex the index of { Writes a BIFF format record defined in AFormatData. AListIndex the index of
the formatdata in the format list (not the FormatIndex!). the formatdata in the format list (not the FormatIndex!).
Needs to be overridden by descendants. } Needs to be overridden by descendants. }

View File

@ -151,6 +151,8 @@ type
//todo: add WriteDate //todo: add WriteDate
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); override; ACell: PCell); override;
procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: Boolean; ACell: PCell); override;
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); override; ACell: PCell); override;
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
@ -448,6 +450,14 @@ begin
ACell^.FontIndex := xf.FontIndex; ACell^.FontIndex := xf.FontIndex;
// Alignment // Alignment
if xf.HorAlignment <> haDefault then
Include(ACell^.UsedFormattingFields, uffHorAlign)
else
Exclude(ACell^.UsedFormattingFields, uffHorAlign);
if xf.VertAlignment <> vaDefault then
Include(ACell^.UsedFormattingFields, uffVertAlign)
else
Exclude(ACell^.UsedformattingFields, uffVertAlign);
ACell^.HorAlignment := xf.HorAlignment; ACell^.HorAlignment := xf.HorAlignment;
ACell^.VertAlignment := xf.VertAlignment; ACell^.VertAlignment := xf.VertAlignment;
@ -469,7 +479,7 @@ begin
if (borderData <> nil) then begin if (borderData <> nil) then begin
ACell^.BorderStyles := borderData.BorderStyles; ACell^.BorderStyles := borderData.BorderStyles;
if borderData.Borders <> [] then begin if borderData.Borders <> [] then begin
Include(Acell^.UsedFormattingFields, uffBorder); Include(ACell^.UsedFormattingFields, uffBorder);
ACell^.Border := borderData.Borders; ACell^.Border := borderData.Borders;
end else end else
Exclude(ACell^.UsedFormattingFields, uffBorder); Exclude(ACell^.UsedFormattingFields, uffBorder);
@ -485,7 +495,7 @@ begin
if xf.NumFmtIndex > 0 then begin if xf.NumFmtIndex > 0 then begin
j := NumFormatList.FindByIndex(xf.NumFmtIndex); j := NumFormatList.FindByIndex(xf.NumFmtIndex);
if j > -1then begin if j > -1 then begin
numFmtData := NumFormatList[j]; numFmtData := NumFormatList[j];
Include(ACell^.UsedFormattingFields, uffNumberFormat); Include(ACell^.UsedFormattingFields, uffNumberFormat);
ACell^.NumberFormat := numFmtData.NumFormat; ACell^.NumberFormat := numFmtData.NumFormat;
@ -511,12 +521,13 @@ procedure TsSpreadOOXMLReader.ReadBorders(ANode: TDOMNode);
nodeName: String; nodeName: String;
begin begin
Result := false; Result := false;
ABorderStyle.LineStyle := lsThin;
ABorderStyle.Color := scBlack;
s := GetAttrValue(ANode, 'style'); s := GetAttrValue(ANode, 'style');
if s = '' then if s = '' then
exit; exit;
ABorderStyle.LineStyle := lsThin;
if s = 'thin' then if s = 'thin' then
ABorderStyle.LineStyle := lsThin ABorderStyle.LineStyle := lsThin
else if s = 'medium' then else if s = 'medium' then
@ -532,23 +543,11 @@ procedure TsSpreadOOXMLReader.ReadBorders(ANode: TDOMNode);
else if s = 'hair' then else if s = 'hair' then
ABorderStyle.LineStyle := lsHair; ABorderStyle.LineStyle := lsHair;
ABorderStyle.Color := scBlack;
colorNode := ANode.FirstChild; colorNode := ANode.FirstChild;
while Assigned(colorNode) do begin while Assigned(colorNode) do begin
nodeName := colorNode.NodeName; nodeName := colorNode.NodeName;
if nodeName = 'color' then begin if nodeName = 'color' then
ABorderStyle.Color := ReadColor(colorNode); ABorderStyle.Color := ReadColor(colorNode);
{
s := GetAttrValue(colorNode, 'rgb');
if s <> '' then
ABorderStyle.Color := FWorkbook.AddColorToPalette(HTMLColorStrToColor('#' + s))
else begin
s := GetAttrValue(colorNode, 'indexed');
if s <> '' then
ABorderStyle.Color := StrToInt(s);
end;
}
end;
colorNode := colorNode.NextSibling; colorNode := colorNode.NextSibling;
end; end;
Result := true; Result := true;
@ -567,6 +566,7 @@ begin
if ANode = nil then if ANode = nil then
exit; exit;
borderStyles := DEFAULT_BORDERSTYLES;
borderNode := ANode.FirstChild; borderNode := ANode.FirstChild;
while Assigned(borderNode) do begin while Assigned(borderNode) do begin
nodeName := borderNode.NodeName; nodeName := borderNode.NodeName;
@ -2548,6 +2548,21 @@ begin
'</c>'); '</c>');
end; end;
{ Writes a boolean value to the stream }
procedure TsSpreadOOXMLWriter.WriteBool(AStream: TStream;
const ARow, ACol: Cardinal; const AValue: Boolean; ACell: PCell);
var
CellPosText: String;
CellValueText: String;
lStyleIndex: Integer;
begin
CellPosText := TsWorksheet.CellPosToText(ARow, ACol);
lStyleIndex := GetStyleIndex(ACell);
if AValue then CellValueText := '1' else CellValueText := '0';
AppendToStream(AStream, Format(
'<c r="%s" s="%d" t="b"><v>%s</v></c>', [CellPosText, lStyleIndex, CellValueText]));
end;
{ Writes a string formula to the given cell. } { Writes a string formula to the given cell. }
procedure TsSpreadOOXMLWriter.WriteFormula(AStream: TStream; procedure TsSpreadOOXMLWriter.WriteFormula(AStream: TStream;
const ARow, ACol: Cardinal; ACell: PCell); const ARow, ACol: Cardinal; ACell: PCell);
@ -2684,9 +2699,6 @@ var
ResultingValue: string; ResultingValue: string;
//S: string; //S: string;
begin begin
Unused(AStream);
Unused(ARow, ACol, ACell);
// Office 2007-2010 (at least) support no more characters in a cell; // Office 2007-2010 (at least) support no more characters in a cell;
if Length(AValue) > MAXBYTES then if Length(AValue) > MAXBYTES then
begin begin
@ -2700,7 +2712,7 @@ begin
if not ValidXMLText(ResultingValue) then if not ValidXMLText(ResultingValue) then
Workbook.AddErrorMsg( Workbook.AddErrorMsg(
'Invalid character(s) in cell %s.', [ rsInvalidCharacterInCell, [
GetCellString(ARow, ACol) GetCellString(ARow, ACol)
]); ]);
@ -2727,7 +2739,6 @@ var
CellValueText: String; CellValueText: String;
lStyleIndex: Integer; lStyleIndex: Integer;
begin begin
Unused(AStream, ACell);
CellPosText := TsWorksheet.CellPosToText(ARow, ACol); CellPosText := TsWorksheet.CellPosToText(ARow, ACol);
lStyleIndex := GetStyleIndex(ACell); lStyleIndex := GetStyleIndex(ACell);
CellValueText := FloatToStr(AValue, FPointSeparatorSettings); CellValueText := FloatToStr(AValue, FPointSeparatorSettings);