You've already forked lazarus-ccr
fpspreadsheet: Fix PageLayout. Separate BIFF5 and BIFF8 handling of 3d references.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6412 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -105,7 +105,8 @@ type
|
|||||||
procedure WriteBOF(AStream: TStream; ADataType: Word);
|
procedure WriteBOF(AStream: TStream; ADataType: Word);
|
||||||
function WriteBoundsheet(AStream: TStream; AWorkSheet: TsWorksheet): Int64;
|
function WriteBoundsheet(AStream: TStream; AWorkSheet: TsWorksheet): Int64;
|
||||||
procedure WriteDefinedName(AStream: TStream; AWorksheet: TsWorksheet;
|
procedure WriteDefinedName(AStream: TStream; AWorksheet: TsWorksheet;
|
||||||
const AName: String; AIndexToREF: Word); override;
|
const AName: String; AIndexToREF, ASheetIndex: Word;
|
||||||
|
AKind: TsBIFFExternKind); override;
|
||||||
procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
|
procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
|
||||||
procedure WriteEOF(AStream: TStream);
|
procedure WriteEOF(AStream: TStream);
|
||||||
procedure WriteFont(AStream: TStream; AFont: TsFont);
|
procedure WriteFont(AStream: TStream; AFont: TsFont);
|
||||||
@ -117,10 +118,6 @@ type
|
|||||||
procedure WriteLABEL(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteLABEL(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
const AValue: string; ACell: PCell); override;
|
const AValue: string; ACell: PCell); override;
|
||||||
procedure WriteLocalLinkTable(AStream: TStream; AWorksheet: TsWorksheet);
|
procedure WriteLocalLinkTable(AStream: TStream; AWorksheet: TsWorksheet);
|
||||||
(*
|
|
||||||
function WriteRPNCellAddress3D(AStream: TStream; ASheet, ARow, ACol: Cardinal;
|
|
||||||
AFlags: TsRelFlags): Word; override;
|
|
||||||
*)
|
|
||||||
function WriteRPNSheetIndex(AStream: TStream; ADocumentURL: String;
|
function WriteRPNSheetIndex(AStream: TStream; ADocumentURL: String;
|
||||||
ASheet1, ASheet2: Integer): Word; override;
|
ASheet1, ASheet2: Integer): Word; override;
|
||||||
procedure WriteStringRecord(AStream: TStream; AString: String); override;
|
procedure WriteStringRecord(AStream: TStream; AString: String); override;
|
||||||
@ -482,14 +479,15 @@ begin
|
|||||||
|
|
||||||
case RecordType of
|
case RecordType of
|
||||||
INT_EXCEL_ID_BOF : ;
|
INT_EXCEL_ID_BOF : ;
|
||||||
INT_EXCEL_ID_BOUNDSHEET : ReadBoundSheet(AStream);
|
INT_EXCEL_ID_BOUNDSHEET : ReadBOUNDSHEET(AStream);
|
||||||
INT_EXCEL_ID_CODEPAGE : ReadCodePage(AStream);
|
INT_EXCEL_ID_CODEPAGE : ReadCODEPAGE(AStream);
|
||||||
INT_EXCEL_ID_DEFINEDNAME : ReadDefinedName(AStream);
|
INT_EXCEL_ID_DEFINEDNAME : ReadDefinedName(AStream);
|
||||||
INT_EXCEL_ID_EOF : SectionEOF := True;
|
INT_EXCEL_ID_EOF : SectionEOF := True;
|
||||||
INT_EXCEL_ID_EXTERNSHEET : ReadExternSheet(AStream);
|
INT_EXCEL_ID_EXTERNCOUNT : ReadEXTERNCOUNT(AStream, nil);
|
||||||
INT_EXCEL_ID_FONT : ReadFont(AStream);
|
INT_EXCEL_ID_EXTERNSHEET : ReadEXTERNSHEET(AStream, nil);
|
||||||
INT_EXCEL_ID_FORMAT : ReadFormat(AStream);
|
INT_EXCEL_ID_FONT : ReadFONT(AStream);
|
||||||
INT_EXCEL_ID_PALETTE : ReadPalette(AStream);
|
INT_EXCEL_ID_FORMAT : ReadFORMAT(AStream);
|
||||||
|
INT_EXCEL_ID_PALETTE : ReadPALETTE(AStream);
|
||||||
INT_EXCEL_ID_PASSWORD : ReadPASSWORD(AStream);
|
INT_EXCEL_ID_PASSWORD : ReadPASSWORD(AStream);
|
||||||
INT_EXCEL_ID_PROTECT : ReadPROTECT(AStream);
|
INT_EXCEL_ID_PROTECT : ReadPROTECT(AStream);
|
||||||
INT_EXCEL_ID_XF : ReadXF(AStream);
|
INT_EXCEL_ID_XF : ReadXF(AStream);
|
||||||
@ -532,8 +530,8 @@ begin
|
|||||||
INT_EXCEL_ID_COLINFO : ReadColInfo(AStream);
|
INT_EXCEL_ID_COLINFO : ReadColInfo(AStream);
|
||||||
INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream);
|
INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream);
|
||||||
INT_EXCEL_ID_EOF : SectionEOF := True;
|
INT_EXCEL_ID_EOF : SectionEOF := True;
|
||||||
INT_EXCEL_ID_EXTERNCOUNT : ReadEXTERNCOUNT(AStream);
|
INT_EXCEL_ID_EXTERNCOUNT : ReadEXTERNCOUNT(AStream, FWorksheet);
|
||||||
INT_EXCEL_ID_EXTERNSHEET : ReadEXTERNSHEET(AStream);
|
INT_EXCEL_ID_EXTERNSHEET : ReadEXTERNSHEET(AStream, FWorksheet);
|
||||||
INT_EXCEL_ID_FOOTER : ReadHeaderFooter(AStream, false);
|
INT_EXCEL_ID_FOOTER : ReadHeaderFooter(AStream, false);
|
||||||
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
|
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
|
||||||
INT_EXCEL_ID_HEADER : ReadHeaderFooter(AStream, true);
|
INT_EXCEL_ID_HEADER : ReadHeaderFooter(AStream, true);
|
||||||
@ -653,12 +651,16 @@ procedure TsSpreadBIFF5Reader.ReadRPNSheetIndex(AStream: TStream;
|
|||||||
var
|
var
|
||||||
idx: Int16;
|
idx: Int16;
|
||||||
s: String;
|
s: String;
|
||||||
|
sheetList: TsBIFFExternSheetList;
|
||||||
|
sheet: TsWorksheet;
|
||||||
|
extsheet: TsBIFFExternSheet;
|
||||||
begin
|
begin
|
||||||
ADocumentURL := '';
|
ADocumentURL := '';
|
||||||
ASheet1 := -1;
|
ASheet1 := -1;
|
||||||
ASheet2 := -1;
|
ASheet2 := -1;
|
||||||
|
|
||||||
{ One-based index to EXTERNSHEET record. Negative to indicate a 3D reference.
|
{ One-based index to EXTERNSHEET record in the booklist.
|
||||||
|
Negative to indicate a 3D reference.
|
||||||
Positive to indicate an external reference }
|
Positive to indicate an external reference }
|
||||||
idx := WordLEToN(AStream.ReadWord);
|
idx := WordLEToN(AStream.ReadWord);
|
||||||
|
|
||||||
@ -668,31 +670,24 @@ begin
|
|||||||
// Skip 8 unused bytes
|
// Skip 8 unused bytes
|
||||||
AStream.Position := AStream.Position + 8;
|
AStream.Position := AStream.Position + 8;
|
||||||
|
|
||||||
// one-based index to first referenced sheet (-1 = deleted sheet)
|
// zero-based index to first referenced sheet in workbook (-1 = deleted sheet)
|
||||||
idx := Int16(WordLEToN(AStream.ReadWord));
|
ASheet1 := Int16(WordLEToN(AStream.ReadWord));
|
||||||
if idx <> -1 then begin
|
|
||||||
s := FExternSheets.Strings[idx-1];
|
|
||||||
ASheet1 := FWorkbook.GetWorksheetIndex(s);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// one-based index to last referenced sheet (-1 = deleted sheet)
|
// zero-based index to last referenced sheet in workbook (-1 = deleted sheet)
|
||||||
idx := WordLEToN(AStream.ReadWord);
|
ASheet2 := WordLEToN(AStream.ReadWord);
|
||||||
if idx <> -1 then begin
|
|
||||||
s := FExternSheets.Strings[idx-1];
|
|
||||||
ASheet2 := FWorkbook.GetWorksheetIndex(s);
|
|
||||||
end;
|
|
||||||
end
|
end
|
||||||
else begin
|
else begin
|
||||||
{ *** External reference *** }
|
{ *** External reference *** }
|
||||||
|
|
||||||
// Skip 12 unused byes
|
{ !!! NOT CLEAR IF THIS IS CORRECT .... !!! }
|
||||||
AStream.Position := AStream.Position + 12;
|
|
||||||
|
|
||||||
dec(idx); // 1-based index to 0-based index
|
sheetlist := FLinkLists.GetLocalLinks(FWorksheet);
|
||||||
s := FExternSheets[idx];
|
extSheet := sheetlist.Items[idx - 1]; // Convert 1-based to 0-based index
|
||||||
|
s := ConvertEncoding(extSheet.SheetName, FCodePage, encodingUTF8);
|
||||||
ADocumentURL := s;
|
ADocumentURL := s;
|
||||||
|
|
||||||
// NOTE: THIS IS NOT COMPLETE !!!
|
// Skip 12 unused byes
|
||||||
|
AStream.Position := AStream.Position + 12;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -1447,32 +1442,45 @@ end;
|
|||||||
Writes out a DEFINEDNAMES record
|
Writes out a DEFINEDNAMES record
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsSpreadBIFF5Writer.WriteDefinedName(AStream: TStream;
|
procedure TsSpreadBIFF5Writer.WriteDefinedName(AStream: TStream;
|
||||||
AWorksheet: TsWorksheet; const AName: String; AIndexToREF: Word);
|
AWorksheet: TsWorksheet; const AName: String; AIndexToREF, ASheetIndex: Word;
|
||||||
|
AKind: TsBIFFExternKind);
|
||||||
|
|
||||||
procedure WriteRangeFormula(MemStream: TMemoryStream; ARange: TsCellRange;
|
procedure WriteRangeFormula(MemStream: TMemoryStream; ARange: TsCellRange;
|
||||||
AIndexToREF, ACounter: Word);
|
AIndexToREF, ASheetIndex, ACounter: Word);
|
||||||
var
|
var
|
||||||
sheetIdx: Integer;
|
idx: Word;
|
||||||
begin
|
begin
|
||||||
Unused(AIndexToREF);
|
|
||||||
|
|
||||||
sheetIdx := FWorkbook.GetWorksheetIndex(AWorksheet);
|
|
||||||
|
|
||||||
{ Token for tArea3dR }
|
{ Token for tArea3dR }
|
||||||
MemStream.WriteByte($3B);
|
MemStream.WriteByte($3B);
|
||||||
|
|
||||||
{ 1-based sheet index, negative to indicate 3D reference }
|
if AKind = ebkInternal then begin
|
||||||
MemStream.WriteWord(WordToLE(-(sheetIdx+1)));
|
{ INTERNAL REFERENCE:
|
||||||
|
1-based sheet index, negative to indicate 3D reference }
|
||||||
|
idx := word(-int16(AIndexToRef + 1));
|
||||||
|
MemStream.WriteWord(WordToLE(idx));
|
||||||
|
|
||||||
{ 8 bytes not used }
|
{ 8 bytes not used }
|
||||||
MemStream.WriteDWord(0);
|
MemStream.WriteDWord(0);
|
||||||
MemStream.WriteDWord(0);
|
MemStream.WriteDWord(0);
|
||||||
|
|
||||||
{ Index to first reference worksheet }
|
{ Index to first reference worksheet }
|
||||||
MemStream.WriteWord(WordToLE(sheetIdx)); // THIS IS ONLY VALID FOR PRINTRANGE!
|
MemStream.WriteWord(WordToLE(ASheetIndex));
|
||||||
|
|
||||||
{ Index to last reference worksheet }
|
{ Index to last reference worksheet }
|
||||||
MemStream.WriteWord(WordToLE(sheetIdx)); // THIS IS ONLY VALID FOR PRINTRANGE!
|
MemStream.WriteWord(WordToLE(ASheetIndex));
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
{ EXTERNAL REFERENCE:
|
||||||
|
always positive, 1-based index to EXTERNSHEET record }
|
||||||
|
idx := AIndexToRef;
|
||||||
|
MemStream.WriteWord(WordToLE(idx));
|
||||||
|
|
||||||
|
{ 12 bytes not used }
|
||||||
|
MemStream.WriteDWord(0);
|
||||||
|
MemStream.WriteDWord(0);
|
||||||
|
MemStream.WriteDWord(0);
|
||||||
|
end;
|
||||||
|
|
||||||
{ First row index }
|
{ First row index }
|
||||||
MemStream.WriteWord(WordToLE(ARange.Row1));
|
MemStream.WriteWord(WordToLE(ARange.Row1));
|
||||||
@ -1494,12 +1502,11 @@ procedure TsSpreadBIFF5Writer.WriteDefinedName(AStream: TStream;
|
|||||||
var
|
var
|
||||||
memstream: TMemoryStream;
|
memstream: TMemoryStream;
|
||||||
rng: TsCellRange;
|
rng: TsCellRange;
|
||||||
j: Integer;
|
|
||||||
idx: Integer;
|
idx: Integer;
|
||||||
|
j: Integer;
|
||||||
begin
|
begin
|
||||||
// Since this is a variable length record we begin by writing the formula
|
// Since this is a variable length record we begin by writing the formula
|
||||||
// to a memory stream
|
// to a memory stream
|
||||||
|
|
||||||
memstream := TMemoryStream.Create;
|
memstream := TMemoryStream.Create;
|
||||||
try
|
try
|
||||||
case AName of
|
case AName of
|
||||||
@ -1507,7 +1514,7 @@ begin
|
|||||||
for j := 0 to AWorksheet.PageLayout.NumPrintRanges-1 do
|
for j := 0 to AWorksheet.PageLayout.NumPrintRanges-1 do
|
||||||
begin
|
begin
|
||||||
rng := AWorksheet.PageLayout.PrintRange[j];
|
rng := AWorksheet.PageLayout.PrintRange[j];
|
||||||
WriteRangeFormula(memstream, rng, AIndexToRef, j+1);
|
WriteRangeFormula(memstream, rng, AIndexToRef, ASheetIndex, j+1);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
#07: begin
|
#07: begin
|
||||||
@ -1519,7 +1526,7 @@ begin
|
|||||||
if rng.Col2 = UNASSIGNED_ROW_COL_INDEX then rng.Col2 := rng.Col1;
|
if rng.Col2 = UNASSIGNED_ROW_COL_INDEX then rng.Col2 := rng.Col1;
|
||||||
rng.Row1 := 0;
|
rng.Row1 := 0;
|
||||||
rng.Row2 := 65535;
|
rng.Row2 := 65535;
|
||||||
WriteRangeFormula(memstream, rng, AIndexToRef, j);
|
WriteRangeFormula(memstream, rng, AIndexToRef, ASheetIndex, j);
|
||||||
inc(j);
|
inc(j);
|
||||||
end;
|
end;
|
||||||
if AWorksheet.PageLayout.HasRepeatedRows then
|
if AWorksheet.PageLayout.HasRepeatedRows then
|
||||||
@ -1529,14 +1536,12 @@ begin
|
|||||||
if rng.Row2 = UNASSIGNED_ROW_COL_INDEX then rng.Row2 := rng.Row1;
|
if rng.Row2 = UNASSIGNED_ROW_COL_INDEX then rng.Row2 := rng.Row1;
|
||||||
rng.Col1 := 0;
|
rng.Col1 := 0;
|
||||||
rng.Col2 := 255;
|
rng.Col2 := 255;
|
||||||
WriteRangeFormula(memstream, rng, AIndexToRef, j);
|
WriteRangeFormula(memstream, rng, AIndexToRef, ASheetIndex, j);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
else raise EFPSpreadsheetWriter.Create('Name not supported');
|
else raise EFPSpreadsheetWriter.Create('Name not supported');
|
||||||
end; // case
|
end; // case
|
||||||
|
|
||||||
idx := FWorkbook.GetWorksheetIndex(AWorksheet);
|
|
||||||
|
|
||||||
{ BIFF record header }
|
{ BIFF record header }
|
||||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_DEFINEDNAME, 14 + Length(AName) + Word(memstream.Size));
|
WriteBIFFHeader(AStream, INT_EXCEL_ID_DEFINEDNAME, 14 + Length(AName) + Word(memstream.Size));
|
||||||
|
|
||||||
@ -1553,10 +1558,10 @@ begin
|
|||||||
AStream.WriteWord(WordToLE(memstream.Size));
|
AStream.WriteWord(WordToLE(memstream.Size));
|
||||||
|
|
||||||
{ Global name, otherwise index to EXTERNSHEET record (1-based) }
|
{ Global name, otherwise index to EXTERNSHEET record (1-based) }
|
||||||
AStream.WriteWord(WordToLE(AIndexToREF+1));
|
AStream.WriteWord(WordToLE(AIndexToREF + 1));
|
||||||
|
|
||||||
{ Global name, otherwise index to sheet (1-based) }
|
{ Global name, otherwise index to sheet (1-based) }
|
||||||
AStream.WriteWord(WordToLE(idx+1));
|
AStream.WriteWord(WordToLE(ASheetIndex + 1));
|
||||||
|
|
||||||
{ Length of menu text }
|
{ Length of menu text }
|
||||||
AStream.WriteByte(0);
|
AStream.WriteByte(0);
|
||||||
@ -1756,28 +1761,25 @@ end;
|
|||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsSpreadBIFF5Writer.WriteGlobalLinkTable(AStream: TStream);
|
procedure TsSpreadBIFF5Writer.WriteGlobalLinkTable(AStream: TStream);
|
||||||
var
|
var
|
||||||
L: TStringList;
|
|
||||||
i: Integer;
|
|
||||||
sheet: TsWorksheet;
|
sheet: TsWorksheet;
|
||||||
|
globalLinks: TsBIFFExternSheetList;
|
||||||
|
sheetList: TsBIFFExternSheetList;
|
||||||
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
L := TStringList.Create;
|
{ collect the global links data }
|
||||||
try
|
CollectExternData;
|
||||||
for i := 0 to FWorkbook.GetWorksheetCount-1 do
|
|
||||||
begin
|
{ get global link list }
|
||||||
sheet := FWorkbook.GetWorksheetByIndex(i);
|
globalLinks := FLinkLists.GetGlobalLinks;
|
||||||
with sheet.PageLayout do
|
if (globalLinks = nil) or (globalLinks.Count = 0) then
|
||||||
if (NumPrintRanges > 0) or HasRepeatedCols or HasRepeatedRows then
|
|
||||||
L.Add(sheet.Name);
|
|
||||||
end;
|
|
||||||
if L.Count = 0 then
|
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
WriteEXTERNCOUNT(AStream, L.Count);
|
{ Write number of EXTERNSHEET records }
|
||||||
for i:=0 to L.Count-1 do
|
WriteEXTERNCOUNT(AStream, globalLinks.Count);
|
||||||
WriteEXTERNSHEET(AStream, L[i], true);
|
|
||||||
finally
|
{ For each sheet write an EXTERNSHEET record. }
|
||||||
L.Free;
|
for i:=0 to globalLinks.Count-1 do
|
||||||
end;
|
WriteEXTERNSHEET(AStream, globalLinks[i].SheetName, true);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
@ -1914,36 +1916,32 @@ var
|
|||||||
cell: PCell;
|
cell: PCell;
|
||||||
found: Boolean;
|
found: Boolean;
|
||||||
i, j, n: Integer;
|
i, j, n: Integer;
|
||||||
sheetref: PsBIFFExternSheet;
|
|
||||||
book: TsBIFFExternBook;
|
|
||||||
sheet: TsWorksheet;
|
sheet: TsWorksheet;
|
||||||
|
externSheetList: TsBIFFExternSheetList;
|
||||||
|
externsheet: TsBIFFExternSheet;
|
||||||
begin
|
begin
|
||||||
CollectExternData(FWorksheet);
|
i := CollectExternData(AWorksheet);
|
||||||
if (FExternBooks = nil) or (FExternSheets = nil) then
|
if i = -1 then
|
||||||
|
exit;
|
||||||
|
if FLinkLists[i] = nil then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
// Write the count of records in the local link table
|
externSheetlist := TsBIFFLinkListItem(FLinkLists.Items[i]).SheetList;
|
||||||
n := FExternSheets.Count;
|
if externSheetList = nil then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// Write the count of records to the local link table
|
||||||
|
n := externSheetList.Count;
|
||||||
WriteEXTERNCOUNT(AStream, word(n));
|
WriteEXTERNCOUNT(AStream, word(n));
|
||||||
|
|
||||||
// Write a EXTERNSHEET record for each linked sheet
|
// Write a EXTERNSHEET record for each linked sheet
|
||||||
for i := 0 to n-1 do begin
|
for i := 0 to externSheetList.Count-1 do begin
|
||||||
sheetref := FExternSheets[i];
|
externSheet := externSheetList[i];
|
||||||
book := FExternBooks[sheetref^.ExternBookIndex];
|
if externSheet.Kind = ebkInternal then
|
||||||
if book.Kind = ebkInternal then
|
WriteEXTERNSHEET(AStream, externSheet.SheetName, true)
|
||||||
begin
|
|
||||||
for j := sheetref^.FirstSheetIndex to sheetref^.LastSheetIndex do
|
|
||||||
begin
|
|
||||||
sheet := FWorkbook.GetWorksheetByIndex(j);
|
|
||||||
if sheet = AWorksheet then
|
|
||||||
WriteEXTERNSHEET(AStream, '', true)
|
|
||||||
else
|
else
|
||||||
WriteEXTERNSHEET(AStream, sheet.Name, true);
|
|
||||||
end;
|
|
||||||
end else
|
|
||||||
begin
|
|
||||||
// Handle external links here
|
// Handle external links here
|
||||||
end;
|
;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
(*
|
(*
|
||||||
@ -1991,40 +1989,35 @@ function TsSpreadBIFF5Writer.WriteRPNSheetIndex(AStream: TStream;
|
|||||||
ADocumentURL: String; ASheet1, ASheet2: Integer): Word;
|
ADocumentURL: String; ASheet1, ASheet2: Integer): Word;
|
||||||
var
|
var
|
||||||
p: Int64;
|
p: Int64;
|
||||||
bookidx: Integer;
|
externSheetList: TsBIFFExternSheetList;
|
||||||
book: TsBIFFExternBook;
|
externSheetIdx1, externSheetIdx2: Integer;
|
||||||
refidx: Integer;
|
s: String;
|
||||||
sheetref: PsBIFFExternSheet;
|
|
||||||
begin
|
begin
|
||||||
if ADocumentURL <> '' then // Supporting only internal links
|
if ADocumentURL <> '' then // Supporting only internal links
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
p := AStream.Position;
|
p := AStream.Position;
|
||||||
|
|
||||||
// Find stored information on this link
|
externSheetList := FLinkLists.GetLocalLinks(FWorksheet);
|
||||||
bookidx := FExternBooks.FindBook(ADocumentURL);
|
|
||||||
refidx := FExternSheets.FindSheets(ADocumentURL, ASheet1, ASheet2);
|
s := FWorkbook.GetWorksheetByIndex(ASheet1).Name;
|
||||||
sheetref := FExternSheets[refidx];
|
externSheetIdx1 := externSheetList.IndexOfSheet(s);
|
||||||
book := FExternBooks[sheetRef^.ExternBookIndex];
|
|
||||||
if book.Kind = ebkExternal then
|
if ASheet2 = -1 then
|
||||||
exit($FFFF);
|
ASheet2 := ASheet1;
|
||||||
|
|
||||||
// One-based index of the EXTERNBOOK record to which this reference belongs.
|
// One-based index of the EXTERNBOOK record to which this reference belongs.
|
||||||
// For internal references ("3D references") this must be written as a
|
// For internal references ("3D references") this must be written as a
|
||||||
// negative value.
|
// negative value.
|
||||||
AStream.WriteWord(WordToLE(word(-(bookidx+1))));
|
AStream.WriteWord(WordToLE(word(-(externSheetIdx1 + 1))));
|
||||||
|
|
||||||
// 8 unused bytes
|
// 8 unused bytes
|
||||||
AStream.WriteDWord(0);
|
AStream.WriteQWord(0);
|
||||||
AStream.WriteDWord(0);
|
|
||||||
|
|
||||||
// Zero-based index to first referenced sheet (FFFFH = deleted sheet)
|
// Zero-based index to first referenced sheet of the workbook (FFFFH = deleted sheet)
|
||||||
AStream.WriteWord(WordToLE(ASheet1));
|
AStream.WriteWord(WordToLE(ASheet1));
|
||||||
|
|
||||||
// Single sheet reference
|
// Zero-based index to last referenced sheet of the workbook (FFFFH = deleted sheet)
|
||||||
if ASheet2 < 0 then ASheet2 := ASheet1;
|
|
||||||
|
|
||||||
// Zero-based index to last referenced sheet (FFFFH = deleted sheet)
|
|
||||||
AStream.WriteWord(WordToLE(ASheet2));
|
AStream.WriteWord(WordToLE(ASheet2));
|
||||||
|
|
||||||
Result := AStream.Position - p;
|
Result := AStream.Position - p;
|
||||||
|
@ -64,6 +64,56 @@ uses
|
|||||||
fpsutils;
|
fpsutils;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
{ TsBiff8ExternSheet - Information on sheets used in out-of-sheet references }
|
||||||
|
TsBIFF8ExternSheet = packed record
|
||||||
|
ExternBookIndex: Word;
|
||||||
|
FirstSheetIndex: Word;
|
||||||
|
LastSheetIndex: Word;
|
||||||
|
end;
|
||||||
|
PsBIFF8ExternSheet = ^TsBIFF8ExternSheet;
|
||||||
|
|
||||||
|
{ TsBIFF8ExternBook - Information on where out-of-sheet references are stored. }
|
||||||
|
TsBIFF8ExternBook = class
|
||||||
|
Kind: TsBIFFExternKind;
|
||||||
|
// The following fields are used only for external workbooks.
|
||||||
|
DocumentURL: String;
|
||||||
|
SheetNames: String; // List of worksheetnames separated by #1
|
||||||
|
function GetWorksheetName(AIndex: Integer): String;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TsBIFF8ExternBookList }
|
||||||
|
TsBIFF8ExternBookList = class(TFPObjectlist)
|
||||||
|
private
|
||||||
|
function GetItem(AIndex: Integer): TsBIFF8ExternBook;
|
||||||
|
procedure SetItem(AIndex: Integer; AValue: TsBIFF8ExternBook);
|
||||||
|
public
|
||||||
|
function AddBook(ABookName: String; ASheetNames: TStrings): Integer;
|
||||||
|
function AddInternal: Integer;
|
||||||
|
function FindBook(ABookName: String): TsBIFF8ExternBook;
|
||||||
|
function FindInternalBook: TsBIFF8ExternBook;
|
||||||
|
function IndexOfBook(ABookName: String): Integer;
|
||||||
|
function IndexOfInternalbook: Integer;
|
||||||
|
property Items[AIndex: Integer]: TsBIFF8ExternBook read GetItem write SetItem; default;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ A list for sheets used in out-of-sheet references }
|
||||||
|
TsBIFF8ExternSheetList = class(TFPList)
|
||||||
|
private
|
||||||
|
FBookList: TsBIFF8ExternBookList;
|
||||||
|
function GetItem(AIndex: Integer): PsBIFF8ExternSheet;
|
||||||
|
procedure SetItem(AIndex: Integer; AValue: PsBIFF8ExternSheet);
|
||||||
|
public
|
||||||
|
constructor Create(ABookList: TsBIFF8ExternBookList);
|
||||||
|
destructor Destroy; override;
|
||||||
|
function AddInternalSheets(ASheetIndex1, ASheetIndex2: Integer): Integer;
|
||||||
|
function AddSheets(ABookName: String; ASheetNames: TStrings;
|
||||||
|
ASheetIndex1, ASheetIndex2: Integer): Integer;
|
||||||
|
procedure Clear;
|
||||||
|
function IndexOfSheets(ABookName: String; ASheetIndex1, ASheetIndex2: Integer): Integer;
|
||||||
|
property Item[AIndex: Integer]: PsBIFF8ExternSheet read GetItem write SetItem; default;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TsSpreadBIFF8Reader }
|
{ TsSpreadBIFF8Reader }
|
||||||
TsSpreadBIFF8Reader = class(TsSpreadBIFFReader)
|
TsSpreadBIFF8Reader = class(TsSpreadBIFFReader)
|
||||||
private
|
private
|
||||||
@ -73,8 +123,8 @@ type
|
|||||||
FCommentPending: Boolean;
|
FCommentPending: Boolean;
|
||||||
FCommentID: Integer;
|
FCommentID: Integer;
|
||||||
FCommentLen: Integer;
|
FCommentLen: Integer;
|
||||||
FBiff8ExternBooks: TFPObjectList;
|
FBiff8ExternBooks: TsBiff8ExternBookList;
|
||||||
FBiff8ExternSheets: array of TsBiffExternSheet;
|
FBiff8ExternSheetArray: array of TsBiff8ExternSheet;
|
||||||
function ReadString(const AStream: TStream; const ALength: Word;
|
function ReadString(const AStream: TStream; const ALength: Word;
|
||||||
out ARichTextParams: TsRichTextParams): String;
|
out ARichTextParams: TsRichTextParams): String;
|
||||||
function ReadUnformattedWideString(const AStream: TStream;
|
function ReadUnformattedWideString(const AStream: TStream;
|
||||||
@ -89,7 +139,7 @@ type
|
|||||||
procedure ReadCONTINUE(const AStream: TStream);
|
procedure ReadCONTINUE(const AStream: TStream);
|
||||||
procedure ReadDEFINEDNAME(const AStream: TStream);
|
procedure ReadDEFINEDNAME(const AStream: TStream);
|
||||||
procedure ReadEXTERNBOOK(const AStream: TStream);
|
procedure ReadEXTERNBOOK(const AStream: TStream);
|
||||||
procedure ReadEXTERNSHEET(const AStream: TStream); virtual;
|
procedure ReadEXTERNSHEET(const AStream: TStream);
|
||||||
procedure ReadFONT(const AStream: TStream);
|
procedure ReadFONT(const AStream: TStream);
|
||||||
procedure ReadFORMAT(AStream: TStream); override;
|
procedure ReadFORMAT(AStream: TStream); override;
|
||||||
procedure ReadHeaderFooter(AStream: TStream; AIsHeader: Boolean); override;
|
procedure ReadHeaderFooter(AStream: TStream; AIsHeader: Boolean); override;
|
||||||
@ -137,8 +187,11 @@ type
|
|||||||
private
|
private
|
||||||
FSharedStringTable: TStringList;
|
FSharedStringTable: TStringList;
|
||||||
FNumStrings: DWord;
|
FNumStrings: DWord;
|
||||||
|
FBiff8ExternBooks: TsBIFF8ExternbookList;
|
||||||
|
FBiff8ExternSheets: TsBIFF8ExternSheetList;
|
||||||
private
|
private
|
||||||
procedure BeginCONTINUERecord(AStream: TStream; out ASizePos: Int64);
|
procedure BeginCONTINUERecord(AStream: TStream; out ASizePos: Int64);
|
||||||
|
procedure CollectExternData;
|
||||||
procedure FixRecordSize(AStream: TStream; ASizePos: Int64; ASize: Word);
|
procedure FixRecordSize(AStream: TStream; ASizePos: Int64; ASize: Word);
|
||||||
function WriteStringHelper(AStream: TStream; const AText: RawByteString;
|
function WriteStringHelper(AStream: TStream; const AText: RawByteString;
|
||||||
const ARichTextParams: TsRichTextParams; Is8BitString: Boolean;
|
const ARichTextParams: TsRichTextParams; Is8BitString: Boolean;
|
||||||
@ -159,10 +212,12 @@ type
|
|||||||
procedure WriteComment(AStream: TStream; ACell: PCell); override;
|
procedure WriteComment(AStream: TStream; ACell: PCell); override;
|
||||||
procedure WriteComments(AStream: TStream; AWorksheet: TsWorksheet);
|
procedure WriteComments(AStream: TStream; AWorksheet: TsWorksheet);
|
||||||
procedure WriteDefinedName(AStream: TStream; AWorksheet: TsWorksheet;
|
procedure WriteDefinedName(AStream: TStream; AWorksheet: TsWorksheet;
|
||||||
const AName: String; AIndexToREF: Word); override;
|
const AName: String; AIndexToREF, ASheetIndex: Word;
|
||||||
|
AKind: TsBIFFExternKind);
|
||||||
|
procedure WriteDefinedNames(AStream: TStream);
|
||||||
procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
|
procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
|
||||||
procedure WriteEOF(AStream: TStream);
|
procedure WriteEOF(AStream: TStream);
|
||||||
procedure WriteEXTERNBOOK(AStream: TStream);
|
procedure WriteEXTERNBOOK(AStream: TStream; AUrl: String);
|
||||||
procedure WriteEXTERNSHEET(AStream: TStream);
|
procedure WriteEXTERNSHEET(AStream: TStream);
|
||||||
procedure WriteFONT(AStream: TStream; AFont: TsFont);
|
procedure WriteFONT(AStream: TStream; AFont: TsFont);
|
||||||
procedure WriteFonts(AStream: TStream);
|
procedure WriteFonts(AStream: TStream);
|
||||||
@ -189,10 +244,6 @@ type
|
|||||||
out AContinueInString: Boolean): Boolean;
|
out AContinueInString: Boolean): Boolean;
|
||||||
function WriteRPNCellAddress(AStream: TStream; ARow, ACol: Cardinal;
|
function WriteRPNCellAddress(AStream: TStream; ARow, ACol: Cardinal;
|
||||||
AFlags: TsRelFlags): word; override;
|
AFlags: TsRelFlags): word; override;
|
||||||
(*
|
|
||||||
function WriteRPNCellAddress3D(AStream: TStream; ASheet, ARow, ACol: Cardinal;
|
|
||||||
AFlags: TsRelFlags): Word; override;
|
|
||||||
*)
|
|
||||||
function WriteRPNCellOffset(AStream: TStream; ARowOffset, AColOffset: Integer;
|
function WriteRPNCellOffset(AStream: TStream; ARowOffset, AColOffset: Integer;
|
||||||
AFlags: TsRelFlags): Word; override;
|
AFlags: TsRelFlags): Word; override;
|
||||||
function WriteRPNCellRangeAddress(AStream: TStream; ARow1, ACol1, ARow2, ACol2: Cardinal;
|
function WriteRPNCellRangeAddress(AStream: TStream; ARow1, ACol1, ARow2, ACol2: Cardinal;
|
||||||
@ -490,14 +541,235 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure InitBIFF8Limitations(out ALimitations: TsSpreadsheetFormatLimitations);
|
{ -----------------------------------------------------------------------------}
|
||||||
|
{ TsBIFF8ExternBook }
|
||||||
|
{ -----------------------------------------------------------------------------}
|
||||||
|
function TsBIFF8ExternBook.GetWorksheetName(AIndex: Integer): String;
|
||||||
|
var
|
||||||
|
L: TStrings;
|
||||||
begin
|
begin
|
||||||
InitBiffLimitations(ALimitations);
|
Result := '';
|
||||||
|
if Kind = ebkExternal then begin
|
||||||
|
L := TStringList.Create;
|
||||||
|
try
|
||||||
|
L.Delimiter := #1;
|
||||||
|
L.DelimitedText := SheetNames;
|
||||||
|
Result := L[AIndex];
|
||||||
|
finally
|
||||||
|
L.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TsSpreadBIFF8Reader }
|
{------------------------------------------------------------------------------}
|
||||||
|
{ TsBIFF8ExternBookList }
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
function TsBIFF8ExternBookList.AddBook(ABookName: String;
|
||||||
|
ASheetNames: TStrings): Integer;
|
||||||
|
var
|
||||||
|
book: TsBIFF8ExternBook;
|
||||||
|
s: String;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
if ABookName = '' then
|
||||||
|
Result := AddInternal
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
Result := IndexOfBook(ABookName);
|
||||||
|
if Result = -1 then begin
|
||||||
|
book := TsBIFF8ExternBook.Create;
|
||||||
|
book.DocumentURL := ABookName;
|
||||||
|
book.Kind := ebkExternal;
|
||||||
|
if ASheetNames.Count > 0 then begin
|
||||||
|
s := ASheetNames[0];
|
||||||
|
for i:=1 to ASheetNames.Count-1 do
|
||||||
|
s := s + #1 + ASheetNames[i];
|
||||||
|
book.SheetNames := s;
|
||||||
|
end;
|
||||||
|
Result := Add(book);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsBIFF8ExternBookList.AddInternal: Integer;
|
||||||
|
var
|
||||||
|
book: TsBIFF8ExternBook;
|
||||||
|
begin
|
||||||
|
Result := IndexOfInternalBook;
|
||||||
|
if Result = -1 then begin
|
||||||
|
book := TsBIFF8ExternBook.Create;
|
||||||
|
book.Kind := ebkInternal;
|
||||||
|
Result := Add(book);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsBIFF8ExternBookList.FindBook(ABookName: String): TsBIFF8ExternBook;
|
||||||
|
var
|
||||||
|
idx: Integer;
|
||||||
|
begin
|
||||||
|
idx := IndexOfBook(ABookName);
|
||||||
|
if idx <> -1 then
|
||||||
|
Result := Items[idx]
|
||||||
|
else
|
||||||
|
Result := nil;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsBIFF8ExternBookList.FindInternalBook: TsBIFF8ExternBook;
|
||||||
|
var
|
||||||
|
idx: Integer;
|
||||||
|
begin
|
||||||
|
idx := IndexOfInternalBook;
|
||||||
|
if idx <> -1 then
|
||||||
|
Result := Items[idx]
|
||||||
|
else
|
||||||
|
Result := nil;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsBIFF8ExternBookList.GetItem(AIndex: Integer): TsBIFF8ExternBook;
|
||||||
|
begin
|
||||||
|
Result := TsBIFF8ExternBook(inherited Items[AIndex]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsBIFF8ExternBookList.IndexOfBook(ABookName: String): Integer;
|
||||||
|
var
|
||||||
|
book: TsBIFF8ExternBook;
|
||||||
|
begin
|
||||||
|
if ABookName = '' then
|
||||||
|
Result := IndexOfInternalBook
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
for Result := 0 to Count-1 do
|
||||||
|
begin
|
||||||
|
book := Items[Result];
|
||||||
|
if (book.Kind = ebkExternal) and (book.DocumentURL = ABookName) then
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
Result := -1;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsBIFF8ExternBookList.IndexOfInternalBook: Integer;
|
||||||
|
begin
|
||||||
|
for Result := 0 to Count-1 do
|
||||||
|
if Items[Result].Kind = ebkInternal then exit;
|
||||||
|
Result := -1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TsBIFF8ExternBookList.SetItem(AIndex: Integer;
|
||||||
|
AValue: TsBIFF8ExternBook);
|
||||||
|
begin
|
||||||
|
inherited Items[AIndex] := AValue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
{ TsBIFF8ExternSheetList }
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
constructor TsBIFF8ExternSheetList.Create(ABookList: TsBIFF8ExternBookList);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FBookList := ABookList;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TsBIFF8ExternSheetList.Destroy;
|
||||||
|
begin
|
||||||
|
Clear;
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsBIFF8ExternSheetList.AddInternalSheets(
|
||||||
|
ASheetIndex1, ASheetIndex2: Integer): Integer;
|
||||||
|
begin
|
||||||
|
Result := AddSheets('', nil, ASheetIndex1, ASheetIndex2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsBIFF8ExternSheetList.AddSheets(ABookName: String;
|
||||||
|
ASheetNames: TStrings; ASheetIndex1, ASheetIndex2: Integer): Integer;
|
||||||
|
var
|
||||||
|
P: PsBIFF8ExternSheet;
|
||||||
|
idx: Integer;
|
||||||
|
begin
|
||||||
|
Result := IndexOfSheets(ABookName, ASheetIndex1, ASheetIndex2);
|
||||||
|
if Result = -1 then
|
||||||
|
begin
|
||||||
|
New(P);
|
||||||
|
idx := FBookList.IndexOfBook(ABookName);
|
||||||
|
if idx = -1 then
|
||||||
|
idx := FBookList.AddBook(ABookName, ASheetNames);
|
||||||
|
P^.ExternBookIndex := idx;
|
||||||
|
|
||||||
|
if ASheetIndex2 = -1 then
|
||||||
|
ASheetIndex2 := ASheetIndex1;
|
||||||
|
if ASheetIndex2 < ASheetIndex1 then
|
||||||
|
begin
|
||||||
|
P^.FirstSheetIndex := ASheetIndex2;
|
||||||
|
P^.LastSheetIndex := ASheetIndex1;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
P^.FirstSheetIndex := ASheetIndex1;
|
||||||
|
P^.LastSheetIndex := ASheetIndex2;
|
||||||
|
end;
|
||||||
|
Result := Add(P);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TsBIFF8ExternSheetList.Clear;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
P: PsBIFF8ExternSheet;
|
||||||
|
begin
|
||||||
|
for i:=0 to Count-1 do begin
|
||||||
|
P := Item[i];
|
||||||
|
Dispose(P);
|
||||||
|
end;
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsBIFF8ExternSheetList.IndexOfSheets(ABookName: String;
|
||||||
|
ASheetIndex1, ASheetIndex2: Integer): Integer;
|
||||||
|
var
|
||||||
|
book: TsBIFF8ExternBook;
|
||||||
|
P: PsBIFF8ExternSheet;
|
||||||
|
tmp: Integer;
|
||||||
|
idx: Integer;
|
||||||
|
begin
|
||||||
|
if ASheetIndex2 = -1 then ASheetIndex2 := ASheetIndex1;
|
||||||
|
if ASheetIndex2 < ASheetIndex1 then begin
|
||||||
|
tmp := ASheetIndex1;
|
||||||
|
ASheetIndex1 := ASheetIndex2;
|
||||||
|
ASheetIndex2 := tmp;
|
||||||
|
end;
|
||||||
|
|
||||||
|
idx := FBookList.IndexOfBook(ABookName);
|
||||||
|
if idx = -1 then
|
||||||
|
exit(-1);
|
||||||
|
|
||||||
|
for Result := 0 to Count-1 do begin
|
||||||
|
P := Item[Result];
|
||||||
|
if (P^.ExternBookIndex = idx) and
|
||||||
|
(P^.FirstSheetIndex = ASheetIndex1) and
|
||||||
|
(P^.LastSheetIndex = ASheetIndex2)
|
||||||
|
then
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
Result := -1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsBIFF8ExternSheetList.GetItem(AIndex: Integer): PsBIFF8ExternSheet;
|
||||||
|
begin
|
||||||
|
Result := PsBIFF8ExternSheet(inherited Items[AIndex]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TsBIFF8ExternSheetList.SetItem(AIndex: Integer; AValue: PsBIFF8ExternSheet);
|
||||||
|
begin
|
||||||
|
inherited Items[AIndex] := AValue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
{ TsSpreadBIFF8Reader }
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
constructor TsSpreadBIFF8Reader.Create(AWorkbook: TsWorkbook);
|
constructor TsSpreadBIFF8Reader.Create(AWorkbook: TsWorkbook);
|
||||||
begin
|
begin
|
||||||
inherited;
|
inherited;
|
||||||
@ -508,9 +780,11 @@ destructor TsSpreadBIFF8Reader.Destroy;
|
|||||||
var
|
var
|
||||||
j: Integer;
|
j: Integer;
|
||||||
begin
|
begin
|
||||||
SetLength(FBiff8ExternSheets, 0);
|
{ Destroy linked data }
|
||||||
|
SetLength(FBiff8ExternSheetArray, 0);
|
||||||
FBiff8ExternBooks.Free;
|
FBiff8ExternBooks.Free;
|
||||||
|
|
||||||
|
{ Destroy shared string table }
|
||||||
if Assigned(FSharedStringTable) then
|
if Assigned(FSharedStringTable) then
|
||||||
begin
|
begin
|
||||||
for j := FSharedStringTable.Count-1 downto 0 do
|
for j := FSharedStringTable.Count-1 downto 0 do
|
||||||
@ -1322,15 +1596,15 @@ end;
|
|||||||
procedure TsSpreadBIFF8Reader.ReadRPNSheetIndex(AStream: TStream;
|
procedure TsSpreadBIFF8Reader.ReadRPNSheetIndex(AStream: TStream;
|
||||||
out ADocumentURL: String; out ASheet1, ASheet2: Integer);
|
out ADocumentURL: String; out ASheet1, ASheet2: Integer);
|
||||||
var
|
var
|
||||||
refIndex: Word;
|
refIndex: Int16;
|
||||||
ref: TsBiffExternSheet;
|
ref: TsBiff8ExternSheet;
|
||||||
book: TsBiffExternBook;
|
book: TsBiff8ExternBook;
|
||||||
begin
|
begin
|
||||||
// Index to REF entry in EXTERNSHEET record
|
// Index to REF entry in EXTERNSHEET record
|
||||||
refIndex := WordLEToN(AStream.ReadWord);
|
refIndex := WordLEToN(AStream.ReadWord);
|
||||||
|
|
||||||
ref := FBiff8ExternSheets[refIndex];
|
ref := FBiff8ExternSheetArray[refIndex];
|
||||||
book := FBiff8ExternBooks[ref.ExternBookIndex] as TsBiffExternBook;
|
book := FBiff8ExternBooks[ref.ExternBookIndex] as TsBiff8ExternBook;
|
||||||
|
|
||||||
// Only links to internal sheets supported so far.
|
// Only links to internal sheets supported so far.
|
||||||
if book.Kind <> ebkInternal then
|
if book.Kind <> ebkInternal then
|
||||||
@ -1841,14 +2115,14 @@ var
|
|||||||
i, n: Integer;
|
i, n: Integer;
|
||||||
url: widestring;
|
url: widestring;
|
||||||
sheetnames: widestring;
|
sheetnames: widestring;
|
||||||
book: TsBiffExternbook;
|
book: TsBiff8Externbook;
|
||||||
p: Int64;
|
p: Int64;
|
||||||
t: array[0..1] of byte = (0, 0);
|
t: array[0..1] of byte = (0, 0);
|
||||||
begin
|
begin
|
||||||
if FBiff8ExternBooks = nil then
|
if FBiff8ExternBooks = nil then
|
||||||
FBiff8ExternBooks := TsBIFFExternBookList.Create(true);
|
FBiff8ExternBooks := TsBIFF8ExternBookList.Create(true);
|
||||||
|
|
||||||
book := TsBiffExternBook.Create;
|
book := TsBiff8ExternBook.Create;
|
||||||
|
|
||||||
// Count of sheets in book
|
// Count of sheets in book
|
||||||
n := WordLEToN(AStream.ReadWord);
|
n := WordLEToN(AStream.ReadWord);
|
||||||
@ -1897,11 +2171,11 @@ var
|
|||||||
i: Integer;
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
numItems := WordLEToN(AStream.ReadWord);
|
numItems := WordLEToN(AStream.ReadWord);
|
||||||
SetLength(FBiff8ExternSheets, numItems);
|
SetLength(FBiff8ExternSheetArray, numItems);
|
||||||
|
|
||||||
for i := 0 to numItems-1 do begin
|
for i := 0 to numItems-1 do begin
|
||||||
AStream.ReadBuffer(FBiff8ExternSheets[i], Sizeof(FBiff8ExternSheets[i]));
|
AStream.ReadBuffer(FBiff8ExternSheetArray[i], Sizeof(FBiff8ExternSheetArray[i]));
|
||||||
with FBiff8ExternSheets[i] do
|
with FBiff8ExternSheetArray[i] do
|
||||||
begin
|
begin
|
||||||
ExternBookIndex := WordLEToN(ExternBookIndex);
|
ExternBookIndex := WordLEToN(ExternBookIndex);
|
||||||
FirstSheetIndex := WordLEToN(FirstSheetIndex);
|
FirstSheetIndex := WordLEToN(FirstSheetIndex);
|
||||||
@ -2244,6 +2518,8 @@ end;
|
|||||||
destructor TsSpreadBIFF8Writer.Destroy;
|
destructor TsSpreadBIFF8Writer.Destroy;
|
||||||
begin
|
begin
|
||||||
FSharedStringTable.Free;
|
FSharedStringTable.Free;
|
||||||
|
FBiff8ExternSheets.Free;
|
||||||
|
FBiff8ExternBooks.Free;
|
||||||
inherited;
|
inherited;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -2260,6 +2536,79 @@ begin
|
|||||||
AStream.WriteWord(0);
|
AStream.WriteWord(0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Collects the data for out-of-sheet links found in the specified worksheet
|
||||||
|
(or all worksheets if the parameter is omitted).
|
||||||
|
The found data are written to the FExternBooks and FExternSheets lists.
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
procedure TsSpreadBIFF8Writer.CollectExternData;
|
||||||
|
|
||||||
|
procedure DoCollectForSheet(ASheet: TsWorksheet);
|
||||||
|
var
|
||||||
|
cell: PCell;
|
||||||
|
parser: TsExpressionParser;
|
||||||
|
rpn: TsRPNFormula;
|
||||||
|
fe: TsFormulaElement;
|
||||||
|
j: Integer;
|
||||||
|
begin
|
||||||
|
for cell in ASheet.Cells do
|
||||||
|
begin
|
||||||
|
if not HasFormula(cell) then
|
||||||
|
Continue;
|
||||||
|
if not (cf3dFormula in cell^.Flags) then
|
||||||
|
Continue;
|
||||||
|
|
||||||
|
parser := TsSpreadsheetParser.Create(ASheet);
|
||||||
|
try
|
||||||
|
parser.Expression := cell^.FormulaValue;
|
||||||
|
rpn := parser.RPNFormula;
|
||||||
|
for j:=0 to High(rpn) do
|
||||||
|
begin
|
||||||
|
fe := rpn[j];
|
||||||
|
if fe.ElementKind in [fekCell3d, fekCellRef3d, fekCellRange3d] then
|
||||||
|
FBiff8ExternSheets.AddSheets('', nil, fe.Sheet, fe.Sheet2);
|
||||||
|
// FIXME: '' --> supporting only internal 3d links so far
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
parser.Free;
|
||||||
|
rpn := nil;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
sheet: TsWorksheet;
|
||||||
|
i: Integer;
|
||||||
|
writeIt: Boolean;
|
||||||
|
begin
|
||||||
|
if FBiff8ExternBooks <> nil then
|
||||||
|
raise Exception.Create('[TsSpreadBIFF8Writer.CollectExternData] Can be entered only once.');
|
||||||
|
|
||||||
|
FBiff8ExternBooks := TsBIFF8ExternBookList.Create;
|
||||||
|
FBiff8ExternSheets := TsBIFF8ExternSheetList.Create(FBiff8ExternBooks);
|
||||||
|
|
||||||
|
{ Add sheets used in print ranges, repeated cols or repeated rows }
|
||||||
|
for i:=0 to FWorkbook.GetWorksheetCount-1 do begin
|
||||||
|
sheet := FWorkbook.GetWorksheetByIndex(i);
|
||||||
|
with sheet.PageLayout do
|
||||||
|
writeIt := (NumPrintRanges > 0) or HasRepeatedCols or HasRepeatedRows;
|
||||||
|
if writeIt then
|
||||||
|
FBiff8ExternSheets.AddInternalSheets(i, i);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Add sheets related to 3d references of all sheets }
|
||||||
|
for i:=0 to FWorkbook.GetWorksheetCount-1 do
|
||||||
|
begin
|
||||||
|
sheet := FWorkbook.GetWorksheetByIndex(i);
|
||||||
|
DoCollectForSheet(sheet);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if FBiff8ExternSheets.Count = 0 then begin
|
||||||
|
FreeAndNil(FBiff8ExternSheets);
|
||||||
|
FreeAndNil(FBiff8ExternBooks);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Sometimes the size of records is not known when writing them (see
|
Sometimes the size of records is not known when writing them (see
|
||||||
BeginCONTINUERecord). This method rewinds the stream to the position where
|
BeginCONTINUERecord). This method rewinds the stream to the position where
|
||||||
@ -2344,7 +2693,7 @@ begin
|
|||||||
for i := 0 to Workbook.GetWorksheetCount - 1 do
|
for i := 0 to Workbook.GetWorksheetCount - 1 do
|
||||||
sheetPos[i] := WriteBoundsheet(AStream, Workbook.GetWorksheetByIndex(i));
|
sheetPos[i] := WriteBoundsheet(AStream, Workbook.GetWorksheetByIndex(i));
|
||||||
|
|
||||||
WriteEXTERNBOOK(AStream);
|
WriteEXTERNBOOK(AStream, '');
|
||||||
WriteEXTERNSHEET(AStream);
|
WriteEXTERNSHEET(AStream);
|
||||||
WriteDefinedNames(AStream);
|
WriteDefinedNames(AStream);
|
||||||
WriteSST(AStream);
|
WriteSST(AStream);
|
||||||
@ -2661,7 +3010,8 @@ end;
|
|||||||
Implements only the builtin defined names for print ranges and titles!
|
Implements only the builtin defined names for print ranges and titles!
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsSpreadBIFF8Writer.WriteDefinedName(AStream: TStream;
|
procedure TsSpreadBIFF8Writer.WriteDefinedName(AStream: TStream;
|
||||||
AWorksheet: TsWorksheet; const AName: String; AIndexToREF: Word);
|
AWorksheet: TsWorksheet; const AName: String; AIndexToREF, ASheetIndex: Word;
|
||||||
|
AKind: TsBIFFExternKind);
|
||||||
|
|
||||||
procedure WriteRangeFormula(MemStream: TMemoryStream; ARange: TsCellRange;
|
procedure WriteRangeFormula(MemStream: TMemoryStream; ARange: TsCellRange;
|
||||||
AIndexToRef, ACounter: Word);
|
AIndexToRef, ACounter: Word);
|
||||||
@ -2782,6 +3132,38 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TsSpreadBIFF8Writer.WriteDefinedNames(AStream: TStream);
|
||||||
|
var
|
||||||
|
externbook: TsBIFF8ExternBook;
|
||||||
|
bookIdx: Integer;
|
||||||
|
sheet: TsWorksheet;
|
||||||
|
i: Integer;
|
||||||
|
idx: Word;
|
||||||
|
extSheetIdx: Integer;
|
||||||
|
sheetList: TsBIFFExternSheetList;
|
||||||
|
begin
|
||||||
|
if (FBiff8ExternBooks = nil) or (FBiff8ExternSheets = nil) then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// Defined names in "internal" book only
|
||||||
|
bookIdx := FBiff8ExternBooks.IndexOfInternalbook;
|
||||||
|
|
||||||
|
for i:=0 to FWorkbook.GetWorksheetCount-1 do
|
||||||
|
begin
|
||||||
|
sheet := FWorkbook.GetWorksheetByIndex(i);
|
||||||
|
if (sheet.PageLayout.NumPrintRanges > 0) or
|
||||||
|
sheet.PageLayout.HasRepeatedCols or sheet.PageLayout.HasRepeatedRows then
|
||||||
|
begin
|
||||||
|
// idx := sheetList.IndexOfSheet(sheet.Name);
|
||||||
|
// Write 1-based index. And negate it to indicate an internal reference.
|
||||||
|
if sheet.PageLayout.NumPrintRanges > 0 then
|
||||||
|
WriteDefinedName(AStream, sheet, #6, bookIdx, i, ebkInternal);
|
||||||
|
if sheet.PageLayout.HasRepeatedCols or sheet.PageLayout.HasRepeatedRows then
|
||||||
|
WriteDefinedName(AStream, sheet, #7, bookIdx, i, ebkInternal);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Writes an Excel 8 DIMENSIONS record
|
Writes an Excel 8 DIMENSIONS record
|
||||||
|
|
||||||
@ -2829,41 +3211,47 @@ end;
|
|||||||
NOTE: This writes only the case for "internal references" required for print
|
NOTE: This writes only the case for "internal references" required for print
|
||||||
ranges and titles.
|
ranges and titles.
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsSpreadBIFF8Writer.WriteEXTERNBOOK(AStream: TStream);
|
procedure TsSpreadBIFF8Writer.WriteEXTERNBOOK(AStream: TStream; AUrl: string);
|
||||||
begin
|
begin
|
||||||
if (FExternBooks = nil) or (FExternBooks.Count = 0) then
|
if (FBiff8ExternBooks = nil) or (FBiff8ExternBooks.Count = 0) then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
{ BIFF record header }
|
{ BIFF record header }
|
||||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_EXTERNBOOK, 4);
|
WriteBIFFHeader(AStream, INT_EXCEL_ID_EXTERNBOOK, 4);
|
||||||
|
|
||||||
|
// To do: When external books are activated then the "4" must be replaced !!!
|
||||||
|
|
||||||
|
{ Current workbook -- assuming that it has index 0 in list FExternBook8 }
|
||||||
|
if AUrl = '' then begin
|
||||||
{ Number of sheets in this workbook }
|
{ Number of sheets in this workbook }
|
||||||
AStream.WriteWord(WordToLE(FWorkbook.GetWorksheetCount));
|
AStream.WriteWord(WordToLE(FWorkbook.GetWorksheetCount));
|
||||||
|
|
||||||
{ Relict from BIFF5 }
|
{ Relict from BIFF5 }
|
||||||
AStream.WriteWord(WordToLE($0401));
|
AStream.WriteWord(WordToLE($0401));
|
||||||
|
end else
|
||||||
|
raise Exception.Create('[WriteEXTERNBOOK] External books not supported.');
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Writes an EXTERNSHEET record needed for defined names and links.
|
Writes an EXTERNSHEET record needed for defined names and links.
|
||||||
NOTE: This writes only what is required for print ranges and titles.
|
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsSpreadBIFF8Writer.WriteEXTERNSHEET(AStream: TStream);
|
procedure TsSpreadBIFF8Writer.WriteEXTERNSHEET(AStream: TStream);
|
||||||
var
|
var
|
||||||
n, i: Integer;
|
n, i: Integer;
|
||||||
sheetRef: PsBIFFExternSheet;
|
sheetRef: PsBIFF8ExternSheet;
|
||||||
book: TsBIFFExternBook;
|
book: TsBIFF8ExternBook;
|
||||||
begin
|
begin
|
||||||
if (FExternSheets = nil) or (FExternBooks = nil) then
|
if (FBiff8ExternSheets = nil) or (FBiff8ExternBooks = nil) then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
{ Count the following REF structures }
|
{ Count the following REF structures }
|
||||||
{ We support only internal links. Once external links are supported the
|
{ We support only internal links. Once external links are supported the
|
||||||
following code probably can be dropped. }
|
following code probably can be dropped. }
|
||||||
n := 0;
|
n := 0;
|
||||||
for i := 0 to FExternSheets.Count-1 do begin
|
for i := 0 to FBiff8ExternSheets.Count-1 do begin
|
||||||
sheetRef := FExternSheets[i];
|
sheetRef := FBiff8ExternSheets[i];
|
||||||
book := FExternBooks[sheetRef^.ExternBookIndex];
|
book := FBiff8ExternBooks[sheetRef^.ExternBookIndex];
|
||||||
if (book.Kind = ebkInternal) then inc(n);
|
if (book.Kind = ebkInternal) then inc(n);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -2873,9 +3261,9 @@ begin
|
|||||||
{ Write the determined count of REF structures }
|
{ Write the determined count of REF structures }
|
||||||
AStream.WriteWord(WordToLE(n));
|
AStream.WriteWord(WordToLE(n));
|
||||||
|
|
||||||
for i:= 0 to FExternSheets.Count-1 do begin
|
for i:= 0 to FBiff8ExternSheets.Count-1 do begin
|
||||||
sheetRef := FExternSheets[i];
|
sheetRef := FBiff8ExternSheets[i];
|
||||||
book := FExternBooks[sheetRef^.ExternBookIndex];
|
book := FBiff8ExternBooks[sheetRef^.ExternBookIndex];
|
||||||
if (book.Kind = ebkInternal) then
|
if (book.Kind = ebkInternal) then
|
||||||
begin
|
begin
|
||||||
AStream.WriteWord(WordToLE(sheetRef^.ExternBookIndex));
|
AStream.WriteWord(WordToLE(sheetRef^.ExternBookIndex));
|
||||||
@ -2885,94 +3273,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
(*
|
|
||||||
{ Since sheet range are not supported we simply note every sheet here. }
|
|
||||||
n := FWorkbook.GetWorksheetCount;
|
|
||||||
|
|
||||||
{ BIFF record header }
|
|
||||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_EXTERNSHEET, 2 + 6*n);
|
|
||||||
|
|
||||||
{ Count of following REF structures }
|
|
||||||
AStream.WriteWord(WordToLE(n));
|
|
||||||
|
|
||||||
{ REF record for each sheet }
|
|
||||||
for i := 0 to n-1 do
|
|
||||||
begin
|
|
||||||
AStream.WriteWord(0); // Index to EXTERNBOOK record, always 0
|
|
||||||
AStream.WriteWord(WordToLE(i)); // Index to first sheet in EXTERNBOOK sheet list
|
|
||||||
AStream.WriteWord(WordToLE(i)); // Index to last sheet in EXTERNBOOK sheet list
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
*)
|
|
||||||
(*
|
|
||||||
|
|
||||||
write a record for
|
|
||||||
// every sheet
|
|
||||||
type
|
|
||||||
TExternRefRec = record
|
|
||||||
FirstIndex, LastIndex: Word;
|
|
||||||
end;
|
|
||||||
const
|
|
||||||
BUF_COUNT = 10;
|
|
||||||
var
|
|
||||||
extern: Array of TExternRefRec;
|
|
||||||
sheet: TsWorksheet;
|
|
||||||
cell: PCell;
|
|
||||||
i, j: Integer;
|
|
||||||
n: Word;
|
|
||||||
writeIt: Boolean;
|
|
||||||
begin
|
|
||||||
|
|
||||||
n := 0;
|
|
||||||
SetLength(extern, BUF_COUNT);
|
|
||||||
|
|
||||||
// Find sheets used in formula references
|
|
||||||
for i:=0 to FWorkBook.GetWorksheetCount-1 do begin
|
|
||||||
sheet := FWorkBook.GetWorksheetByIndex(i);
|
|
||||||
for cell in sheet.Cells do
|
|
||||||
if HasFormula(cell) then
|
|
||||||
if pos('!', cell^.FormulaValue) > 0 then
|
|
||||||
for j:=0 to FWorkbook.GetWorksheetCount-1 do
|
|
||||||
if pos(FWorksbook.GetWorksheetByIndex(j).Name, cell1.FormulaValue) = 1 then begin
|
|
||||||
extern[n].FirstIndex := j;
|
|
||||||
extern[n].LastIndex := j;
|
|
||||||
// NOTE: This must be extended to allow a range of sheets !!!
|
|
||||||
inc(n);
|
|
||||||
if n mod BUF_COUNT = 0 then
|
|
||||||
Setlength(extern, Length(extern) + BUF_COUNT);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Find sheets used in print ranges, repeated cols or repeated rows
|
|
||||||
for i:=0 to FWorkbook.GetWorksheetCount-1 do begin
|
|
||||||
sheet := FWorkbook.GetWorksheetbyIndex(i);
|
|
||||||
with sheet.PageLayout do
|
|
||||||
writeIt := (NumPrintRanges > 0) or HasRepeatedCols or HasRepeatedRows;
|
|
||||||
if writeIt then begin
|
|
||||||
extern[n].FirstIndex := i;
|
|
||||||
extern[n].LastIndex := i;
|
|
||||||
inc(n);
|
|
||||||
if n mod BUF_COUNT = 0 then
|
|
||||||
SetLength(extern, Length(extern) + BUF_COUNT);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
SetLength(extern, n);
|
|
||||||
|
|
||||||
{ BIFF record header }
|
|
||||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_EXTERNSHEET, 2 + 6*n);
|
|
||||||
|
|
||||||
{ Count of following REF structures }
|
|
||||||
AStream.WriteWord(WordToLE(n));
|
|
||||||
|
|
||||||
{ REF record for each sheet }
|
|
||||||
for i := 0 to n-1 do
|
|
||||||
begin
|
|
||||||
AStream.WriteWord(0); // Index to EXTERNBOOK record, always 0
|
|
||||||
AStream.WriteWord(WordToLE(extern[i])); // Index to first sheet in EXTERNBOOK sheet list
|
|
||||||
AStream.WriteWord(WordToLE(extern[i])); // Index to last sheet in EXTERNBOOK sheet list
|
|
||||||
end;
|
|
||||||
end; *)
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Writes an Excel 8 FONT record.
|
Writes an Excel 8 FONT record.
|
||||||
@ -3820,9 +4120,9 @@ function TsSpreadBIFF8Writer.WriteRPNSheetIndex(AStream: TStream;
|
|||||||
var
|
var
|
||||||
idx: Integer;
|
idx: Integer;
|
||||||
begin
|
begin
|
||||||
idx := FExternSheets.FindSheets(ADocumentURL, ASheet1, ASheet2);
|
idx := FBiff8ExternSheets.IndexOfSheets(ADocumentURL, ASheet1, ASheet2);
|
||||||
if idx = -1 then
|
if idx = -1 then
|
||||||
Result := $FFFE
|
Result := $FFFE // E at the end: sheets not found
|
||||||
else begin
|
else begin
|
||||||
AStream.WriteWord(WordToLE(word(idx)));
|
AStream.WriteWord(WordToLE(word(idx)));
|
||||||
Result := 2;
|
Result := 2;
|
||||||
@ -4714,6 +5014,15 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
{ Global utilities }
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
procedure InitBIFF8Limitations(out ALimitations: TsSpreadsheetFormatLimitations);
|
||||||
|
begin
|
||||||
|
InitBiffLimitations(ALimitations);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
|
|
||||||
// Registers this reader / writer in fpSpreadsheet
|
// Registers this reader / writer in fpSpreadsheet
|
||||||
|
@ -334,6 +334,8 @@ const
|
|||||||
ROWHEIGHT_EPS = 1E-2;
|
ROWHEIGHT_EPS = 1E-2;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
TsBIFFExternKind = (ebkExternal, ebkInternal, ebkAddInFunc, ebkDDE_OLE);
|
||||||
|
|
||||||
TDateMode = (dm1900, dm1904); //DATEMODE values, 5.28
|
TDateMode = (dm1900, dm1904); //DATEMODE values, 5.28
|
||||||
|
|
||||||
// Adjusts Excel float (date, date/time, time) with the file's base date to get a TDateTime
|
// Adjusts Excel float (date, date/time, time) with the file's base date to get a TDateTime
|
||||||
@ -379,50 +381,39 @@ type
|
|||||||
property ValidOnSheet: Integer read FValidOnSheet;
|
property ValidOnSheet: Integer read FValidOnSheet;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TsExternBook - Information on where out-of-sheet references are stored. }
|
{ TsBIFFExternBook - Information on where out-of-sheet references are stored. }
|
||||||
TsBIFFExternBookKind = (ebkExternal, ebkInternal, ebkAddInFunc, ebkDDE_OLE);
|
TsBIFFExternSheet = class
|
||||||
TsBIFFExternBook = class
|
|
||||||
Kind: TsBIFFExternBookKind;
|
|
||||||
// The following fields are used only for external workbooks.
|
|
||||||
DocumentURL: String;
|
|
||||||
SheetNames: String; // List of worksheetnames separated by #1
|
|
||||||
function GetWorksheetName(AIndex: Integer): String;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TsBIFFExternBookList }
|
|
||||||
TsBIFFExternBookList = class(TFPObjectlist)
|
|
||||||
private
|
private
|
||||||
function GetItem(AIndex: Integer): TsBIFFExternBook;
|
FKind: TsBIFFExternKind;
|
||||||
procedure SetItem(AIndex: Integer; AValue: TsBIFFExternBook);
|
FSheetName: String;
|
||||||
public
|
public
|
||||||
function AddBook(ABookName: String): Integer;
|
constructor Create(ASheetName: String; AKind: TsBIFFExternKind);
|
||||||
function AddInternal: Integer;
|
property Kind: TsBIFFExternKind read FKind;
|
||||||
function FindBook(ABookName: String): Integer;
|
property SheetName: String read FSheetName;
|
||||||
function FindInternalBook: Integer;
|
|
||||||
property Item[AIndex: Integer]: TsBIFFExternBook read GetItem write SetItem; default;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TsExternSheet - Information on a sheets used in out-of-sheet references }
|
TsBIFFExternSheetList = class(TFPObjectList)
|
||||||
TsBIFFExternSheet = packed record
|
|
||||||
ExternBookIndex: Word;
|
|
||||||
FirstSheetIndex: Word;
|
|
||||||
LastSheetIndex: Word;
|
|
||||||
end;
|
|
||||||
PsBIFFExternSheet = ^TsBIFFExternSheet;
|
|
||||||
|
|
||||||
{ A list for sheets used in out-of-sheet references }
|
|
||||||
TsBIFFExternSheetList = class(TFPList)
|
|
||||||
private
|
private
|
||||||
FBookList: TsBIFFExternBookList;
|
function GetItem(AIndex: Integer): TsBIFFExternSheet;
|
||||||
function GetItem(AIndex: Integer): PsBIFFExternSheet;
|
procedure SetItem(AIndex: Integer; AValue: TsBIFFExternSheet);
|
||||||
procedure SetItem(AIndex: Integer; AValue: PsBIFFExternSheet);
|
|
||||||
public
|
public
|
||||||
constructor Create(ABookList: TsBIFFExternBookList);
|
function AddSheet(ASheetName: String; AKind: TsBIFFExternKind): Integer;
|
||||||
|
function FindSheet(ASheetName: String): TsBIFFExternSheet;
|
||||||
|
function IndexOfSheet(ASheetName: String): Integer;
|
||||||
|
property Items[AIndex: Integer]: TsBIFFExternSheet read GetItem write SetItem; default;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TsBIFFLinkListItem = class
|
||||||
|
Worksheet: TsWorksheet;
|
||||||
|
Sheetlist: TsBIFFExternSheetList;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
function AddSheets(ABookName: String; ASheetIndex1, ASheetIndex2: Integer): Integer;
|
end;
|
||||||
procedure Clear;
|
|
||||||
function FindSheets(ABookName: String; ASheetIndex1, ASheetIndex2: Integer): Integer;
|
TsBIFFLinkLists = class(TFPObjectList)
|
||||||
property Item[AIndex: Integer]: PsBIFFExternSheet read GetItem write SetItem; default;
|
public
|
||||||
|
function GetSheetList(AWorksheet: TsWorksheet): TsBiffExternSheetList;
|
||||||
|
function GetGlobalLinks: TsBiffExternSheetList;
|
||||||
|
function GetLocalLinks(AWorksheet: TsWorksheet): TsBiffExternSheetList;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -437,11 +428,11 @@ type
|
|||||||
FIncompleteNoteLength: Word;
|
FIncompleteNoteLength: Word;
|
||||||
FFirstNumFormatIndexInFile: Integer;
|
FFirstNumFormatIndexInFile: Integer;
|
||||||
FPalette: TsPalette;
|
FPalette: TsPalette;
|
||||||
FDefinedNames: TFPList;
|
FDefinedNames: TFPObjectList;
|
||||||
FWorksheetData: TFPList;
|
FWorksheetData: TFPList;
|
||||||
FCurSheetIndex: Integer;
|
FCurSheetIndex: Integer;
|
||||||
FActivePane: Integer;
|
FActivePane: Integer;
|
||||||
FExternSheets: TStrings;
|
FLinkLists: TsBIFFLinkLists;
|
||||||
|
|
||||||
procedure AddBuiltinNumFormats; override;
|
procedure AddBuiltinNumFormats; override;
|
||||||
procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); virtual;
|
procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); virtual;
|
||||||
@ -483,9 +474,9 @@ type
|
|||||||
// Read the default row height
|
// Read the default row height
|
||||||
procedure ReadDefRowHeight(AStream: TStream);
|
procedure ReadDefRowHeight(AStream: TStream);
|
||||||
// Read an EXTERNCOUNT record
|
// Read an EXTERNCOUNT record
|
||||||
procedure ReadEXTERNCOUNT(AStream: TStream);
|
procedure ReadEXTERNCOUNT(AStream: TStream; AWorksheet: TsWorksheet);
|
||||||
// Read an EXTERNSHEET record (defined names)
|
// Read an EXTERNSHEET record (defined names)
|
||||||
procedure ReadEXTERNSHEET(AStream: TStream); virtual;
|
procedure ReadEXTERNSHEET(AStream: TStream; AWorksheet: TsWorksheet);
|
||||||
// Read FORMAT record (cell formatting)
|
// Read FORMAT record (cell formatting)
|
||||||
procedure ReadFormat(AStream: TStream); virtual;
|
procedure ReadFormat(AStream: TStream); virtual;
|
||||||
// Read FORMULA record
|
// Read FORMULA record
|
||||||
@ -575,11 +566,10 @@ type
|
|||||||
FCodePage: String; // in a format prepared for lconvencoding.ConvertEncoding
|
FCodePage: String; // in a format prepared for lconvencoding.ConvertEncoding
|
||||||
FFirstNumFormatIndexInFile: Integer;
|
FFirstNumFormatIndexInFile: Integer;
|
||||||
FPalette: TsPalette;
|
FPalette: TsPalette;
|
||||||
FExternBooks: TsBIFFExternBookList;
|
FLinkLists: TsBiffLinkLists;
|
||||||
FExternSheets: TsBIFFExternSheetList;
|
|
||||||
|
|
||||||
procedure AddBuiltinNumFormats; override;
|
procedure AddBuiltinNumFormats; override;
|
||||||
procedure CollectExternData(AWorksheet: TsWorksheet = nil);
|
function CollectExternData(AWorksheet: TsWorksheet = nil): Integer;
|
||||||
function FindXFIndex(AFormatIndex: Integer): Integer; virtual;
|
function FindXFIndex(AFormatIndex: Integer): Integer; virtual;
|
||||||
function FixLineEnding(const AText: String): String;
|
function FixLineEnding(const AText: String): String;
|
||||||
function FormulaSupported(ARPNFormula: TsRPNFormula; out AUnsupported: String): Boolean;
|
function FormulaSupported(ARPNFormula: TsRPNFormula; out AUnsupported: String): Boolean;
|
||||||
@ -619,7 +609,8 @@ type
|
|||||||
procedure WriteDefaultRowHeight(AStream: TStream; AWorksheet: TsWorksheet);
|
procedure WriteDefaultRowHeight(AStream: TStream; AWorksheet: TsWorksheet);
|
||||||
// Writes out DEFINEDNAMES records
|
// Writes out DEFINEDNAMES records
|
||||||
procedure WriteDefinedName(AStream: TStream; AWorksheet: TsWorksheet;
|
procedure WriteDefinedName(AStream: TStream; AWorksheet: TsWorksheet;
|
||||||
const AName: String; AIndexToREF: Word); virtual;
|
const AName: String; AIndexToREF, ASheetIndex: Word;
|
||||||
|
AKind: TsBIFFExternKind); virtual;
|
||||||
procedure WriteDefinedNames(AStream: TStream);
|
procedure WriteDefinedNames(AStream: TStream);
|
||||||
// Writes out ERROR cell record
|
// Writes out ERROR cell record
|
||||||
procedure WriteError(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteError(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
@ -959,194 +950,107 @@ end;
|
|||||||
|
|
||||||
|
|
||||||
{ -----------------------------------------------------------------------------}
|
{ -----------------------------------------------------------------------------}
|
||||||
{ TsBIFFExternBook }
|
{ TsBIFFExternSheet }
|
||||||
{ -----------------------------------------------------------------------------}
|
{ -----------------------------------------------------------------------------}
|
||||||
function TsBIFFExternBook.GetWorksheetName(AIndex: Integer): String;
|
constructor TsBIFFExternSheet.Create(ASheetName: string;
|
||||||
var
|
AKind: TsBiffExternKind);
|
||||||
L: TStrings;
|
|
||||||
begin
|
|
||||||
Result := '';
|
|
||||||
if Kind = ebkExternal then begin
|
|
||||||
L := TStringList.Create;
|
|
||||||
try
|
|
||||||
L.Delimiter := #1;
|
|
||||||
L.DelimitedText := SheetNames;
|
|
||||||
Result := L[AIndex];
|
|
||||||
finally
|
|
||||||
L.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
{------------------------------------------------------------------------------}
|
|
||||||
{ TsBIFFExternBookList }
|
|
||||||
{------------------------------------------------------------------------------}
|
|
||||||
function TsBIFFExternBookList.AddBook(ABookName: String): Integer;
|
|
||||||
var
|
|
||||||
book: TsBIFFExternBook;
|
|
||||||
begin
|
|
||||||
if ABookName = '' then
|
|
||||||
Result := AddInternal
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
Result := FindBook(ABookName);
|
|
||||||
if Result = -1 then begin
|
|
||||||
book := TsBIFFExternBook.Create;
|
|
||||||
book.DocumentURL := ABookName;
|
|
||||||
book.Kind := ebkExternal;
|
|
||||||
Result := Add(book);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TsBIFFExternBookList.AddInternal: Integer;
|
|
||||||
var
|
|
||||||
book: TsBIFFExternBook;
|
|
||||||
begin
|
|
||||||
Result := FindInternalBook;
|
|
||||||
if Result = -1 then begin
|
|
||||||
book := TsBIFFExternBook.Create;
|
|
||||||
book.Kind := ebkInternal;
|
|
||||||
Result := Add(book);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TsBIFFExternBookList.FindBook(ABookName: String): Integer;
|
|
||||||
var
|
|
||||||
book: TsBIFFExternBook;
|
|
||||||
begin
|
|
||||||
if ABookName = '' then
|
|
||||||
Result := FindInternalBook
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
for Result:=0 to Count-1 do
|
|
||||||
begin
|
|
||||||
book := Item[Result];
|
|
||||||
if (book.Kind = ebkExternal) and (book.DocumentURL = ABookName) then
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
Result := -1;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TsBIFFExternBookList.FindInternalBook: Integer;
|
|
||||||
begin
|
|
||||||
for Result := 0 to Count-1 do
|
|
||||||
if Item[Result].Kind = ebkInternal then exit;
|
|
||||||
Result := -1;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TsBIFFExternBookList.GetItem(AIndex: Integer): TsBIFFExternBook;
|
|
||||||
begin
|
|
||||||
Result := TsBIFFExternBook(inherited Items[AIndex]);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TsBIFFExternBookList.SetItem(AIndex: Integer;
|
|
||||||
AValue: TsBIFFExternBook);
|
|
||||||
begin
|
|
||||||
inherited Items[AIndex] := AValue;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
{------------------------------------------------------------------------------}
|
|
||||||
{ TsBiffExternSheetList }
|
|
||||||
{------------------------------------------------------------------------------}
|
|
||||||
constructor TsBIFFExternSheetList.Create(ABookList: TsBIFFExternBookList);
|
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FBookList := ABookList;
|
FSheetName := ASheetName;
|
||||||
|
FKind := AKind;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TsBIFFExternSheetList.Destroy;
|
|
||||||
begin
|
|
||||||
Clear;
|
|
||||||
inherited;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TsBIFFExternSheetList.AddSheets(ABookName: String;
|
{ -----------------------------------------------------------------------------}
|
||||||
ASheetIndex1, ASheetIndex2: Integer): Integer;
|
{ TsBIFFExternSheetList }
|
||||||
|
{ -----------------------------------------------------------------------------}
|
||||||
|
function TsBIFFExternSheetList.AddSheet(ASheetName: String;
|
||||||
|
AKind: TsBIFFExternKind): Integer;
|
||||||
|
var
|
||||||
|
sheet: TsBIFFExternSheet;
|
||||||
|
begin
|
||||||
|
Result := IndexOfSheet(ASheetName);
|
||||||
|
if Result = -1 then begin
|
||||||
|
sheet := TsBIFFExternSheet.Create(ASheetName, AKind);
|
||||||
|
Result := inherited Add(sheet);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsBIFFExternSheetList.FindSheet(ASheetName: string): TsBIFFExternSheet;
|
||||||
var
|
var
|
||||||
P: PsBIFFExternSheet;
|
|
||||||
idx: Integer;
|
idx: Integer;
|
||||||
begin
|
begin
|
||||||
Result := FindSheets(ABookName, ASheetIndex1, ASheetIndex2);
|
idx := IndexOfSheet(ASheetName);
|
||||||
if Result = -1 then
|
if idx <> -1 then
|
||||||
begin
|
Result := Items[idx]
|
||||||
New(P);
|
else
|
||||||
idx := FBookList.FindBook(ABookName);
|
Result := nil;
|
||||||
if idx = -1 then
|
|
||||||
idx := FBookList.AddBook(ABookName);
|
|
||||||
P^.ExternBookIndex := idx;
|
|
||||||
|
|
||||||
if ASheetIndex2 = -1 then
|
|
||||||
ASheetIndex2 := ASheetIndex1;
|
|
||||||
if ASheetIndex2 < ASheetIndex1 then
|
|
||||||
begin
|
|
||||||
P^.FirstSheetIndex := ASheetIndex2;
|
|
||||||
P^.LastSheetIndex := ASheetIndex1;
|
|
||||||
end else
|
|
||||||
begin
|
|
||||||
P^.FirstSheetIndex := ASheetIndex1;
|
|
||||||
P^.LastSheetIndex := ASheetIndex2;
|
|
||||||
end;
|
|
||||||
Result := Add(P);
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsBIFFExternSheetList.Clear;
|
function TsBIFFExternSheetList.GetItem(AIndex: Integer): TsBIFFExternSheet;
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
P: PsBIFFExternSheet;
|
|
||||||
begin
|
begin
|
||||||
for i:=0 to Count-1 do begin
|
Result := TsBIFFExternSheet(inherited Items[AIndex]);
|
||||||
P := Item[i];
|
|
||||||
Dispose(P);
|
|
||||||
end;
|
|
||||||
inherited;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TsBIFFExternSheetList.FindSheets(ABookName: String;
|
function TsBIFFExternSheetList.IndexOfSheet(ASheetName: string): Integer;
|
||||||
ASheetIndex1, ASheetIndex2: Integer): Integer;
|
|
||||||
var
|
var
|
||||||
book: TsBIFFExternBook;
|
sheet: TsBIFFExternSheet;
|
||||||
P: PsBIFFExternSheet;
|
|
||||||
tmp: Integer;
|
|
||||||
idx: Integer;
|
|
||||||
begin
|
begin
|
||||||
if ASheetIndex2 = -1 then ASheetIndex2 := ASheetIndex1;
|
for Result := 0 to Count-1 do begin
|
||||||
if ASheetIndex2 < ASheetIndex1 then begin
|
sheet := GetItem(Result);
|
||||||
tmp := ASheetIndex1;
|
if sheet.SheetName = ASheetName then
|
||||||
ASheetIndex1 := ASheetIndex2;
|
|
||||||
ASheetIndex2 := tmp;
|
|
||||||
end;
|
|
||||||
|
|
||||||
idx := FBookList.FindBook(ABookName);
|
|
||||||
if idx = -1 then
|
|
||||||
exit(-1);
|
|
||||||
|
|
||||||
for Result:=0 to Count-1 do begin
|
|
||||||
P := Item[Result];
|
|
||||||
if (P^.ExternBookIndex = idx) and
|
|
||||||
(P^.FirstSheetIndex = ASheetIndex1) and
|
|
||||||
(P^.LastSheetIndex = ASheetIndex2)
|
|
||||||
then
|
|
||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
Result := -1;
|
Result := -1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TsBIFFExternSheetList.GetItem(AIndex: Integer): PsBIFFExternSheet;
|
procedure TsBIFFExternSheetList.SetItem(AIndex: Integer;
|
||||||
begin
|
AValue: TsBIFFExternSheet);
|
||||||
Result := PsBIFFExternSheet(inherited Items[AIndex]);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TsBIFFExternSheetList.SetItem(AIndex: Integer; AValue: PsBIFFExternSheet);
|
|
||||||
begin
|
begin
|
||||||
inherited Items[AIndex] := AValue;
|
inherited Items[AIndex] := AValue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
{ TsBIFFLinkListItem }
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
destructor TsBIFFLinkListItem.Destroy;
|
||||||
|
begin
|
||||||
|
SheetList.Free;
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
{ TsBIFFLinkLists }
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
function TsBIFFLinkLists.GetSheetList(
|
||||||
|
AWorksheet: TsWorksheet): TsBiffExternSheetList;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
item: TsBIFFLinkListItem;
|
||||||
|
begin
|
||||||
|
for i := 0 to Count-1 do begin
|
||||||
|
item := TsBIFFLinkListItem(Items[i]);
|
||||||
|
if item.Worksheet = AWorksheet then begin
|
||||||
|
Result := item.SheetList;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Result := nil;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsBIFFLinkLists.GetGlobalLinks: TsBIFFExternSheetList;
|
||||||
|
begin
|
||||||
|
Result := GetSheetList(nil);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsBIFFLinkLists.GetLocalLinks(
|
||||||
|
AWorksheet: TsWorksheet): TsBIFFExternSheetList;
|
||||||
|
begin
|
||||||
|
Result := GetSheetList(AWorksheet);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{------------------------------------------------------------------------------}
|
{------------------------------------------------------------------------------}
|
||||||
{ TsBIFFDefinedName }
|
{ TsBIFFDefinedName }
|
||||||
@ -1231,7 +1135,8 @@ begin
|
|||||||
FCellFormatList := TsCellFormatList.Create(true);
|
FCellFormatList := TsCellFormatList.Create(true);
|
||||||
// true = allow duplicates! XF indexes get out of sync if not all format records are in list
|
// true = allow duplicates! XF indexes get out of sync if not all format records are in list
|
||||||
|
|
||||||
FDefinedNames := TFPList.Create;
|
FLinkLists := TsBIFFLinkLists.Create;
|
||||||
|
FDefinedNames := TFPObjectList.Create;
|
||||||
|
|
||||||
// Initial base date in case it won't be read from file
|
// Initial base date in case it won't be read from file
|
||||||
FDateMode := dm1900;
|
FDateMode := dm1900;
|
||||||
@ -1247,13 +1152,9 @@ end;
|
|||||||
Destructor of the reader class
|
Destructor of the reader class
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
destructor TsSpreadBIFFReader.Destroy;
|
destructor TsSpreadBIFFReader.Destroy;
|
||||||
var
|
|
||||||
j: Integer;
|
|
||||||
begin
|
begin
|
||||||
for j:=0 to FDefinedNames.Count-1 do TObject(FDefinedNames[j]).Free;
|
|
||||||
FDefinedNames.Free;
|
FDefinedNames.Free;
|
||||||
|
FLinkLists.Free;
|
||||||
FExternSheets.Free;
|
|
||||||
FPalette.Free;
|
FPalette.Free;
|
||||||
|
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
@ -1857,12 +1758,19 @@ begin
|
|||||||
FWorksheet.WriteDefaultRowHeight(TwipsToPts(hw), suPoints);
|
FWorksheet.WriteDefaultRowHeight(TwipsToPts(hw), suPoints);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsSpreadBIFFReader.ReadEXTERNCOUNT(AStream: TStream);
|
procedure TsSpreadBIFFReader.ReadEXTERNCOUNT(AStream: TStream;
|
||||||
|
AWorksheet: TsWorksheet);
|
||||||
|
var
|
||||||
|
item: TsBIFFLinkListItem;
|
||||||
begin
|
begin
|
||||||
AStream.ReadWord;
|
AStream.ReadWord;
|
||||||
{ We ignore the value of the record, but use the presence of the record
|
{ We ignore the value of the record, but use the presence of the record
|
||||||
to create the FExternSheets list. }
|
to create the link list item. The data themselves follow in the next
|
||||||
FExternSheets := TStringList.Create;
|
record. }
|
||||||
|
item := TsBIFFLinkListItem.Create;
|
||||||
|
item.Worksheet := AWorksheet;
|
||||||
|
item.Sheetlist := TsBIFFExternSheetList.Create;
|
||||||
|
FLinkLists.Add(item);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
@ -1875,12 +1783,18 @@ end;
|
|||||||
NOTE: The string length field is decreased by 1, if the EXTERNSHEET stores
|
NOTE: The string length field is decreased by 1, if the EXTERNSHEET stores
|
||||||
a reference to one of the own sheets (first character is #03).
|
a reference to one of the own sheets (first character is #03).
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsSpreadBIFFReader.ReadExternSheet(AStream: TStream);
|
procedure TsSpreadBIFFReader.ReadEXTERNSHEET(AStream: TStream;
|
||||||
|
AWorksheet: TsWorksheet);
|
||||||
var
|
var
|
||||||
len, b: Byte;
|
len, b: Byte;
|
||||||
ansistr: AnsiString;
|
ansistr: AnsiString;
|
||||||
s: String;
|
s: String;
|
||||||
|
sheetlist: TsBIFFExternSheetList;
|
||||||
|
sheet: TsBIFFExternSheet;
|
||||||
|
idx: Integer;
|
||||||
begin
|
begin
|
||||||
|
sheetList := FLinkLists.GetSheetList(AWorksheet);
|
||||||
|
|
||||||
len := AStream.ReadByte;
|
len := AStream.ReadByte;
|
||||||
b := AStream.ReadByte;
|
b := AStream.ReadByte;
|
||||||
if b = 3 then
|
if b = 3 then
|
||||||
@ -1888,9 +1802,16 @@ begin
|
|||||||
SetLength(ansistr, len);
|
SetLength(ansistr, len);
|
||||||
AStream.ReadBuffer(ansistr[2], len-1);
|
AStream.ReadBuffer(ansistr[2], len-1);
|
||||||
Delete(ansistr, 1, 1);
|
Delete(ansistr, 1, 1);
|
||||||
// ansistr[1] := char(b);
|
|
||||||
s := ConvertEncoding(ansistr, FCodePage, encodingUTF8);
|
s := ConvertEncoding(ansistr, FCodePage, encodingUTF8);
|
||||||
FExternSheets.AddObject(s, TObject(PtrInt(b)));
|
|
||||||
|
if b = 3 then
|
||||||
|
{ Internal (within-workbook) references }
|
||||||
|
sheetList.AddSheet(s, ebkInternal)
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
{ External references }
|
||||||
|
// to do: implement external references
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
@ -3435,12 +3356,14 @@ begin
|
|||||||
// Color palette
|
// Color palette
|
||||||
FPalette := TsPalette.Create;
|
FPalette := TsPalette.Create;
|
||||||
PopulatePalette(AWorkbook);
|
PopulatePalette(AWorkbook);
|
||||||
|
|
||||||
|
{ List for external links }
|
||||||
|
FLinkLists := TsBIFFLinkLists.Create;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TsSpreadBIFFWriter.Destroy;
|
destructor TsSpreadBIFFWriter.Destroy;
|
||||||
begin
|
begin
|
||||||
FExternSheets.Free;
|
FLinkLists.Free;
|
||||||
FExternBooks.Free;
|
|
||||||
FPalette.Free;
|
FPalette.Free;
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
@ -3475,18 +3398,22 @@ end;
|
|||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Collects the data for out-of-sheet links found in the specified worksheet
|
Collects the data for out-of-sheet links found in the specified worksheet
|
||||||
(or all worksheets if the parameter is omitted).
|
(or all worksheets if the parameter is omitted).
|
||||||
The found data are written to the FExternBooks and FExternSheets lists.
|
The found data are written to the a TBIFFLinkListItem which is added to
|
||||||
|
FLinkLists. The function returns the index of the new TBIFFLinkListItem.
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsSpreadBIFFWriter.CollectExternData(AWorksheet: TsWorksheet = nil);
|
function TsSpreadBIFFWriter.CollectExternData(AWorksheet: TsWorksheet = nil): Integer;
|
||||||
|
|
||||||
procedure DoCollectForSheet(ASheet: TsWorksheet);
|
procedure DoCollectForSheet(ASheet: TsWorksheet; ASheetList: TsBIFFExternSheetList);
|
||||||
var
|
var
|
||||||
cell: PCell;
|
cell: PCell;
|
||||||
|
workbook: TsWorkbook;
|
||||||
parser: TsExpressionParser;
|
parser: TsExpressionParser;
|
||||||
rpn: TsRPNFormula;
|
rpn: TsRPNFormula;
|
||||||
fe: TsFormulaElement;
|
fe: TsFormulaElement;
|
||||||
j: Integer;
|
i, j: Integer;
|
||||||
|
kind: TsBIFFExternKind;
|
||||||
begin
|
begin
|
||||||
|
workbook := ASheet.Workbook;
|
||||||
for cell in ASheet.Cells do
|
for cell in ASheet.Cells do
|
||||||
begin
|
begin
|
||||||
if not HasFormula(cell) then
|
if not HasFormula(cell) then
|
||||||
@ -3494,15 +3421,28 @@ procedure TsSpreadBIFFWriter.CollectExternData(AWorksheet: TsWorksheet = nil);
|
|||||||
if not (cf3dFormula in cell^.Flags) then
|
if not (cf3dFormula in cell^.Flags) then
|
||||||
Continue;
|
Continue;
|
||||||
|
|
||||||
|
if (pos('[', ASheet.Name) = 0) then
|
||||||
|
kind := ebkInternal
|
||||||
|
else
|
||||||
|
kind := ebkExternal;
|
||||||
|
|
||||||
parser := TsSpreadsheetParser.Create(ASheet);
|
parser := TsSpreadsheetParser.Create(ASheet);
|
||||||
try
|
try
|
||||||
parser.Expression := cell^.FormulaValue;
|
parser.Expression := cell^.FormulaValue;
|
||||||
rpn := parser.RPNFormula;
|
rpn := parser.RPNFormula;
|
||||||
for j:=0 to High(rpn) do
|
for i:=0 to High(rpn) do
|
||||||
begin
|
begin
|
||||||
fe := rpn[j];
|
fe := rpn[i];
|
||||||
if fe.ElementKind in [fekCell3d, fekCellRef3d, fekCellRange3d] then
|
if fe.ElementKind in [fekCell3d, fekCellRef3d, fekCellRange3d] then begin
|
||||||
FExternSheets.AddSheets('', fe.Sheet, fe.Sheet2); // '' --> supporting only internal 3d links so far
|
if fe.Sheet = -1 then
|
||||||
|
ASheetList.AddSheet(ASheet.Name, kind)
|
||||||
|
else
|
||||||
|
if fe.Sheet2 = -1 then
|
||||||
|
ASheetList.AddSheet(workbook.GetWorksheetByIndex(fe.Sheet).Name, kind)
|
||||||
|
else
|
||||||
|
for j :=fe.Sheet to fe.Sheet2 do
|
||||||
|
ASheetList.AddSheet(workbook.GetWorksheetbyIndex(j).Name, kind);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
finally
|
finally
|
||||||
parser.Free;
|
parser.Free;
|
||||||
@ -3514,24 +3454,37 @@ procedure TsSpreadBIFFWriter.CollectExternData(AWorksheet: TsWorksheet = nil);
|
|||||||
var
|
var
|
||||||
sheet: TsWorksheet;
|
sheet: TsWorksheet;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
|
writeIt: boolean;
|
||||||
|
linkList: TsBIFFLinkListItem;
|
||||||
begin
|
begin
|
||||||
FExternBooks.Free;
|
linkList := TsBIFFLinkListItem.Create;
|
||||||
FExternBooks := TsBIFFExternBookList.Create;
|
linkList.SheetList := TsBIFFExternSheetList.Create;
|
||||||
FExternSheets.Free;
|
|
||||||
FExternSheets := TsBIFFExternSheetList.Create(FExternBooks);
|
|
||||||
|
|
||||||
if AWorksheet <> nil then
|
if AWorksheet <> nil then
|
||||||
DoCollectForSheet(AWorksheet)
|
begin
|
||||||
else
|
{ This part is active when called from WriteLocalLinkList }
|
||||||
|
linklist.Worksheet := AWorksheet;
|
||||||
|
DoCollectForSheet(AWorksheet, linklist.SheetList)
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
{ This part is active when called from WriteGlobalLinkList }
|
||||||
|
{ Find sheets used in print ranges, repeated cols or repeated rows }
|
||||||
|
linkList.Worksheet := nil; // signals global linklist
|
||||||
for i:=0 to FWorkbook.GetWorksheetCount-1 do
|
for i:=0 to FWorkbook.GetWorksheetCount-1 do
|
||||||
begin
|
begin
|
||||||
sheet := FWorkbook.GetWorksheetbyIndex(i);
|
sheet := FWorkbook.GetWorksheetbyIndex(i);
|
||||||
DoCollectForSheet(sheet);
|
with sheet.PageLayout do
|
||||||
|
writeIt := (NumPrintRanges > 0) or HasRepeatedCols or HasRepeatedRows;
|
||||||
|
if writeIt then
|
||||||
|
linkList.SheetList.AddSheet(sheet.Name, ebkInternal);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if FExternSheets.Count = 0 then begin
|
if linkList.SheetList.Count <> 0 then
|
||||||
FreeAndNil(FExternSheets);
|
Result := FLinkLists.Add(linklist)
|
||||||
FreeAndNil(FExternBooks);
|
else begin
|
||||||
|
linkList.Free; // destroys booklist, too.
|
||||||
|
Result := -1;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -3998,10 +3951,12 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsSpreadBIFFWriter.WriteDefinedName(AStream: TStream;
|
procedure TsSpreadBIFFWriter.WriteDefinedName(AStream: TStream;
|
||||||
AWorksheet: TsWorksheet; const AName: String; AIndexToREF: Word);
|
AWorksheet: TsWorksheet; const AName: String; AIndexToREF, ASheetIndex: Word;
|
||||||
|
AKind: TsBIFFExternKind);
|
||||||
begin
|
begin
|
||||||
Unused(AStream, AWorksheet);
|
Unused(AStream, AWorksheet);
|
||||||
Unused(Aname, AIndexToREF);
|
Unused(Aname, AIndexToREF);
|
||||||
|
Unused(AKind);
|
||||||
// Override
|
// Override
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -4009,20 +3964,28 @@ procedure TsSpreadBIFFWriter.WriteDefinedNames(AStream: TStream);
|
|||||||
var
|
var
|
||||||
sheet: TsWorksheet;
|
sheet: TsWorksheet;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
n: Word;
|
idx: Word;
|
||||||
|
extSheetIdx: Integer;
|
||||||
|
sheetList: TsBIFFExternSheetList;
|
||||||
begin
|
begin
|
||||||
n := 0;
|
{ Find sheetlist of global link table }
|
||||||
|
sheetList := FLinkLists.GetGlobalLinks;
|
||||||
|
if sheetList = nil then
|
||||||
|
exit;
|
||||||
|
|
||||||
for i:=0 to FWorkbook.GetWorksheetCount-1 do
|
for i:=0 to FWorkbook.GetWorksheetCount-1 do
|
||||||
begin
|
begin
|
||||||
sheet := FWorkbook.GetWorksheetByIndex(i);
|
sheet := FWorkbook.GetWorksheetByIndex(i);
|
||||||
|
extSheetIdx := sheetList.IndexOfSheet(sheet.Name);
|
||||||
if (sheet.PageLayout.NumPrintRanges > 0) or
|
if (sheet.PageLayout.NumPrintRanges > 0) or
|
||||||
sheet.PageLayout.HasRepeatedCols or sheet.PageLayout.HasRepeatedRows then
|
sheet.PageLayout.HasRepeatedCols or sheet.PageLayout.HasRepeatedRows then
|
||||||
begin
|
begin
|
||||||
|
// idx := sheetList.IndexOfSheet(sheet.Name);
|
||||||
|
// Write 1-based index. And negate it to indicate an internal reference.
|
||||||
if sheet.PageLayout.NumPrintRanges > 0 then
|
if sheet.PageLayout.NumPrintRanges > 0 then
|
||||||
WriteDefinedName(AStream, sheet, #6, n);
|
WriteDefinedName(AStream, sheet, #6, extSheetIdx, i, ebkInternal);
|
||||||
if sheet.PageLayout.HasRepeatedCols or sheet.PageLayout.HasRepeatedRows then
|
if sheet.PageLayout.HasRepeatedCols or sheet.PageLayout.HasRepeatedRows then
|
||||||
WriteDefinedName(AStream, sheet, #7, n);
|
WriteDefinedName(AStream, sheet, #7, extSheetIdx, i, ebkInternal);
|
||||||
inc(n);
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -5040,6 +5003,8 @@ begin
|
|||||||
n := WriteRPNSheetIndex(AStream, '', AFormula[i].Sheet, AFormula[i].Sheet2);
|
n := WriteRPNSheetIndex(AStream, '', AFormula[i].Sheet, AFormula[i].Sheet2);
|
||||||
if n = $FFFF then
|
if n = $FFFF then
|
||||||
FWorkbook.AddErrorMsg('3D cell addresses are not supported.')
|
FWorkbook.AddErrorMsg('3D cell addresses are not supported.')
|
||||||
|
else if n = $FFFE then
|
||||||
|
raise Exception.Create('[TsSpreadBIFFWriter.WriteRPNTokenArray] Worksheet(s) not found.')
|
||||||
else begin
|
else begin
|
||||||
inc(n, WriteRPNCellAddress(AStream, AFormula[i].Row, AFormula[i].Col, AFormula[i].RelFlags));
|
inc(n, WriteRPNCellAddress(AStream, AFormula[i].Row, AFormula[i].Col, AFormula[i].RelFlags));
|
||||||
inc(RPNLength, n);
|
inc(RPNLength, n);
|
||||||
@ -5062,8 +5027,8 @@ begin
|
|||||||
n := WriteRPNSheetIndex(AStream, '', AFormula[i].Sheet, AFormula[i].Sheet2);
|
n := WriteRPNSheetIndex(AStream, '', AFormula[i].Sheet, AFormula[i].Sheet2);
|
||||||
if n = $FFFF then
|
if n = $FFFF then
|
||||||
FWorkbook.AddErrorMsg('3D cell address ranges are not supported.')
|
FWorkbook.AddErrorMsg('3D cell address ranges are not supported.')
|
||||||
else if n = $FFF3 then
|
else if n = $FFFE then
|
||||||
FWorkbook.AddErrorMsg('Sheets not found in LinkTable.')
|
raise Exception.Create('[TsSpreadBIFFWriter.WriteRPNTokenArray] Worksheet(s) not found.')
|
||||||
else begin
|
else begin
|
||||||
inc(n, WriteRPNCellRangeAddress(AStream,
|
inc(n, WriteRPNCellRangeAddress(AStream,
|
||||||
AFormula[i].Row, AFormula[i].Col,
|
AFormula[i].Row, AFormula[i].Col,
|
||||||
|
@ -7,8 +7,8 @@ interface
|
|||||||
uses
|
uses
|
||||||
// Not using Lazarus package as the user may be working with multiple versions
|
// Not using Lazarus package as the user may be working with multiple versions
|
||||||
// Instead, add .. to unit search path
|
// Instead, add .. to unit search path
|
||||||
Classes, SysUtils, fpcunit, testutils, testregistry,
|
Classes, SysUtils, fpcunit, testregistry,
|
||||||
fpstypes, fpsallformats, fpspreadsheet, fpsexprparser,
|
fpstypes, fpspreadsheet, fpsexprparser,
|
||||||
xlsbiff8 {and a project requirement for lclbase for utf8 handling},
|
xlsbiff8 {and a project requirement for lclbase for utf8 handling},
|
||||||
testsutility;
|
testsutility;
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ uses
|
|||||||
{$IFDEF FORMULADEBUG}
|
{$IFDEF FORMULADEBUG}
|
||||||
LazLogger,
|
LazLogger,
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
math, typinfo, lazUTF8, fpsUtils;
|
typinfo, lazUTF8, fpsUtils;
|
||||||
|
|
||||||
|
|
||||||
{ TSpreadExtendedFormulaTests }
|
{ TSpreadExtendedFormulaTests }
|
||||||
@ -96,6 +96,7 @@ var
|
|||||||
begin
|
begin
|
||||||
TempFile := GetTempFileName;
|
TempFile := GetTempFileName;
|
||||||
|
|
||||||
|
try
|
||||||
// Create test workbook and write test formula and needed cells
|
// Create test workbook and write test formula and needed cells
|
||||||
workbook := TsWorkbook.Create;
|
workbook := TsWorkbook.Create;
|
||||||
try
|
try
|
||||||
@ -159,7 +160,10 @@ begin
|
|||||||
CheckEquals(AFormula, actualformula, 'Saved formula text mismatch.');
|
CheckEquals(AFormula, actualformula, 'Saved formula text mismatch.');
|
||||||
finally
|
finally
|
||||||
workbook.Free;
|
workbook.Free;
|
||||||
DeleteFile(TempFile);
|
end;
|
||||||
|
|
||||||
|
finally
|
||||||
|
if FileExists(TempFile) then DeleteFile(TempFile);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user