You've already forked lazarus-ccr
fpspreadsheet: Add unit xlsEscher for writing Microsoft Office shapes needed for cell comments in BIFF8. Writing of comments not yet working. Lots of additions to BIFFExplorer.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3942 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -37,6 +37,7 @@ object MainForm: TMainForm
|
||||
Font.Color = clBlack
|
||||
Font.Height = -13
|
||||
Font.Name = 'Arial'
|
||||
MouseWheelOption = mwGrid
|
||||
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goRowSizing, goColSizing, goEditing, goThumbTracking, goDblClickAutoSize, goSmoothScroll, goHeaderHotTracking, goCellHints]
|
||||
ParentFont = False
|
||||
RowCount = 101
|
||||
|
@ -8735,9 +8735,12 @@ end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
(Pseudo-) abstract method writing a cell comment to the stream.
|
||||
The cell comment is written immediately after the cell content.
|
||||
NOTE: This is not good for XLSX and BIFF8.
|
||||
|
||||
Must be overridden by descendents.
|
||||
|
||||
@param ACell Pointer to the cell to be written
|
||||
@param ACell Pointer to the cell containing the comment to be written
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsCustomSpreadWriter.WriteComment(AStream: TStream; ACell: PCell);
|
||||
begin
|
||||
|
@ -28,7 +28,7 @@
|
||||
This package is all you need if you don't want graphical components (like grids and charts)."/>
|
||||
<License Value="LGPL with static linking exception. This is the same license as is used in the LCL (Lazarus Component Library)."/>
|
||||
<Version Major="1" Minor="5"/>
|
||||
<Files Count="29">
|
||||
<Files Count="30">
|
||||
<Item1>
|
||||
<Filename Value="fpolestorage.pas"/>
|
||||
<UnitName Value="fpolestorage"/>
|
||||
@ -145,6 +145,10 @@ This package is all you need if you don't want graphical components (like grids
|
||||
<Filename Value="fpstypes.pas"/>
|
||||
<UnitName Value="fpsTypes"/>
|
||||
</Item29>
|
||||
<Item30>
|
||||
<Filename Value="xlsescher.pas"/>
|
||||
<UnitName Value="xlsEscher"/>
|
||||
</Item30>
|
||||
</Files>
|
||||
<RequiredPkgs Count="2">
|
||||
<Item1>
|
||||
|
@ -12,7 +12,7 @@ uses
|
||||
fpsutils, fpszipper, uvirtuallayer_types, uvirtuallayer, uvirtuallayer_ole,
|
||||
uvirtuallayer_ole_helpers, uvirtuallayer_ole_types, uvirtuallayer_stream,
|
||||
fpolebasic, wikitable, fpsNumFormatParser, fpsfunc, fpsRPN, fpsStrings,
|
||||
fpscsv, fpsCsvDocument, fpspatches, fpsTypes;
|
||||
fpscsv, fpsCsvDocument, fpspatches, fpsTypes, xlsEscher;
|
||||
|
||||
implementation
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -191,7 +191,7 @@ begin
|
||||
$0006: Result := 'FORMULA';
|
||||
$0007: Result := 'STRING';
|
||||
$0008: Result := 'ROW';
|
||||
$0009: Result := 'BOF';
|
||||
$0009: Result := 'BOF: Begin of file';
|
||||
$000A: Result := 'EOF: End of file';
|
||||
$000B: Result := 'INDEX';
|
||||
$000C: Result := 'CALCCOUNT: Iteration count';
|
||||
@ -234,7 +234,7 @@ begin
|
||||
$0037: Result := 'DateTable2';
|
||||
$003C: Result := 'CONTINUE: Continues long records';
|
||||
$003D: Result := 'WINDOW1: Window information';
|
||||
$003E: Result := 'WINDOW2';
|
||||
$003E: Result := 'WINDOW2: Window information';
|
||||
$0040: Result := 'BACKUP: Save backup version of the file';
|
||||
$0041: Result := 'PANE: Number of panes and their position';
|
||||
$0042: Result := 'CODEPAGE: Default code page'; // also: CODENAME: VBE object name ???
|
||||
@ -250,7 +250,7 @@ begin
|
||||
$005A: Result := 'CRN: Non-resident operands';
|
||||
$005B: Result := 'FILESHARING: File-sharing information';
|
||||
$005C: Result := 'WRITEACCESS: Write access user name';
|
||||
$005D: Result := 'OBJ';
|
||||
$005D: Result := 'OBJ: Properties of an object in a sheet';
|
||||
$005E: Result := 'UNCALCED: Recalculation status';
|
||||
$005F: Result := 'SAVERECALC: Recalculate before saving';
|
||||
$0060: Result := 'TEMPLATE: Workbook is a template';
|
||||
|
@ -71,9 +71,9 @@ object MainForm: TMainForm
|
||||
Height = 506
|
||||
Top = 0
|
||||
Width = 665
|
||||
ActivePage = PgAnalysis
|
||||
ActivePage = PgValues
|
||||
Align = alClient
|
||||
TabIndex = 0
|
||||
TabIndex = 1
|
||||
TabOrder = 0
|
||||
OnChange = PageControlChange
|
||||
object PgAnalysis: TTabSheet
|
||||
@ -108,12 +108,12 @@ object MainForm: TMainForm
|
||||
end
|
||||
object PgValues: TTabSheet
|
||||
Caption = 'Values'
|
||||
ClientHeight = 463
|
||||
ClientHeight = 478
|
||||
ClientWidth = 657
|
||||
object ValueGrid: TStringGrid
|
||||
Left = 0
|
||||
Height = 158
|
||||
Top = 305
|
||||
Top = 320
|
||||
Width = 657
|
||||
Align = alBottom
|
||||
ColCount = 3
|
||||
@ -144,17 +144,17 @@ object MainForm: TMainForm
|
||||
end
|
||||
object HexPanel: TPanel
|
||||
Left = 0
|
||||
Height = 300
|
||||
Height = 315
|
||||
Top = 0
|
||||
Width = 657
|
||||
Align = alClient
|
||||
Caption = 'HexPanel'
|
||||
ClientHeight = 300
|
||||
ClientHeight = 315
|
||||
ClientWidth = 657
|
||||
TabOrder = 1
|
||||
object HexGrid: TStringGrid
|
||||
Left = 1
|
||||
Height = 298
|
||||
Height = 313
|
||||
Top = 1
|
||||
Width = 373
|
||||
Align = alClient
|
||||
@ -171,22 +171,22 @@ object MainForm: TMainForm
|
||||
OnSelection = HexGridSelection
|
||||
ColWidths = (
|
||||
28
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
24
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
26
|
||||
)
|
||||
Cells = (
|
||||
16
|
||||
@ -242,7 +242,7 @@ object MainForm: TMainForm
|
||||
end
|
||||
object AlphaGrid: TStringGrid
|
||||
Left = 379
|
||||
Height = 298
|
||||
Height = 313
|
||||
Top = 1
|
||||
Width = 277
|
||||
Align = alRight
|
||||
@ -256,22 +256,22 @@ object MainForm: TMainForm
|
||||
OnClick = GridClick
|
||||
OnSelection = AlphaGridSelection
|
||||
ColWidths = (
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
18
|
||||
)
|
||||
Cells = (
|
||||
16
|
||||
@ -327,7 +327,7 @@ object MainForm: TMainForm
|
||||
end
|
||||
object HexDumpSplitter: TSplitter
|
||||
Left = 374
|
||||
Height = 298
|
||||
Height = 313
|
||||
Top = 1
|
||||
Width = 5
|
||||
Align = alRight
|
||||
@ -338,7 +338,7 @@ object MainForm: TMainForm
|
||||
Cursor = crVSplit
|
||||
Left = 0
|
||||
Height = 5
|
||||
Top = 300
|
||||
Top = 315
|
||||
Width = 657
|
||||
Align = alBottom
|
||||
ResizeAnchor = akBottom
|
||||
|
@ -769,22 +769,24 @@ procedure TMainForm.LoadFile(const AFileName: String);
|
||||
var
|
||||
valid: Boolean;
|
||||
excptn: Exception = nil;
|
||||
ext: String;
|
||||
begin
|
||||
if not FileExistsUTF8(AFileName) then begin
|
||||
MessageDlg(Format('File "%s" not found.', [AFileName]), mtError, [mbOK], 0);
|
||||
exit;
|
||||
end;
|
||||
|
||||
if Lowercase(ExtractFileExt(AFileName)) <> '.xls' then begin
|
||||
ext := Lowercase(ExtractFileExt(AFilename));
|
||||
if ext <> '.xls' then begin
|
||||
MessageDlg('BIFFExplorer can only process binary Excel files (extension ".xls")',
|
||||
mtError, [mbOK], 0);
|
||||
exit;
|
||||
end;
|
||||
|
||||
// .xls files can contain several formats. We look into the header first.
|
||||
if Lowercase(ExtractFileExt(AFileName))=STR_EXCEL_EXTENSION then
|
||||
if ext = STR_EXCEL_EXTENSION then
|
||||
begin
|
||||
valid := GetFormatFromFileHeader(AFileName, FFormat);
|
||||
valid := GetFormatFromFileHeader(UTF8ToAnsi(AFileName), FFormat);
|
||||
// It is possible that valid xls files are not detected correctly. Therefore,
|
||||
// we open them explicitly by trial and error - see below.
|
||||
if not valid then
|
||||
@ -853,7 +855,7 @@ begin
|
||||
|
||||
// Rewind the stream and read from it
|
||||
MemStream.Position := 0;
|
||||
FFileName := ExpandFileName(UTF8ToSys(AFileName));
|
||||
FFileName := ExpandFileName(AFileName);
|
||||
ReadFromStream(MemStream);
|
||||
|
||||
FFormat := AFormat;
|
||||
|
@ -117,10 +117,16 @@ type
|
||||
{ TsSpreadBIFF8Writer }
|
||||
|
||||
TsSpreadBIFF8Writer = class(TsSpreadBIFFWriter)
|
||||
private
|
||||
FCommentList: TFPList;
|
||||
procedure WriteCommentsCallback(ACell: PCell; AStream: TStream);
|
||||
|
||||
protected
|
||||
{ Record writing methods }
|
||||
procedure WriteBOF(AStream: TStream; ADataType: Word);
|
||||
function WriteBoundsheet(AStream: TStream; ASheetName: string): Int64;
|
||||
procedure WriteComment(AStream: TStream; ACell: PCell); override;
|
||||
procedure WriteComments(AStream: TStream; AWorksheet: TsWorksheet);
|
||||
procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
|
||||
procedure WriteEOF(AStream: TStream);
|
||||
procedure WriteFont(AStream: TStream; AFont: TsFont);
|
||||
@ -129,8 +135,14 @@ type
|
||||
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
||||
const AValue: string; ACell: PCell); override;
|
||||
procedure WriteMergedCells(AStream: TStream; AWorksheet: TsWorksheet);
|
||||
procedure WriteMSODrawing1(AStream: TStream; ANumShapes: Word; ACell: PCell);
|
||||
procedure WriteMSODrawing2(AStream: TStream; ACell: PCell; AObjID: Word);
|
||||
procedure WriteMSODrawing2_Data(AStream: TStream; ACell: PCell; AShapeID: Word);
|
||||
procedure WriteMSODrawing3(AStream: TStream; ACell: PCell);
|
||||
procedure WriteNOTE(AStream: TStream; ACell: PCell; AObjID: Word);
|
||||
procedure WriteNumFormat(AStream: TStream; AFormatData: TsNumFormatData;
|
||||
AListIndex: Integer); override;
|
||||
procedure WriteOBJ(AStream: TStream; AObjID: Word);
|
||||
function WriteRPNCellAddress(AStream: TStream; ARow, ACol: Cardinal;
|
||||
AFlags: TsRelFlags): word; override;
|
||||
function WriteRPNCellOffset(AStream: TStream; ARowOffset, AColOffset: Integer;
|
||||
@ -139,8 +151,9 @@ type
|
||||
AFlags: TsRelFlags): Word; override;
|
||||
function WriteString_8bitLen(AStream: TStream; AString: String): Integer; override;
|
||||
procedure WriteStringRecord(AStream: TStream; AString: string); override;
|
||||
procedure WriteStyle(AStream: TStream);
|
||||
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
|
||||
procedure WriteSTYLE(AStream: TStream);
|
||||
procedure WriteTXO(AStream: TStream; ACell: PCell);
|
||||
procedure WriteWINDOW2(AStream: TStream; ASheet: TsWorksheet);
|
||||
procedure WriteXF(AStream: TStream; AFormatRecord: PsCellFormat;
|
||||
XFType_Prot: Byte = 0); override;
|
||||
public
|
||||
@ -227,11 +240,13 @@ var
|
||||
implementation
|
||||
|
||||
uses
|
||||
Math, lconvencoding, fpsStrings, fpsStreams, fpsExprParser;
|
||||
Math, lconvencoding,
|
||||
fpsStrings, fpsStreams, fpsExprParser, xlsEscher;
|
||||
|
||||
const
|
||||
{ Excel record IDs }
|
||||
INT_EXCEL_ID_MERGEDCELLS = $00E5; // BIFF8 only
|
||||
INT_EXCEL_ID_MSODRAWING = $00EC; // BIFF8 only
|
||||
INT_EXCEL_ID_SST = $00FC; // BIFF8 only
|
||||
INT_EXCEL_ID_LABELSST = $00FD; // BIFF8 only
|
||||
INT_EXCEL_ID_TXO = $01B6; // BIFF8 only
|
||||
@ -312,6 +327,8 @@ const
|
||||
XF_ROTATION_STACKED
|
||||
);
|
||||
|
||||
SHAPEID_BASE = 1024;
|
||||
|
||||
type
|
||||
TBIFF8_DimensionsRecord = packed record
|
||||
RecordID: Word;
|
||||
@ -357,11 +374,26 @@ type
|
||||
BkGr3: Word;
|
||||
end;
|
||||
|
||||
TBIFF8TXORecord = packed record
|
||||
RecordID: Word;
|
||||
RecordSize: Word;
|
||||
OptionFlags: Word;
|
||||
TextRot: Word;
|
||||
Reserved1: Word;
|
||||
Reserved2: Word;
|
||||
Reserved3: Word;
|
||||
TextLen: Word;
|
||||
NumFormattingRuns: Word;
|
||||
Reserved4: Word;
|
||||
Reserved5: Word;
|
||||
end;
|
||||
|
||||
TBIFF8Comment = class
|
||||
ID: Integer;
|
||||
Text: String;
|
||||
end;
|
||||
|
||||
|
||||
{ TsSpreadBIFF8Reader }
|
||||
|
||||
destructor TsSpreadBIFF8Reader.Destroy;
|
||||
@ -1104,23 +1136,11 @@ end;
|
||||
We only extract the length of the comment text (in characters). The text itself
|
||||
is contained in the following CONTINUE record. }
|
||||
procedure TsSpreadBIFF8Reader.ReadTXO(const AStream: TStream);
|
||||
type
|
||||
TBIFF8TXORecord = packed record
|
||||
OptionFlags: Word;
|
||||
TextRot: Word;
|
||||
Reserved1: Word;
|
||||
Reserved2: Word;
|
||||
Reserved3: Word;
|
||||
TextLen: Word;
|
||||
NumFormattingRuns: Word;
|
||||
Reserved4: Word;
|
||||
Reserved5: Word;
|
||||
end;
|
||||
var
|
||||
rec: TBIFF8TXORecord;
|
||||
begin
|
||||
rec.OptionFlags := 0; // to silence the compiler
|
||||
AStream.ReadBuffer(rec, Sizeof(Rec));
|
||||
AStream.ReadBuffer(rec.OptionFlags, Sizeof(Rec) - 2*SizeOf(Word));
|
||||
FCommentLen := WordLEToN(rec.TextLen);
|
||||
end;
|
||||
|
||||
@ -1462,6 +1482,7 @@ begin
|
||||
else begin
|
||||
WriteRows(AStream, FWorksheet);
|
||||
WriteCellsToStream(AStream, FWorksheet.Cells);
|
||||
WriteComments(AStream, FWorksheet);
|
||||
end;
|
||||
|
||||
// View settings block
|
||||
@ -1490,8 +1511,7 @@ end;
|
||||
procedure TsSpreadBIFF8Writer.WriteBOF(AStream: TStream; ADataType: Word);
|
||||
begin
|
||||
{ BIFF Record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_BOF));
|
||||
AStream.WriteWord(WordToLE(16)); //total record size
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_BOF, 16);
|
||||
|
||||
{ BIFF version. Should only be used if this BOF is for the workbook globals }
|
||||
{ OpenOffice rejects to correctly read xls files if this field is
|
||||
@ -1515,19 +1535,14 @@ begin
|
||||
AStream.WriteDWord(DWordToLE(0)); //?????????
|
||||
end;
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF8Writer.WriteBoundsheet ()
|
||||
*
|
||||
* DESCRIPTION: Writes an Excel 8 BOUNDSHEET record
|
||||
*
|
||||
* Always located on the workbook globals substream.
|
||||
*
|
||||
* One BOUNDSHEET is written for each worksheet.
|
||||
*
|
||||
* RETURNS: The stream position where the absolute stream position
|
||||
* of the BOF of this sheet should be written (4 bytes size).
|
||||
*
|
||||
*******************************************************************}
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an Excel 8 BOUNDSHEET record
|
||||
Always located in the workbook globals substream.
|
||||
One BOUNDSHEET is written for each worksheet.
|
||||
|
||||
@return The stream position where the absolute stream position
|
||||
of the BOF of this sheet should be written (4 bytes size).
|
||||
-------------------------------------------------------------------------------}
|
||||
function TsSpreadBIFF8Writer.WriteBoundsheet(AStream: TStream; ASheetName: string): Int64;
|
||||
var
|
||||
Len: Byte;
|
||||
@ -1537,8 +1552,7 @@ begin
|
||||
Len := Length(WideSheetName);
|
||||
|
||||
{ BIFF Record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_BOUNDSHEET));
|
||||
AStream.WriteWord(WordToLE(6 + 1 + 1 + Len * Sizeof(WideChar)));
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_BOUNDSHEET, 8 + Len * Sizeof(WideChar));
|
||||
|
||||
{ Absolute stream position of the BOF record of the sheet represented
|
||||
by this record }
|
||||
@ -1558,6 +1572,63 @@ begin
|
||||
AStream.WriteBuffer(WideStringToLE(WideSheetName)[1], Len * Sizeof(WideChar));
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Inherited method for writing a cell comment immediately after cell content.
|
||||
A writing method has been implemented by xlscommon. But in BIFF8, this
|
||||
must not do anything because comments are collected in a list and
|
||||
written en-bloc. See WriteComments.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFF8Writer.WriteComment(AStream: TStream; ACell: PCell);
|
||||
begin
|
||||
// Nothing to do. Reverts the behavior introduced by xlscommon.
|
||||
Unused(AStream, ACell);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes all comments to the worksheet stream
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFF8Writer.WriteComments(AStream: TStream;
|
||||
AWorksheet: TsWorksheet);
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
exit; // Remove after comments can be written correctly
|
||||
{$warning TODO: Fix writing of cell comments in BIFF8 (file is readable by OpenOffice, but not by Excel)}
|
||||
|
||||
FCommentList := TFPList.Create;
|
||||
try
|
||||
IterateThroughCells(AStream, AWorksheet.Cells, WriteCommentsCallback);
|
||||
if FCommentList.Count = 0 then
|
||||
exit;
|
||||
|
||||
for i:=0 to FCommentList.Count-1 do begin
|
||||
if i = 0 then
|
||||
WriteMSODRAWING1(AStream, FCommentList.Count, PCell(FCommentList[i]))
|
||||
else
|
||||
WriteMSODRAWING2(AStream, PCell(FCommentList[i]), i+1);
|
||||
WriteOBJ(AStream, i+1);
|
||||
WriteMSODRAWING3(AStream, PCell(FCommentList[i]));
|
||||
WriteTXO(AStream, PCell(FCommentList[i]));
|
||||
end;
|
||||
|
||||
for i:=0 to FCommentList.Count-1 do
|
||||
WriteNOTE(AStream, PCell(FCommentList[i]), i+1);
|
||||
finally
|
||||
FreeAndNil(FCommentList);
|
||||
end;
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Helper method which stores the pointer to a cell in the FCommentsList if the
|
||||
cell contains a comment
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFF8Writer.WriteCommentsCallback(ACell: PCell;
|
||||
AStream: TStream);
|
||||
begin
|
||||
Unused(AStream);
|
||||
if (ACell <> nil) and (ACell^.Comment <> '') then
|
||||
FCommentList.Add(ACell);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an Excel 8 DIMENSIONS record
|
||||
@ -1590,29 +1661,20 @@ begin
|
||||
AStream.WriteBuffer(rec, SizeOf(rec));
|
||||
end;
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF8Writer.WriteEOF ()
|
||||
*
|
||||
* DESCRIPTION: Writes an Excel 8 EOF record
|
||||
*
|
||||
* This must be the last record on an Excel 8 stream
|
||||
*
|
||||
*******************************************************************}
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an Excel 8 EOF record.
|
||||
This must be the last record on an Excel 8 stream
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFF8Writer.WriteEOF(AStream: TStream);
|
||||
begin
|
||||
{ BIFF Record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_EOF));
|
||||
AStream.WriteWord(WordToLE($0000));
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_EOF, 0);
|
||||
end;
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF8Writer.WriteFont ()
|
||||
*
|
||||
* DESCRIPTION: Writes an Excel 8 FONT record
|
||||
*
|
||||
* The font data is passed in an instance of TsFont
|
||||
*
|
||||
*******************************************************************}
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an Excel 8 FONT record.
|
||||
The font data is passed as an instance of TsFont
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFF8Writer.WriteFont(AStream: TStream; AFont: TsFont);
|
||||
var
|
||||
Len: Byte;
|
||||
@ -1631,8 +1693,7 @@ begin
|
||||
Len := Length(WideFontName);
|
||||
|
||||
{ BIFF Record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FONT));
|
||||
AStream.WriteWord(WordToLE(14 + 1 + 1 + Len * Sizeof(WideChar)));
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_FONT, 16 + Len * Sizeof(WideChar));
|
||||
|
||||
{ Height of the font in twips = 1/20 of a point }
|
||||
AStream.WriteWord(WordToLE(round(AFont.Size*20)));
|
||||
@ -1691,7 +1752,179 @@ var
|
||||
i: Integer;
|
||||
begin
|
||||
for i:=0 to Workbook.GetFontCount-1 do
|
||||
WriteFont(AStream, Workbook.GetFont(i));
|
||||
WriteFONT(AStream, Workbook.GetFont(i));
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes the first MSODRAWING record to file. It is needed for a comment
|
||||
attached to a cell, but also for embedded shapes (currently not supported).
|
||||
|
||||
<pre>
|
||||
Structure of this record:
|
||||
Type Ver Inst
|
||||
Dg container $F002 0
|
||||
|--- FDG record $F008 0 1
|
||||
|--- SpGr container $F003 0
|
||||
|---- Sp container (group shape) $F004 0
|
||||
| |---- FSpGr record $F009 1 0
|
||||
MSODRAWING1 | |---- FSp record $F00A 2 0
|
||||
................................................................................
|
||||
MSODRAWING2 |---- Sp container (child shape) $F004 0
|
||||
|---- FSp record $F00A 2 202 (Textbox)
|
||||
|---- FOpt record $F00B 3 13 (num props)
|
||||
|---- Client anchor record $F010 0 0
|
||||
|---- Client data record $F011 0 0
|
||||
</pre>
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBiff8Writer.WriteMSODrawing1(AStream: TStream; ANumShapes: Word;
|
||||
ACell: PCell);
|
||||
const
|
||||
DRAWING_ID = 1;
|
||||
var
|
||||
len: DWord;
|
||||
tmpStream: TMemoryStream;
|
||||
begin
|
||||
tmpStream := TMemoryStream.Create;
|
||||
try
|
||||
{ OfficeArtDgContainer record (container of drawing) }
|
||||
len := 224 + 152*(ANumShapes - 1);
|
||||
WriteMSODgContainer(tmpStream, len);
|
||||
|
||||
{ OfficeArtFdg record (info on shapes: num shapes, drawing ID, last Obj ID ) }
|
||||
WriteMSOFdgRecord(tmpStream, ANumShapes + 1, DRAWING_ID, SHAPEID_BASE + ANumShapes);
|
||||
|
||||
{ OfficeArtSpGrContainer record (shape group container) }
|
||||
len := 200 + 152*(ANumShapes - 1);
|
||||
WriteMSOSpGrContainer(tmpStream, len);
|
||||
|
||||
{ OfficeArtSpContainer record }
|
||||
WriteMSOSpContainer(tmpStream, 40);
|
||||
|
||||
{ OfficeArtFSpGr record }
|
||||
WriteMSOFSpGrRecord(tmpStream, 0, 0, 0, 0); // 16 + 8 bytes
|
||||
|
||||
{ OfficeArtFSp record }
|
||||
WriteMSOFSpRecord(tmpStream, SHAPEID_BASE, MSO_SPT_NOTPRIMITIVE,
|
||||
MSO_FSP_BITS_GROUP + MSO_FSP_BITS_PATRIARCH); // 8 + 8 bytes
|
||||
|
||||
{ Data for the 1st comment }
|
||||
WriteMSODrawing2_Data(tmpStream, ACell, SHAPEID_BASE + 1);
|
||||
|
||||
// Write the BIFF stream
|
||||
tmpStream.Position := 0;
|
||||
len := tmpStream.Size;
|
||||
WriteBiffHeader(AStream, INT_EXCEL_ID_MSODRAWING, tmpStream.Size);
|
||||
AStream.CopyFrom(tmpStream, tmpStream.Size);
|
||||
finally
|
||||
tmpStream.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Write the MSODRAWING record which occurs before the OBJ record.
|
||||
Do not use for the very first OBJ record where the record must be
|
||||
WriteMSODrawing1 + WriteMSODrawing2_Data + WriteMSODrawing3_Data}
|
||||
procedure TsSpreadBiff8Writer.WriteMSODrawing2(AStream: TStream; ACell: PCell;
|
||||
AObjID: Word);
|
||||
var
|
||||
tmpStream: TStream;
|
||||
len: Word;
|
||||
begin
|
||||
tmpStream := TMemoryStream.Create;
|
||||
try
|
||||
{ Shape data for cell comment }
|
||||
WriteMSODrawing2_Data(tmpStream, ACell, SHAPEID_BASE + AObjID);
|
||||
|
||||
{ Get size of data stream }
|
||||
len := tmpStream.Size;
|
||||
|
||||
{ BIFF Header }
|
||||
WriteBiffHeader(AStream, INT_EXCEL_ID_MSODRAWING, len);
|
||||
|
||||
{ Copy MSO data to BIFF stream }
|
||||
tmpStream.Position := 0;
|
||||
AStream.CopyFrom(tmpStream, len);
|
||||
finally
|
||||
tmpStream.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TsSpreadBiff8Writer.WriteMSODrawing2_Data(AStream: TStream;
|
||||
ACell: PCell; AShapeID: Word);
|
||||
var
|
||||
tmpStream: TStream;
|
||||
len: Cardinal;
|
||||
begin
|
||||
// We write all the record data to a temporary stream to get the record
|
||||
// size (it depends on the number of properties written to the FOPT record.
|
||||
// The record size is needed in the very first SpContainer record...
|
||||
|
||||
tmpStream := TMemoryStream.Create;
|
||||
try
|
||||
{ OfficeArtFSp record }
|
||||
WriteMSOFSpRecord(tmpStream, AShapeID, MSO_SPT_TEXTBOX,
|
||||
MSO_FSP_BITS_HASANCHOR + MSO_FSP_BITS_HASSHAPETYPE);
|
||||
|
||||
{ OfficeArtFOpt record }
|
||||
WriteMSOFOptRecord_Comment(tmpStream);
|
||||
|
||||
{ OfficeArtClientAnchor record }
|
||||
WriteMSOClientAnchorSheetRecord(tmpStream,
|
||||
ACell^.Row + 1, ACell^.Col + 1, ACell.Row + 3, ACell^.Col + 5,
|
||||
691, 486, 38, 26,
|
||||
true, true
|
||||
);
|
||||
|
||||
{ OfficeArtClientData record }
|
||||
WriteMSOClientDataRecord(tmpStream);
|
||||
|
||||
// Now we know the record size
|
||||
len := tmpStream.Size;
|
||||
|
||||
// Write an OfficeArtSpContainer to the stream provided...
|
||||
WriteMSOSpContainer(AStream, len+8); // !!! for some reason, Excel wants here 8 additional bytes !!!
|
||||
|
||||
// ... and append the data from the temporary stream.
|
||||
tmpStream.Position := 0;
|
||||
AStream.Copyfrom(tmpStream, len);
|
||||
|
||||
finally
|
||||
tmpStream.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Writes the MSODRAWING record which must occur immediately before a TXO record }
|
||||
procedure TsSpreadBiff8Writer.WriteMSODRAWING3(AStream: TStream; ACell: PCell);
|
||||
begin
|
||||
{ BIFF Header }
|
||||
WriteBiffHeader(AStream, INT_EXCEL_ID_MSODRAWING, 8);
|
||||
|
||||
{ OfficeArtClientTextbox record: Text-related data for a shape }
|
||||
WriteMSOClientTextBoxRecord(AStream);
|
||||
end;
|
||||
|
||||
{ Writes a NOTE record for a comment attached to a cell }
|
||||
procedure TsSpreadBiff8Writer.WriteNOTE(AStream: TStream; ACell: PCell;
|
||||
AObjID: Word);
|
||||
const
|
||||
AUTHOR: ansistring = 'Werner';
|
||||
var
|
||||
len: Integer;
|
||||
begin
|
||||
len := Length(AUTHOR) * sizeOf(ansichar);
|
||||
|
||||
{ BIFF Header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_NOTE)); // ID of NOTE record
|
||||
AStream.WriteWord(WordToLE(12+len)); // Size of NOTE record
|
||||
|
||||
{ Record data }
|
||||
AStream.WriteWord(WordToLE(ACell^.Row)); // Row index of cell
|
||||
AStream.WriteWord(WordToLE(ACell^.Col)); // Column index of cell
|
||||
AStream.WriteWord(0); // Flags
|
||||
AStream.WriteWord(WordToLE(AObjID)); // Object identifier (1, ...)
|
||||
AStream.WriteWord(len); // Char length of author string
|
||||
AStream.WriteByte(0); // Flag for 8-bit characters
|
||||
AStream.WriteBuffer(AUTHOR[1], len); // Author
|
||||
AStream.WriteByte(0); // Unused
|
||||
end;
|
||||
|
||||
procedure TsSpreadBiff8Writer.WriteNumFormat(AStream: TStream;
|
||||
@ -1742,6 +1975,35 @@ begin
|
||||
SetLength(buf, 0);
|
||||
end;
|
||||
|
||||
{ Writes an OBJ record - belongs to the record required for cell comments }
|
||||
procedure TsSpreadBIFF8Writer.WriteOBJ(AStream: TStream; AObjID: Word);
|
||||
var
|
||||
guid: TGuid;
|
||||
begin
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_OBJ));
|
||||
AStream.WriteWord(WordToLE(52));
|
||||
|
||||
AStream.WriteWord(WordToLE($0015)); // Subrecord ftCmo
|
||||
AStream.WriteWord(WordToLE(18)); // Subrecord size: 18 bytes
|
||||
AStream.WriteWord(WordToLE($0019)); // Object type: Comment
|
||||
AStream.WriteWord(WordToLE(AObjID)); // Object ID number (1, ... )
|
||||
AStream.WriteWord(WordToLE($4011)); // Option flags automatic line style, locked when sheet is protected
|
||||
AStream.WriteDWord(0); // Unused
|
||||
AStream.WriteDWord(0); // Unused
|
||||
AStream.WriteDWord(0); // Unused
|
||||
|
||||
AStream.WriteWord(WordToLE($000D)); // Subrecord ftNts
|
||||
AStream.WriteWord(WordToLE(22)); // Size of subrecord: 22 bytes
|
||||
// CreateGUID(guid);
|
||||
FillChar(guid{%H-}, SizeOf(guid), 0);
|
||||
AStream.WriteBuffer(guid, 16); // GUID of comment
|
||||
AStream.WriteWord(WordToLE(0)); // shared note (0 = false)
|
||||
AStream.WriteDWord(0); // unused
|
||||
|
||||
AStream.WriteWord(WordToLE($0000)); // Subrecord ftEnd
|
||||
AStream.WriteWord(0); // Size of subrecord: 0 bytes
|
||||
end;
|
||||
|
||||
{ Writes the address of a cell as used in an RPN formula and returns the
|
||||
number of bytes written. }
|
||||
function TsSpreadBIFF8Writer.WriteRPNCellAddress(AStream: TStream;
|
||||
@ -1840,19 +2102,15 @@ begin
|
||||
AStream.WriteBuffer(WideStringToLE(wideStr)[1], len * SizeOf(WideChar));
|
||||
end;
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF8Writer.WriteIndex ()
|
||||
*
|
||||
* DESCRIPTION: Writes an Excel 8 INDEX record
|
||||
*
|
||||
* nm = (rl - rf - 1) / 32 + 1 (using integer division)
|
||||
*
|
||||
*******************************************************************}
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an Excel 8 INDEX record
|
||||
|
||||
nm = (rl - rf - 1) / 32 + 1 (using integer division)
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFF8Writer.WriteIndex(AStream: TStream);
|
||||
begin
|
||||
{ BIFF Record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_INDEX));
|
||||
AStream.WriteWord(WordToLE(16));
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_INDEX, 16);
|
||||
|
||||
{ Not used }
|
||||
AStream.WriteDWord(DWordToLE(0));
|
||||
@ -1872,16 +2130,12 @@ begin
|
||||
{ OBS: It seems to be no problem just ignoring this part of the record }
|
||||
end;
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF8Writer.WriteLabel ()
|
||||
*
|
||||
* DESCRIPTION: Writes an Excel 8 LABEL record
|
||||
*
|
||||
* Writes a string to the sheet
|
||||
* If the string length exceeds 32758 bytes, the string
|
||||
* will be silently truncated.
|
||||
*
|
||||
*******************************************************************}
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an Excel 8 LABEL record (string cell value)
|
||||
|
||||
If the string length exceeds 32758 bytes, the string will be truncated,
|
||||
a note will be left in the workbooks log.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFF8Writer.WriteLabel(AStream: TStream; const ARow,
|
||||
ACol: Cardinal; const AValue: string; ACell: PCell);
|
||||
const
|
||||
@ -1963,8 +2217,7 @@ begin
|
||||
// at most 1026 merged ranges per BIFF record, the rest goes into a new record
|
||||
|
||||
{ BIFF record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_MERGEDCELLS));
|
||||
AStream.WriteWord(WordToLE(2 + n*8));
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_MERGEDCELLS, 2 + n*8);
|
||||
|
||||
// Count of cell ranges in this record
|
||||
AStream.WriteWord(WordToLE(n));
|
||||
@ -1983,20 +2236,16 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF8Writer.WriteStyle ()
|
||||
*
|
||||
* DESCRIPTION: Writes an Excel 8 STYLE record
|
||||
*
|
||||
* Registers the name of a user-defined style or
|
||||
* specific options for a built-in cell style.
|
||||
*
|
||||
*******************************************************************}
|
||||
{@@-----------------------------------------------------------------------------
|
||||
Writes an Excel 8 STYLE record
|
||||
|
||||
Registers the name of a user-defined style or specific options
|
||||
for a built-in cell style.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFF8Writer.WriteStyle(AStream: TStream);
|
||||
begin
|
||||
{ BIFF record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_STYLE));
|
||||
AStream.WriteWord(WordToLE(4));
|
||||
WriteBiffHeader(AStream, INT_EXCEL_ID_STYLE, 4);
|
||||
|
||||
{ Index to style XF and defines if it's a built-in or used defined style }
|
||||
AStream.WriteWord(WordToLE(MASK_STYLE_BUILT_IN));
|
||||
@ -2008,27 +2257,83 @@ begin
|
||||
AStream.WriteByte($FF);
|
||||
end;
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF8Writer.WriteWindow2 ()
|
||||
*
|
||||
* DESCRIPTION: Writes an Excel 8 WINDOW2 record
|
||||
*
|
||||
* This record contains aditional settings for the
|
||||
* document window (BIFF2-BIFF4) or for a specific
|
||||
* worksheet (BIFF5-BIFF8).
|
||||
*
|
||||
* The values written here are reasonable defaults,
|
||||
* which should work for most sheets.
|
||||
*
|
||||
*******************************************************************}
|
||||
procedure TsSpreadBIFF8Writer.WriteWindow2(AStream: TStream;
|
||||
ASheet: TsWorksheet);
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes a TXO and two CONTINUE records as needed for cell comments.
|
||||
It can safely be assumed that the cell exists and contains a comment.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFF8Writer.WriteTXO(AStream: TStream; ACell: PCell);
|
||||
var
|
||||
recTXO: TBIFF8TXORecord;
|
||||
comment: widestring;
|
||||
compressed: ansistring;
|
||||
len: Integer;
|
||||
wchar: widechar;
|
||||
i: Integer;
|
||||
bytesFmtRuns: Integer;
|
||||
begin
|
||||
{ Prepare comment string. It is stored as a string with 8-bit characters }
|
||||
comment := UTF8Decode(ACell^.Comment);
|
||||
SetLength(compressed, length(comment));
|
||||
for i:= 1 to Length(comment) do
|
||||
begin
|
||||
wchar := comment[i];
|
||||
compressed[i] := wchar;
|
||||
end;
|
||||
len := Length(compressed);
|
||||
|
||||
{ (1) TXO record ---------------------------------------------------------- }
|
||||
{ BIFF record header }
|
||||
FillChar(recTXO{%H-}, SizeOf(recTXO), 0);
|
||||
recTXO.RecordID := WordToLE(INT_EXCEL_ID_TXO);
|
||||
recTXO.RecordSize := SizeOf(recTXO) - 2*SizeOf(word);
|
||||
{ Record data }
|
||||
recTXO.OptionFlags := WordToLE($0212); // Left & top aligned, lock option on
|
||||
recTXO.TextRot := 0; // Comment text not rotated
|
||||
recTXO.TextLen := WordToLE(len);
|
||||
bytesFmtRuns := 8*SizeOf(Word); // see (3) below
|
||||
recTXO.NumFormattingRuns := WordToLE(bytesFmtRuns);
|
||||
{ Write out to file }
|
||||
AStream.WriteBuffer(recTXO, SizeOf(recTXO));
|
||||
|
||||
{ (2) 1st CONTINUE record containing the comment text --------------------- }
|
||||
{ BIFF record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_CONTINUE));
|
||||
AStream.WriteWord(len+1);
|
||||
{ Record data }
|
||||
AStream.WriteByte(0);
|
||||
AStream.WriteBuffer(compressed[1], len);
|
||||
|
||||
{ (3) 2nd CONTINUE record containing the formatting runs ------------------ }
|
||||
{ BIFF record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_CONTINUE));
|
||||
AStream.WriteWord(bytesFmtRuns);
|
||||
{ Record data }
|
||||
AStream.WriteWord(0); // start index of 1st formatting run (we only use 1 run)
|
||||
AStream.WriteWord(WordToLE(1)); // Font index to be used (default font)
|
||||
AStream.WriteWord(0); // Not used
|
||||
AStream.WriteWord(0); // Not used
|
||||
AStream.WriteWord(WordToLE(len)); // lastRun: number of characters
|
||||
AStream.WriteWord(0); // Not used
|
||||
AStream.WriteWord(0); // Not used
|
||||
AStream.WriteWord(0); // Not used
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an Excel 8 WINDOW2 record
|
||||
|
||||
This record contains additional settings for the document window (BIFF2-BIFF4)
|
||||
or for a specific worksheet (BIFF5-BIFF8).
|
||||
|
||||
The values written here are reasonable defaults, which should work for most
|
||||
sheets.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFF8Writer.WriteWINDOW2(AStream: TStream;
|
||||
ASheet: TsWorksheet);
|
||||
var
|
||||
Options: Word;
|
||||
begin
|
||||
{ BIFF Record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_WINDOW2));
|
||||
AStream.WriteWord(WordToLE(18));
|
||||
WriteBiffHeader(AStream, INT_EXCEL_ID_WINDOW2, 18);
|
||||
|
||||
{ Options flags }
|
||||
Options :=
|
||||
@ -2069,14 +2374,9 @@ begin
|
||||
AStream.WriteDWord(DWordToLE(0));
|
||||
end;
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF8Writer.WriteXF ()
|
||||
*
|
||||
* DESCRIPTION: Writes an Excel 8 XF record
|
||||
*
|
||||
*
|
||||
*
|
||||
*******************************************************************}
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an Excel 8 XF record (cell format)
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFF8Writer.WriteXF(AStream: TStream;
|
||||
AFormatRecord: PsCellFormat; XFType_Prot: Byte = 0);
|
||||
var
|
||||
@ -2087,7 +2387,7 @@ var
|
||||
begin
|
||||
{ BIFF record header }
|
||||
rec.RecordID := WordToLE(INT_EXCEL_ID_XF);
|
||||
rec.RecordSize := WordToLE(SizeOf(TBIFF8_XFRecord) - 2*SizeOf(Word));
|
||||
rec.RecordSize := WordToLE(SizeOf(TBIFF8_XFRecord) - SizeOf(TsBIFFHeader));
|
||||
|
||||
{ Index to font record }
|
||||
rec.FontIndex := 0;
|
||||
@ -2216,13 +2516,12 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
{*******************************************************************
|
||||
* Initialization section
|
||||
*
|
||||
* Registers this reader / writer on fpSpreadsheet
|
||||
* Converts the palette to litte-endian
|
||||
*
|
||||
*******************************************************************}
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Initialization section
|
||||
|
||||
Registers this reader / writer on fpSpreadsheet
|
||||
Converts the palette to litte-endian
|
||||
-------------------------------------------------------------------------------}
|
||||
|
||||
initialization
|
||||
|
||||
|
@ -204,6 +204,12 @@ type
|
||||
function ConvertToExcelError(AValue: TsErrorValue): byte;
|
||||
|
||||
type
|
||||
{ TsBIFFHeader }
|
||||
TsBIFFHeader = packed record
|
||||
RecordID: Word;
|
||||
RecordSize: Word;
|
||||
end;
|
||||
|
||||
{ TsBIFFNumFormatList }
|
||||
TsBIFFNumFormatList = class(TsCustomNumFormatList)
|
||||
protected
|
||||
@ -311,6 +317,9 @@ type
|
||||
function GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
|
||||
procedure GetLastColCallback(ACell: PCell; AStream: TStream);
|
||||
function GetLastColIndex(AWorksheet: TsWorksheet): Word;
|
||||
|
||||
// Helper function for writing the BIFF header
|
||||
procedure WriteBIFFHeader(AStream: TStream; ARecID, ARecSize: Word);
|
||||
// Helper function for writing a string with 8-bit length }
|
||||
function WriteString_8BitLen(AStream: TStream; AString: String): Integer; virtual;
|
||||
|
||||
@ -1817,6 +1826,24 @@ begin
|
||||
Result := FLastCol;
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes the BIFF record header consisting of the record ID and the size of
|
||||
data to be written immediately afterwards.
|
||||
|
||||
@param ARecID ID of the record - see the INT_EXCEL_ID_XXXX constants
|
||||
@param ARedSize Size (in bytes) of the data which follow immediately
|
||||
afterwards
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFFWriter.WriteBIFFHeader(AStream: TStream;
|
||||
ARecID, ARecSize: Word);
|
||||
var
|
||||
rec: TsBIFFHeader;
|
||||
begin
|
||||
rec.RecordID := WordToLE(ARecID);
|
||||
rec.RecordSize := WordToLE(ARecSize);
|
||||
AStream.WriteBuffer(rec, SizeOf(rec));
|
||||
end;
|
||||
|
||||
{ Writes an empty ("blank") cell. Needed for formatting empty cells.
|
||||
Valid for BIFF5 and BIFF8. Needs to be overridden for BIFF2 which has a
|
||||
different record structure. }
|
||||
@ -1844,7 +1871,7 @@ begin
|
||||
end;
|
||||
|
||||
{ Writes a BOOLEAN cell record.
|
||||
Valie for BIFF3-BIFF8. Override for BIFF2. }
|
||||
Valid for BIFF3-BIFF8. Override for BIFF2. }
|
||||
procedure TsSpreadBIFFWriter.WriteBool(AStream: TStream;
|
||||
const ARow, ACol: Cardinal; const AValue: Boolean; ACell: PCell);
|
||||
var
|
||||
@ -1883,8 +1910,7 @@ var
|
||||
cp: Word;
|
||||
begin
|
||||
{ BIFF Record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_CODEPAGE));
|
||||
AStream.WriteWord(WordToLE(2));
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_CODEPAGE, 2);
|
||||
|
||||
{ Codepage }
|
||||
FCodePage := lowercase(ACodePage);
|
||||
@ -2028,8 +2054,7 @@ procedure TsSpreadBIFFWriter.WriteDateMode(AStream: TStream);
|
||||
begin
|
||||
{ BIFF Record header }
|
||||
// todo: check whether this is in the right place. should end up in workbook globals stream
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_DATEMODE));
|
||||
AStream.WriteWord(WordToLE(2));
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_DATEMODE, 2);
|
||||
|
||||
case FDateMode of
|
||||
dm1900: AStream.WriteWord(WordToLE(0));
|
||||
@ -2155,22 +2180,23 @@ begin
|
||||
end;
|
||||
|
||||
procedure TsSpreadBIFFWriter.WritePalette(AStream: TStream);
|
||||
const
|
||||
NUM_COLORS = 56;
|
||||
var
|
||||
i, n: Integer;
|
||||
rgb: TsColorValue;
|
||||
begin
|
||||
{ BIFF Record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_PALETTE));
|
||||
AStream.WriteWord(WordToLE(2 + 4*56));
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_PALETTE, 2 + 4*NUM_COLORS);
|
||||
|
||||
{ Number of colors }
|
||||
AStream.WriteWord(WordToLE(56));
|
||||
AStream.WriteWord(WordToLE(NUM_COLORS));
|
||||
|
||||
{ Take the colors from the palette of the Worksheet }
|
||||
n := Workbook.GetPaletteSize;
|
||||
|
||||
{ Skip the first 8 entries - they are hard-coded into Excel }
|
||||
for i:=8 to 63 do
|
||||
for i := 8 to 8 + NUM_COLORS - 1 do
|
||||
begin
|
||||
rgb := Math.IfThen(i < n, Workbook.GetPaletteColor(i), $FFFFFF);
|
||||
AStream.WriteDWord(DWordToLE(rgb))
|
||||
@ -2185,8 +2211,7 @@ var
|
||||
dbl: Double;
|
||||
begin
|
||||
{ BIFF record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_PAGESETUP));
|
||||
AStream.WriteWord(WordToLE(9*2 + 2*8));
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_PAGESETUP, 9*2 + 2*8);
|
||||
|
||||
{ Paper size }
|
||||
AStream.WriteWord(WordToLE(0)); // 1 = Letter, 9 = A4
|
||||
@ -2242,9 +2267,8 @@ begin
|
||||
error. They possibly require an additional SELECTION record. }
|
||||
|
||||
{ BIFF record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_PANE));
|
||||
if isBIFF58 then n := 10 else n := 9;
|
||||
AStream.WriteWord(WordToLE(n));
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_PANE, n);
|
||||
|
||||
{ Position of the vertical split (px, 0 = No vertical split):
|
||||
- Unfrozen pane: Width of the left pane(s) (in twips = 1/20 of a point)
|
||||
@ -2706,8 +2730,7 @@ begin
|
||||
end;
|
||||
|
||||
{ BIFF record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_ROW));
|
||||
AStream.WriteWord(WordToLE(16));
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_ROW, 16);;
|
||||
|
||||
{ Index of row }
|
||||
AStream.WriteWord(WordToLE(Word(ARowIndex)));
|
||||
@ -2792,8 +2815,7 @@ begin
|
||||
end;
|
||||
|
||||
{ BIFF record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_SELECTION));
|
||||
AStream.WriteWord(WordToLE(15));
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_SELECTION, 15);
|
||||
|
||||
{ Pane identifier }
|
||||
AStream.WriteByte(APane);
|
||||
@ -2923,8 +2945,7 @@ var
|
||||
flags: Word;
|
||||
begin
|
||||
{ BIFF Record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_SHEETPR));
|
||||
AStream.WriteWord(WordToLE(2));
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_SHEETPR, 2);
|
||||
|
||||
flags := $04C1;
|
||||
AStream.WriteWord(WordToLE(flags));
|
||||
@ -3016,8 +3037,7 @@ end;
|
||||
procedure TsSpreadBIFFWriter.WriteWindow1(AStream: TStream);
|
||||
begin
|
||||
{ BIFF Record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_WINDOW1));
|
||||
AStream.WriteWord(WordToLE(18));
|
||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_WINDOW1, 18);
|
||||
|
||||
{ Horizontal position of the document window, in twips = 1 / 20 of a point }
|
||||
AStream.WriteWord(WordToLE(0));
|
||||
|
562
components/fpspreadsheet/xlsescher.pas
Normal file
562
components/fpspreadsheet/xlsescher.pas
Normal file
@ -0,0 +1,562 @@
|
||||
{ xlsEscher }
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
The unit xlsExcel provides basic support for the hierarchy of shapes and
|
||||
drawings in Microsoft Office files ("Escher", "OfficeArt") as it is needed for
|
||||
the BIFF record MSODRAWING (Cell comments, charts).
|
||||
|
||||
AUTHORS: Werner Pamler
|
||||
|
||||
DOCUMENTATION:
|
||||
Office Drawing 97-2007 Binary Format Specification
|
||||
http://www.digitalpreservation.gov/formats/digformatspecs/OfficeDrawing97-2007BinaryFormatSpecification.pdf
|
||||
[MS-ODRAW].pdf
|
||||
https://msdn.microsoft.com/en-us/library/office/cc441433%28v=office.12%29.aspx
|
||||
[MS-PPT].pdf
|
||||
https://msdn.microsoft.com/en-us/library/office/cc313106%28v=office.12%29.aspx
|
||||
[MS-XLS].pdf
|
||||
https://msdn.microsoft.com/en-us/library/office/cc313154%28v=office.12%29.aspx
|
||||
|
||||
LICENSE: See the file COPYING.modifiedLGPL.txt, included in the Lazarus
|
||||
distribution, for details about the license.
|
||||
-------------------------------------------------------------------------------}
|
||||
|
||||
unit xlsEscher;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils;
|
||||
|
||||
const
|
||||
{ Record IDs }
|
||||
MSO_ID_OFFICEART_DG_CONTAINER = $F002;
|
||||
MSO_ID_OFFICEART_SPGR_CONTAINER = $F003;
|
||||
MSO_ID_OFFICEART_SP_CONTAINER = $F004;
|
||||
MSO_ID_OFFICEART_FDG = $F008;
|
||||
MSO_ID_OFFICEART_FSPGR = $F009;
|
||||
MSO_ID_OFFICEART_FSP = $F00A;
|
||||
MSO_ID_OFFICEART_FOPT = $F00B;
|
||||
MSO_ID_OFFICEART_CLIENTTEXTBOX = $F00D;
|
||||
MSO_ID_OFFICEART_CLIENTANCHORSHEET = $F010;
|
||||
MSO_ID_OFFICEART_CLIENTDATA = $F011;
|
||||
|
||||
{ Record version }
|
||||
MSO_VER_CONTAINER = $0F;
|
||||
|
||||
{ Shape types }
|
||||
MSO_SPT_MIN = 0;
|
||||
MSO_SPT_NOTPRIMITIVE = MSO_SPT_MIN;
|
||||
MSO_SPT_RECTANGLE = 1;
|
||||
MSO_SPT_ROUNDRECTANGLE = 2;
|
||||
MSO_SPT_ELLIPSE = 3;
|
||||
MSO_SPT_DIAMOND = 4;
|
||||
MSO_SPT_ISOCELESTRIANGLE = 5;
|
||||
MSO_SPT_RIGHT_TRIANGLE = 6;
|
||||
MSO_SPT_PARALLELOGRAM = 7;
|
||||
MSO_SPT_TRAPEZOID = 8;
|
||||
MSO_SPT_HEXAGON = 9;
|
||||
MSO_SPT_OCTAGON = 10;
|
||||
MSO_SPT_PLUS = 11;
|
||||
MSO_SPT_STAR = 12;
|
||||
MSO_SPT_ARROW = 13;
|
||||
MSO_SPT_THICKARROW = 14;
|
||||
MSO_SPT_HOMEPLAT = 15;
|
||||
MSO_SPT_CUBE = 16;
|
||||
MSO_SPT_BALLOON = 17;
|
||||
MSO_SPT_SEAL = 18;
|
||||
MSO_SPT_ARC = 19;
|
||||
MSO_SPT_LINE = 20;
|
||||
MSO_SPT_PLAQUE = 21;
|
||||
MSO_SPT_CAN = 22;
|
||||
MSO_SPT_DONUT = 23;
|
||||
MSO_SPT_TEXTSIMPLE = 24;
|
||||
MSO_SPT_TEXTOCTAGON = 25;
|
||||
MSO_SPT_TEXTHEXAGON = 26;
|
||||
MSO_SPT_TEXTCURVE = 27;
|
||||
MSO_SPT_TEXTWAVE = 28;
|
||||
MSO_SPT_TEXTRING = 29;
|
||||
MSO_SPT_TEXTONCURVE = 30;
|
||||
MSO_SPT_TEXTONRING = 31;
|
||||
MSO_SPT_STRAIGHTCONNECTOR1 = 32;
|
||||
MSO_SPT_BENTCONNECTOR2 = 33;
|
||||
MSO_SPT_BENTCONNECTOR3 = 34;
|
||||
MSO_SPT_BENTCONNECTOR4 = 35;
|
||||
MSO_SPT_BENTCONNECTOR5 = 36;
|
||||
MSO_SPT_CURVEDCONNECTOR2 = 37;
|
||||
MSO_SPT_CURVEDCONNECTOR3 = 38;
|
||||
MSO_SPT_CURVEDCONNECTOR4 = 39;
|
||||
MSO_SPT_CURVEDCONNECTOR5 = 40;
|
||||
MSO_SPT_CALLOUT1 = 41;
|
||||
MSO_SPT_CALLOUT2 = 42;
|
||||
MSO_SPT_CALLOUT3 = 43;
|
||||
MSO_SPT_ACCENTCALLOUT1 = 44;
|
||||
MSO_SPT_ACCENTCALLOUT2 = 45;
|
||||
MSO_SPT_ACCENTCALLOUT3 = 46;
|
||||
MSO_SPT_BORDERCALLOUT1 = 47;
|
||||
MSO_SPT_BORDERCALLOUT2 = 48;
|
||||
MSO_SPT_BORDERCALLOUT3 = 49;
|
||||
MSO_SPT_ACCENTBORDERCALLOUT1 = 50;
|
||||
MSO_SPT_ACCENTBORDERCALLOUT2 = 51;
|
||||
MSO_SPT_ACCENTBORDERCALLOUT3 = 52;
|
||||
MSO_SPT_RIBBON = 53;
|
||||
MSO_SPT_RIBBON2 = 54;
|
||||
MSO_SPT_CHEVRON = 55;
|
||||
MSO_SPT_PENTAGON = 56;
|
||||
MSO_SPT_NOSMOKING = 57;
|
||||
MSO_SPT_SEAL8 = 58;
|
||||
MSO_SPT_SEAL16 = 59;
|
||||
MSO_SPT_SEAL32 = 60;
|
||||
MSO_SPT_WEDGERECTCALLOUT = 61;
|
||||
MSO_SPT_WEDGERRECTCALLOUT = 62;
|
||||
MSO_SPT_WEDGEELLIPSECALLOUT = 63;
|
||||
MSO_SPT_WAVE = 64;
|
||||
MSO_SPT_FOLDERCORNER = 65;
|
||||
MSO_SPT_LEFTARROW = 66;
|
||||
MSO_SPT_DOWNARROW = 67;
|
||||
MSO_SPT_UPARROW = 68;
|
||||
MSO_SPT_LEFTRIGHTARROW = 69;
|
||||
MSO_SPT_UPDOWNARROW = 70;
|
||||
MSO_SPT_IRREGULARSEAL1 = 71;
|
||||
MSO_SPT_IRREGULARSEAL2 = 72;
|
||||
MSO_SPT_LIGNTNINGBOLT = 73;
|
||||
MSO_SPT_HEART = 74;
|
||||
MSO_SPT_PICTUREFRAME = 75;
|
||||
MSO_SPT_QUADARROW = 76;
|
||||
MSO_SPT_LEFTARROWCALLOUT = 77;
|
||||
MSO_SPT_RIGHTARROWCALLOUT = 78;
|
||||
MSO_SPT_UPARROWCALLOUT = 79;
|
||||
MSO_SPT_DOWNARROWCALLOUT = 80;
|
||||
MSO_SPT_LEFTRIGHTARROWCALLOUT = 81;
|
||||
MSO_SPT_UPDOWNARROWCALLOUT = 82;
|
||||
MSO_SPT_QUADARROWCALLOUT = 83;
|
||||
MSO_SPT_BEVEL = 84;
|
||||
MSO_SPT_LEFTBRACKET = 85;
|
||||
MSO_SPT_RIGHTBRACKET = 86;
|
||||
MSO_SPT_LEFTBRACE = 87;
|
||||
MSO_SPT_RIGHTBRACE = 88;
|
||||
MSO_SPT_LEFTUPARROW = 89;
|
||||
MSO_SPT_BENTUPARROW = 90;
|
||||
MSO_SPT_BENTARROW = 91;
|
||||
MSO_SPT_SEAL25 = 92;
|
||||
MSO_SPT_STRIPEDRIGHTARROW = 83;
|
||||
MSO_SPT_NOTCHEDRIGHTARROW = 84;
|
||||
MSO_SPT_BLOCKARC = 95;
|
||||
MSO_SPT_SMILIEYFACE = 96;
|
||||
MSO_SPT_VERTICALSCROLL = 97;
|
||||
MSO_SPT_HORIZONTALSCROLL = 98;
|
||||
MSO_SPT_CICRULARARROW = 99;
|
||||
MSO_SPT_NOTCHEDCIRCULARARROW = 100;
|
||||
MSO_SPT_UTURNARROW = 101;
|
||||
MSO_SPT_CURVEDRIGHTARROW = 102;
|
||||
MSO_SPT_CURVEDLEFTARROW = 103;
|
||||
MSO_SPT_CURVEDUPARROW = 104;
|
||||
MSO_SPT_CURVEDDOWNARROW = 105;
|
||||
MSO_SPT_CLOUDCALLOUT = 106;
|
||||
MSO_SPT_ELLIPSERIBBON = 107;
|
||||
MSO_SPT_ELLIPSERIBBON2 = 108;
|
||||
MSO_SPT_FLOWCHARTPROCESS = 109;
|
||||
MSO_SPT_FLOWCHARTDECISION = 110;
|
||||
MSO_SPT_FLOWCHARTINPUTOUTPUT = 111;
|
||||
MSO_SPT_FLOWCHARTPREDEFINEDPROCESS = 112;
|
||||
MSO_SPT_FLOWCHARTINTERNALSTORAGE = 113;
|
||||
MSO_SPT_FLOWCHARTDOCUMENT = 114;
|
||||
MSO_SPT_FLOWCHARTMULTIDOCUMENT = 115;
|
||||
MSO_SPT_FLOWCHARTTERMINATOR = 116;
|
||||
MSO_SPT_FLOWCHARTPREPARATION = 117;
|
||||
MSO_SPT_FLOWCHARTMANUALINPUT = 118;
|
||||
MSO_SPT_FLOWCHARTMANUALOPERATION = 119;
|
||||
MSO_SPT_FLOWCHARTCONNECTOR = 120;
|
||||
MSO_SPT_FLOWCHARTPUNCHEDCARD = 121;
|
||||
MSO_SPT_FLOWCHARTPUNCHEDTAPE = 122;
|
||||
MSO_SPT_FLOWCHARTSUMMINGJUNCTION = 123;
|
||||
MSO_SPT_FLOWCHARTOR = 124;
|
||||
MSO_SPT_FLOWCHARTCOLLATE = 125;
|
||||
MSO_SPT_FLOWCHARTSORT = 126;
|
||||
MSO_SPT_FLOWCHARTEXTRACT = 127;
|
||||
MSO_SPT_FLOWCHARTMERGE = 128;
|
||||
MSO_SPT_FLOWCHARTOFFLINESTORAGE = 129;
|
||||
MSO_SPT_FLOWCHARTONLINESTORAGE = 130;
|
||||
MSO_SPT_FLOWCHARTMAGNETICTAPE = 131;
|
||||
MSO_SPT_FLOWCHARTMAGNETICDISK = 132;
|
||||
MSO_SPT_FLOWCHARTMAGNETICDRUM = 133;
|
||||
MSO_SPT_FLOWCHARTDISPLAY = 134;
|
||||
MSO_SPT_FLOWCHARTDELAY = 135;
|
||||
MSO_SPT_TEXTPLAINTEXT = 136;
|
||||
MSO_SPT_TEXTSTOP = 137;
|
||||
MSO_SPT_TEXTTRIANGLE = 138;
|
||||
MSO_SPT_TEXTTRIANGLEINVERTED = 139;
|
||||
MSO_SPT_TEXTCHEVRON = 140;
|
||||
MSO_SPT_TEXTCHEVRONINVERTED = 141;
|
||||
MSO_SPT_TEXTRINGINSIDE = 142;
|
||||
MSO_SPT_TEXTRINGOUTSIDE = 143;
|
||||
MSO_SPT_TEXTARCHUPCURVE = 144;
|
||||
MSO_SPT_TEXTARCHDOWNCURVE = 145;
|
||||
MSO_SPT_TEXTCIRCLECURVE = 146;
|
||||
MSO_SPT_TEXTBUTTONCURVE = 147;
|
||||
MSO_SPT_TEXTARCHUPPOUR = 148;
|
||||
MSO_SPT_TEXTARCHDOWNPOUR = 149;
|
||||
MSO_SPT_TEXTCIRCLEPOUR = 150;
|
||||
MSO_SPT_TEXTBUTTONPOUR = 151;
|
||||
MSO_SPT_TEXTCURVEUP = 152;
|
||||
MSO_SPT_TEXTCURVEDOWN = 153;
|
||||
MSO_SPT_TEXTCASCADEUP = 154;
|
||||
MSO_SPT_TEXTCASCADEDOWN = 155;
|
||||
MSO_SPT_TEXTWAVE1 = 156;
|
||||
MSO_SPT_TEXTWAVE2 = 157;
|
||||
MSO_SPT_TEXTWAVE3 = 158;
|
||||
MSO_SPT_TEXTWAVE4 = 159;
|
||||
MSO_SPT_TEXTINFLATE = 160;
|
||||
MSO_SPT_TEXTDEFLATE = 161;
|
||||
MSO_SPT_TEXTINFLATEBOTTOM = 162;
|
||||
MSO_SPT_TEXTDEFLATEBOTTOM = 163;
|
||||
MSO_SPT_TEXTINFLATETOP = 164;
|
||||
MSO_SPT_TEXTDEFLATETOP = 165;
|
||||
MSO_SPT_TEXTDEFLATEINFLATE = 166;
|
||||
MSO_SPT_TEXTDEFLATEINFLATEDEFLATE = 167;
|
||||
MSO_SPT_TEXTFADERIGHT = 168;
|
||||
MSO_SPT_TEXTFADELEFT = 169;
|
||||
MSO_SPT_TEXTFADEUP = 170;
|
||||
MSO_SPT_TEXTFADEDOWN = 171;
|
||||
MSO_SPT_TEXTSLANTUP = 172;
|
||||
MSO_SPT_TEXTSLANTDOWN = 173;
|
||||
MSO_SPT_TEXTCANUP = 174;
|
||||
MSO_SPT_TEXTCANDOWN = 175;
|
||||
MSO_SPT_FLOWCHARTALTERNATEPROCESS = 176;
|
||||
MSO_SPT_FLOWCHARTOFFPAGECONNECTOR = 177;
|
||||
MSO_SPT_CALLOUT90 = 178;
|
||||
MSO_SPT_ACCENTCALLOUT90 = 179;
|
||||
MSO_SPT_BORDERCALLOUT90 = 180;
|
||||
MSO_SPT_ACCENTBORDERCALLOUT90 = 181;
|
||||
MSO_SPT_LEFTRIGHTUPARROW = 182;
|
||||
MSO_SPT_SUN = 183;
|
||||
MSO_SPT_MOON = 184;
|
||||
MSO_SPT_BRACKETPAIR = 185;
|
||||
MSO_SPT_BRACEPAIR = 186;
|
||||
MSO_SPT_SEAL4 = 187;
|
||||
MSO_SPT_DOUBLEWAVE = 188;
|
||||
MSO_SPT_ACTIONBUTTONBLANK = 189;
|
||||
MSO_SPT_ACTIONBUTTONHOME = 190;
|
||||
MSO_SPT_ACTIONBUTTONHELP = 191;
|
||||
MSO_SPT_ACTIONBUTTONINFORMATION = 192;
|
||||
MSO_SPT_ACTIONBUTTONFORWARDNEXT = 193;
|
||||
MSO_SPT_ACTIONBUTTONBACKPREVIOUS = 194;
|
||||
MSO_SPT_ACTIONBUTTONEND = 195;
|
||||
MSO_SPT_ACTIONBUTTONBEGINNING = 196;
|
||||
MSO_SPT_ACTIONBUTTONRETURN = 197;
|
||||
MSO_SPT_ACTIONBUTTONDOCUMENT = 198;
|
||||
MSO_SPT_ACTIONBUTTONSOUND = 199;
|
||||
MSO_SPT_ACTIONBUTTONMOVIE = 200;
|
||||
MSO_SPT_HOSTCONTROL = 201;
|
||||
MSO_SPT_TEXTBOX = 202;
|
||||
MSO_SPT_NIL = $0FFF;
|
||||
MSO_SPT_MAX = MSO_SPT_NIL;
|
||||
|
||||
{ Bits in OfficeArtFSp record }
|
||||
MSO_FSP_BITS_GROUP = $00000001;
|
||||
MSO_FSP_BITS_CHILD = $00000002;
|
||||
MSO_FSP_BITS_PATRIARCH = $00000004;
|
||||
MSO_FSP_BITS_DELETED = $00000008;
|
||||
MSO_FSP_BITS_OLESHAPE = $00000010;
|
||||
MSO_FSP_BITS_HASMASTER = $00000020;
|
||||
MSO_FSP_BITS_FLIPHOR = $00000040;
|
||||
MSO_FSP_BITS_FLIPVERT = $00000080;
|
||||
MSO_FSP_BITS_CONNECTOR = $00000100;
|
||||
MSO_FSP_BITS_HASANCHOR = $00000200;
|
||||
MSO_FSP_BITS_BACKGROUND = $00000400;
|
||||
MSO_FSP_BITS_HASSHAPETYPE = $00000800;
|
||||
|
||||
{ Identifier of property array items if OfficeArtFOpt record }
|
||||
MSO_FOPT_ID_TEXTID = $0080;
|
||||
MSO_FOPT_ID_TEXTDIRECTION = $008B;
|
||||
MSO_FOPT_ID_TEXTBOOL = $00BF;
|
||||
MSO_FOPT_ID_CONNECTIONPOINTTYPE = $0158;
|
||||
MSO_FOPT_ID_FILLCOLOR = $0181;
|
||||
MSO_FOPT_ID_FILLBACKGROUNDCOLOR = $0183;
|
||||
MSO_FOPT_ID_FILLFOREGROUNDCOLOR = $0185;
|
||||
MSO_FOPT_ID_FILLBOOL = $01BF;
|
||||
MSO_FOPT_ID_SHADOWCOLOR = $0201;
|
||||
MSO_FOPT_ID_SHADOWBOOL = $023F;
|
||||
MSO_FOPT_ID_GROUPBOOL = $03BF;
|
||||
|
||||
procedure WriteMSOClientAnchorSheetRecord(AStream: TStream;
|
||||
ATopRow, ALeftCol, ABottomRow, ARightCol,
|
||||
ALeftMargin, ARightMargin, ATopMargin, ABottomMargin: Word;
|
||||
AMoveIntact, AResizeIntact: Boolean);
|
||||
procedure WriteMSOClientDataRecord(AStream: TStream);
|
||||
procedure WriteMSOClientTextboxRecord(AStream: TStream);
|
||||
procedure WriteMSODgContainer(AStream: TStream; ASize: DWord);
|
||||
procedure WriteMSOFDgRecord(AStream: TStream; ANumShapes, ADrawingID, ALastObjID: Word);
|
||||
procedure WriteMSOFOptRecord_Comment(AStream: TStream);
|
||||
procedure WriteMSOProperty(AStream: TStream; APropertyID: Word; AValue: DWord);
|
||||
procedure WriteMSOFSpRecord(AStream: TStream; AShapeID: DWord; AShapeType: Word; ABits: DWord);
|
||||
procedure WriteMSOFSpGrRecord(AStream: TStream; ALeft, ATop, ARight, ABottom: DWord);
|
||||
procedure WriteMSOHeader(AStream: TStream; AType, AVersion, AInstance: Word; ARecSize: DWord);
|
||||
procedure WriteMSOSpContainer(AStream: TStream; ASize: DWord);
|
||||
procedure WriteMSOSpGrContainer(AStream: TStream; ASize: DWord);
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
fpsutils;
|
||||
|
||||
type
|
||||
TsMSOHeader = packed record
|
||||
Version_Instance: Word;
|
||||
RecordType: Word;
|
||||
RecordSize: DWord;
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an OfficeArtClientAnchorSheet record to a stream.
|
||||
|
||||
The OfficeArtClientAnchorSheet structure specifies the anchor position of
|
||||
a drawing object embedded in a sheet.
|
||||
|
||||
Ref: [MS-XLS].pdf
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure WriteMSOClientAnchorSheetRecord(AStream: TStream;
|
||||
ATopRow, ALeftCol, ABottomRow, ARightCol,
|
||||
ALeftMargin, ARightMargin, ATopMargin, ABottomMargin: Word;
|
||||
AMoveIntact, AResizeIntact: Boolean);
|
||||
const
|
||||
fMOVE = $0001; // specifies whether the shape will be kept intact when the cells are moved.
|
||||
fSIZE = $0002; // specifies whether the shape will be kept intact when the cells are resized.
|
||||
var
|
||||
flags: Word;
|
||||
begin
|
||||
WriteMSOHeader(AStream, MSO_ID_OFFICEART_CLIENTANCHORSHEET, 0, 0, 18);
|
||||
|
||||
flags := 0;
|
||||
if AMoveIntact then begin
|
||||
AResizeIntact := true;
|
||||
flags := flags or fMOVE;
|
||||
end;
|
||||
if AResizeIntact then
|
||||
flags := flags or fSIZE;
|
||||
AStream.WriteWord(WordToLE(flags));
|
||||
|
||||
// Column of the cell under the top left corner of the bounding rectangle of the shape.
|
||||
AStream.WriteWord(WordLEToN(ALeftCol));
|
||||
|
||||
// x coordinate of the top left corner of the bounding rectangle relative to
|
||||
// the corner of the underlying cell.
|
||||
// The value is expressed as 1024th’s of that cell’s width.
|
||||
AStream.WriteWord(WordLEToN(ALeftMargin));
|
||||
|
||||
// Row of the cell under the top left corner of the bounding rectangle of the shape.
|
||||
AStream.WriteWord(WordLEToN(ATopRow));
|
||||
|
||||
// y coordinate of the top left corner of the bounding rectangle relative to
|
||||
// the corner of the underlying cell.
|
||||
// The value is expressed as 256th’s of that cell’s height.
|
||||
AStream.WriteWord(WordLEToN(ATopMargin));
|
||||
|
||||
// Column of the cell under the bottom right corner of the bounding rectangle
|
||||
// of the shape.
|
||||
AStream.WriteWord(WordToLE(ARightCol));
|
||||
|
||||
// x coordinate of the bottom right corner of the bounding rectangle relative
|
||||
// to the corner of the underlying cell.
|
||||
// The value is expressed as 1024th’s of that cell’s width.
|
||||
AStream.WriteWord(WordToLE(ARightMargin));
|
||||
|
||||
// Row of the cell under the bottom right corner of the bounding rectangle
|
||||
// of the shape.
|
||||
AStream.WriteWord(WordToLE(ABottomRow));
|
||||
|
||||
// y coordinate of the bottom right corner of the bounding rectangle relative
|
||||
// to the corner of the underlying cell.
|
||||
// The value is expressed as 256th’s of that cell’s height.
|
||||
AStream.WriteWord(WordToLE(ABottomMargin));
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an OfficeArtClientData record to a stream
|
||||
|
||||
The OfficeArtClientData structure specifies the client data of a drawing
|
||||
object.
|
||||
|
||||
MUST be the last structure of the rgChildRec field of the current
|
||||
MSODRAWING BIFF record.
|
||||
|
||||
The next record MUST be OBJ which contains the detailed data information
|
||||
about this drawing object.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure WriteMSOClientDataRecord(AStream: TStream);
|
||||
begin
|
||||
WriteMSOHeader(AStream, MSO_ID_OFFICEART_CLIENTDATA, 0, 0, 0);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an OfficeArtClientTextbox record to a stream
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure WriteMSOClientTextboxRecord(AStream: TStream);
|
||||
begin
|
||||
WriteMSOHeader(AStream, MSO_ID_OFFICEART_CLIENTTEXTBOX, 0, 0, 0);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an OfficeArtDgContainer record to a stream.
|
||||
|
||||
The OfficeArtDgContainer record specifies the container for all file records
|
||||
for the objects in an MSO drawing.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure WriteMSODgContainer(AStream: TStream; ASize: DWord);
|
||||
begin
|
||||
WriteMSOHeader(AStream, MSO_ID_OFFICEART_DG_CONTAINER, MSO_VER_CONTAINER, 0, ASize);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an OfficeArt FDG record to a stream.
|
||||
|
||||
The OfficeArtFDG record specifies the number of shapes, the drawing identifier,
|
||||
and the shape identifier of the last shape in a drawing.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure WriteMSOFdgRecord(AStream: TStream; ANumShapes, ADrawingID, ALastObjID: Word);
|
||||
begin
|
||||
if ADrawingID > $0FFE then
|
||||
raise Exception.CreateFmt('[WriteMSOFdgRecord] Invalid drawing identifier $%.4x', [ADrawingID]);
|
||||
WriteMSOHeader(AStream, MSO_ID_OFFICEART_FDG, 0, ADrawingID, 8);
|
||||
AStream.WriteDWord(DWordToLE(ANumShapes));
|
||||
AStream.WriteDWord(DWordToLE(ALastObjID));
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an OfficeArtFOpt as it is used for a cell comment record to the stream
|
||||
|
||||
The OfficeArtFOPT record specifies a table of OfficeArtRGFOPTE records,
|
||||
|
||||
The OfficeArtRGFOPTE record specifies a property table, which consists of an
|
||||
array of fixed-size property table entries, followed by a variable-length
|
||||
field of complex data.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure WriteMSOFOptRecord_Comment(AStream: TStream);
|
||||
const
|
||||
NUM_PROPERTIES = 13;
|
||||
begin
|
||||
// Escher header
|
||||
WriteMSOHeader(AStream, MSO_ID_OFFICEART_FOPT, 3, NUM_PROPERTIES, NUM_PROPERTIES*6);
|
||||
|
||||
// TextID
|
||||
WriteMSOProperty(AStream, MSO_FOPT_ID_TEXTID, 0);
|
||||
|
||||
// Text direction
|
||||
WriteMSOProperty(AStream, MSO_FOPT_ID_TEXTDIRECTION, 2); // 2 = "determined from text string"
|
||||
|
||||
// Boolean properties of text in a shape
|
||||
WriteMSOProperty(AStream, MSO_FOPT_ID_TEXTBOOL, $00080008);
|
||||
|
||||
// Type of connection point
|
||||
WriteMSOProperty(AStream, MSO_FOPT_ID_CONNECTIONPOINTTYPE, 0);
|
||||
|
||||
// Fill color
|
||||
WriteMSOProperty(AStream, MSO_FOPT_ID_FILLCOLOR, $00E1FFFF);
|
||||
|
||||
// Background color of fill
|
||||
WriteMSOProperty(AStream, MSO_FOPT_ID_FILLBACKGROUNDCOLOR, $00E1FFFF);
|
||||
|
||||
// Foreground color of fill
|
||||
WriteMSOProperty(AStream, MSO_FOPT_ID_FILLFOREGROUNDCOLOR, $100000F4);
|
||||
|
||||
// Fill style boolean properties
|
||||
WriteMSOProperty(AStream, MSO_FOPT_ID_FILLBOOL, $00100010);
|
||||
|
||||
// Line foreground color for black-and-white mode
|
||||
WriteMSOProperty(AStream, $01C3, $100000F4);
|
||||
|
||||
// Shadow color
|
||||
WriteMSOProperty(AStream, MSO_FOPT_ID_SHADOWCOLOR, 0);
|
||||
|
||||
// Shadow color primary color modifier if in black-and-white mode
|
||||
WriteMSOProperty(AStream, $0203, $100000F4);
|
||||
|
||||
// Shadow style boolean properties
|
||||
WriteMSOProperty(AStream, MSO_FOPT_ID_SHADOWBOOL, $00030003);
|
||||
|
||||
// Group shape boolean properties
|
||||
WriteMSOProperty(AStream, MSO_FOPT_ID_GROUPBOOL, $00020002);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes a property of the FOPT array
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure WriteMSOProperty(AStream: TStream; APropertyID: Word;
|
||||
AValue: DWord);
|
||||
begin
|
||||
AStream.WriteWord(WordToLE(APropertyID));
|
||||
AStream.WriteDWord(DWordToLE(AValue));
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an OfficeArtFSp record to the stream
|
||||
|
||||
The OfficeArtFSP record specifies an instance of a shape.
|
||||
The record header contains the shape type, and the record itself contains
|
||||
the shape identifier and a set of bits that further define the shape.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure WriteMSOFSpRecord(AStream: TStream; AShapeID: DWord;
|
||||
AShapeType: Word; ABits: DWord);
|
||||
begin
|
||||
WriteMSOHeader(AStream, MSO_ID_OFFICEART_FSP, 2, AShapeType, 8);
|
||||
AStream.WriteDWord(DWordToLE(AShapeID));
|
||||
AStream.WriteDWord(DWordToLE(ABits));
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an OfficeArtFSpGr record to the stream.
|
||||
|
||||
The OfficeArtFSPGR record specifies the coordinate system of the group shape
|
||||
that the anchors of the child shape are expressed in.
|
||||
This record is present only for group shapes.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure WriteMSOFSpGrRecord(AStream: TStream; ALeft, ATop, ARight, ABottom: DWord);
|
||||
begin
|
||||
WriteMSOHeader(AStream, MSO_ID_OFFICEART_FSPGR, 1, 0, 16);
|
||||
AStream.WriteDWord(DWordToLE(ALeft));
|
||||
AStream.WriteDWord(DWordToLE(ATop));
|
||||
AStream.WriteDWord(DWordToLE(ARight));
|
||||
AStream.WriteDWord(DWordToLE(ABottom));
|
||||
end;
|
||||
|
||||
{ Writes the header of an MSO subrecord used internally by MSODRAWING records }
|
||||
procedure WriteMSOHeader(AStream: TStream; AType, AVersion, AInstance: Word;
|
||||
ARecSize: DWord);
|
||||
var
|
||||
rec: TsMSOHeader;
|
||||
begin
|
||||
rec.Version_Instance := WordToLE((AVersion and $000F) + AInstance shl 4); //and $FFF0) shr 4);
|
||||
// To do: How to handle Version_Instance on big-endian machines?
|
||||
// Version_Instance combines bits 0..3 for "version" and 4..15 for "instance"
|
||||
rec.RecordType := WordToLE(AType);
|
||||
rec.RecordSize := DWordToLE(ARecSize);
|
||||
AStream.WriteBuffer(rec, SizeOf(rec));
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an OffcieARtSpContainer record to the stream.
|
||||
The OfficeArtSpContainer record specifies a shape container.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure WriteMSOSpContainer(AStream: TStream; ASize: DWord);
|
||||
begin
|
||||
WriteMSOHeader(AStream, MSO_ID_OFFICEART_SP_CONTAINER, MSO_VER_CONTAINER, 0, ASize);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Writes an OfficeArtSpGrContainer record to a stream.
|
||||
The OfficeArtSpgrContainer record specifies a container for groups of shapes.
|
||||
The group container contains a variable number of shape containers and other
|
||||
group containers. Each group is a shape. The first container MUST be an
|
||||
OfficeArtSpContainer record, which MUST contain shape information for the
|
||||
group.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure WriteMSOSpGrContainer(AStream: TStream; ASize: DWord);
|
||||
begin
|
||||
WriteMSOHeader(AStream, MSO_ID_OFFICEART_SPGR_CONTAINER, MSO_VER_CONTAINER, 0, ASize);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
Reference in New Issue
Block a user