fpspreadsheet: Add some more page layout properties (page size, orientation etc) for all Excel and ods formats (ods implementation, however, not correct at the moment).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4103 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2015-04-30 21:55:55 +00:00
parent 4f520e0bf2
commit 82117326a4
10 changed files with 952 additions and 104 deletions

View File

@ -66,6 +66,7 @@ type
FRowStyleList: TFPList;
FRowList: TFPList;
FDateMode: TDateMode;
FPageLayout: TsPageLayout;
// Applies internally stored column widths to current worksheet
procedure ApplyColWidths;
// Applies a style to a cell
@ -91,6 +92,7 @@ type
protected
FPointSeparatorSettings: TFormatSettings;
procedure AddBuiltinNumFormats; override;
procedure ReadAutomaticStyles(AStylesNode: TDOMNode);
procedure ReadNumFormats(AStylesNode: TDOMNode);
procedure ReadSettings(AOfficeSettingsNode: TDOMNode);
procedure ReadStyles(AStylesNode: TDOMNode);
@ -618,6 +620,8 @@ begin
Workbook.UseDefaultPalette;
// Initial base date in case it won't be read from file
FDateMode := dm1899;
// Initialize internal PageLayout record
InitPageLayout(FPageLayout);
end;
destructor TsSpreadOpenDocReader.Destroy;
@ -850,6 +854,79 @@ begin
Result := -1;
end;
procedure TsSpreadOpenDocReader.ReadAutomaticStyles(AStylesNode: TDOMNode);
var
nodeName: String;
layoutNode: TDOMNode;
node: TDOMNode;
s: String;
begin
if not Assigned(AStylesNode) then
exit;
layoutNode := AStylesNode.FirstChild;
while layoutNode <> nil do
begin
nodeName := layoutNode.NodeName;
if nodeName = 'style:page-layout' then begin
s := GetAttrValue(layoutNode, 'style:name');
if s = 'Mpm1' then
begin
node := layoutNode.FirstChild;
while node <> nil do
begin
nodeName := node.NodeName;
if nodeName = 'style:page-layout-properties' then
begin
s := GetAttrValue(node, 'fo:margin-top');
if s <> '' then
FPageLayout.TopMargin := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'fo:margin-bottom');
if s <> '' then
FPageLayout.BottomMargin := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'fo:margin-left');
if s <> '' then
FPageLayout.LeftMargin := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'fo:margin-right');
if s <> '' then
FPageLayout.RightMargin := PtsToMM(HTMLLengthStrToPts(s));
s := GetAttrValue(node, 'style:scale-to');
if (s <> '') then
begin
if s[Length(s)] = '%' then Delete(s, Length(s), 1);
FPageLayout.ScalingFactor := StrToFloat(s, FPointSeparatorSettings);
end;
s := GetAttrValue(node, 'style:print');
if pos('grid', s) > 0 then
Include(FPageLayout.Options, poPrintGridLines);
if pos('headers', s) > 0 then
Include(FPageLayout.Options, poPrintHeaders);
if pos('annotations', s) > 0 then
Include(FPageLayout.Options, poPrintCellComments);
s := GetAttrValue(node, 'style:print-page-order');
if s = 'ltr' then // "left-to-right", the other option is "ttb = top-to-bottom"
Include(FPageLayout.Options, poPrintPagesByRows);
s := GetAttrValue(node, 'style:first-page-number');
if s = 'continue' then
Exclude(FPageLayout.Options, poUseStartPageNumber)
else
if TryStrToInt(s, FPageLayout.StartPageNumber) then
Include(FPageLayout.Options, poUseStartPageNumber);
end;
node := node.NextSibling;
end;
end;
end;
layoutNode := layoutNode.NextSibling;
end;
end;
procedure TsSpreadOpenDocReader.ReadBlank(ARow, ACol: Cardinal;
ACellNode: TDOMNode);
var
@ -1317,6 +1394,9 @@ begin
ReadNumFormats(StylesNode);
ReadStyles(StylesNode);
StylesNode := Doc.DocumentElement.FindNode('office:automatic-styles');
ReadAutomaticStyles(StylesNode);
Doc.Free;
//process the content.xml file
@ -1349,6 +1429,7 @@ begin
continue;
end;
FWorkSheet := FWorkbook.AddWorksheet(GetAttrValue(TableNode,'table:name'), true);
FWorksheet.PageLayout := FPageLayout;
// Collect column styles used
ReadColumns(TableNode);
// Process each row inside the sheet and process each cell of the row

View File

@ -105,7 +105,6 @@ type
FDefaultColWidth: Single; // in "characters". Excel uses the width of char "0" in 1st font
FDefaultRowHeight: Single; // in "character heights", i.e. line count
FSortParams: TsSortParams; // Parameters of the current sorting operation
FPageLayout: TsPageLayout;
FOnChangeCell: TsCellEvent;
FOnChangeFont: TsCellEvent;
FOnCompareCells: TsCellCompareEvent;
@ -138,6 +137,9 @@ type
procedure ExchangeCells(ARow1, ACol1, ARow2, ACol2: Cardinal);
public
{@@ Page layout parameters for printing }
PageLayout: TsPageLayout;
{ Base methods }
constructor Create;
destructor Destroy; override;
@ -468,8 +470,6 @@ type
{@@ The default row height is given in "line count" (height of the
default font }
property DefaultRowHeight: Single read FDefaultRowHeight write FDefaultRowHeight;
{@@ Page layout parameters for printing }
property PageLayout: TsPageLayout read FPageLayout write FPageLayout;
// These are properties to interface to TsWorksheetGrid
{@@ Parameters controlling visibility of grid lines and row/column headers,
@ -1114,14 +1114,7 @@ begin
FMergedCells := TsMergedCells.Create;
FHyperlinks := TsHyperlinks.Create;
with FPageLayout do begin
LeftMargin := InToPts(0.7);
RightMargin := InToPts(0.7);
TopMargin := InToPts(0.78740157499999996);
BottomMargin := InToPts(0.78740157499999996);
HeaderDistance := InToPts(0.3);
FooterDistance := InToPts(0.3);
end;
InitPageLayout(PageLayout);
FDefaultColWidth := 12;
FDefaultRowHeight := 1;
@ -6767,6 +6760,8 @@ begin
// Remember the workbook to which it belongs (This must occur before
// setting the workbook name because the workbook is needed there).
Result.FWorkbook := Self;
Result.FActiveCellRow := 0;
Result.FActiveCellCol := 0;
// Set the name of the new worksheet.
// For this we turn off notification of listeners. This is not necessary here

View File

@ -2881,6 +2881,9 @@ end;
-------------------------------------------------------------------------------}
procedure TsSpreadsheetInspector.UpdateWorksheet(ASheet: TsWorksheet;
AStrings: TStrings);
var
s: String;
po: TsPrintOption;
begin
if ASheet = nil then
begin
@ -2891,6 +2894,7 @@ begin
AStrings.Add('Last column=');
AStrings.Add('Active cell=');
AStrings.Add('Selection=');
AStrings.Add('Page layout=');
end else
begin
AStrings.Add(Format('Name=%s', [ASheet.Name]));
@ -2903,6 +2907,27 @@ begin
AStrings.Add(Format('Comments=%d items', [ASheet.Comments.Count]));
AStrings.Add(Format('Hyperlinks=%d items', [ASheet.Hyperlinks.Count]));
AStrings.Add(Format('MergedCells=%d items', [ASheet.MergedCells.Count]));
AStrings.Add('Page layout=');
AStrings.Add(Format(' Orientation=%s', [GetEnumName(TypeInfo(TsPageOrientation), ord(ASheet.PageLayout.Orientation))]));
AStrings.Add(Format(' Page width=%.1f mm', [ASheet.PageLayout.PageWidth]));
AStrings.Add(Format(' Page height=%.1f mm', [ASheet.PageLayout.PageHeight]));
AStrings.Add(Format(' Left margin=%.1f mm', [ASheet.PageLayout.LeftMargin]));
AStrings.Add(Format(' Right margin=%.1f mm', [ASheet.PageLayout.RightMargin]));
AStrings.Add(Format(' Top margin=%.1f mm', [ASheet.PageLayout.TopMargin]));
AStrings.Add(Format(' Bottom margin=%.1f mm', [ASheet.PageLayout.BottomMargin]));
AStrings.Add(Format(' Header distance=%.1f mm', [ASheet.PageLayout.HeaderMargin]));
AStrings.Add(Format(' Footer distance=%.1f mm', [ASheet.PageLayout.FooterMargin]));
if poUseStartPageNumber in ASheet.PageLayout.Options then
AStrings.Add(Format(' Start page number=%d', [ASheet.pageLayout.StartPageNumber]))
else
AStrings.Add (' Start page number=automatic');
AStrings.Add(Format(' Scaling factor=%.0f%%', [ASheet.PageLayout.ScalingFactor]));
AStrings.Add(Format(' Copies=%d', [ASheet.PageLayout.Copies]));
s := '';
for po in TsPrintOption do
if po in ASheet.PageLayout.Options then s := s + '; ' + GetEnumName(typeInfo(TsPrintOption), ord(po));
if s <> '' then Delete(s, 1, 2);
AStrings.Add(Format(' Options=%s', [s]));
end;
end;

View File

@ -668,13 +668,39 @@ type
cctError : (ErrorValue: TsErrorValue);
end;
TsPageLayout = record
LeftMargin: Double; // in Points
{
TsPaperSize = (psUndefined, psLetter, psLetterSmall, psTabloid, psLedger,
psLegal, psStatement, psExecutive, psA3, psA4, psA4small, psA5, psB4, psB5,
psFolie, psQuarto, ps10x14, ps11x17, psNote, psEnvelope9, psEnvelope10,
psEnvelope11, psEnvelope12, psEnvelope14, psC, psD, psE, psEnvelopeDL,
psEnvelopeC5, psEnvelopeC3, psEnvelopeC4, psEnvelopeC6, psEnvelopeC6C5,
psB4ISO, psB5ISO, psB6ISO,
}
TsPageOrientation = (spoPortrait, spoLandscape);
TsPrintOption = (poPrintGridLines, poPrintHeaders, poPrintPagesByRows,
poMonochrome, poDraftQuality, poPrintCellComments, poDefaultOrientation,
poUseStartPageNumber, poCommentsAtEnd, poHorCentered, poVertCentered);
TsPrintOptions = set of TsPrintOption;
TsPageLayout = record // all lengths in mm
Orientation: TsPageOrientation;
PageWidth: Double; // for "normal" orientation (mostly portrait)
PageHeight: Double;
LeftMargin: Double;
RightMargin: Double;
TopMargin: Double;
BottomMargin: Double;
HeaderDistance: Double;
FooterDistance: Double;
HeaderMargin: Double;
FooterMargin: Double;
StartPageNumber: Integer;
ScalingFactor: Double; // in percent
FitWidthToPages: Integer;
FitHeightToPages: Integer;
Copies: Integer;
Options: TsPrintOptions;
end;
function BuildFormatStringFromSection(const ASection: TsNumFormatSection): String;

View File

@ -134,17 +134,19 @@ function TryStrToFloatAuto(AText: String; out ANumber: Double;
function TryFractionStrToFloat(AText: String; out ANumber: Double;
out AMaxDigits: Integer): Boolean;
function TwipsToPts(AValue: Integer): Single;
function PtsToTwips(AValue: Single): Integer;
function cmToPts(AValue: Double): Double;
function PtsToCm(AValue: Double): Double;
function InToPts(AValue: Double): Double;
function PtsToIn(AValue: Double): Double;
function mmToPts(AValue: Double): Double;
function PtsToMM(AValue: Double): Double;
function pxToPts(AValue, AScreenPixelsPerInch: Integer): Double;
function PtsToPx(AValue: Double; AScreenPixelsPerInch: Integer): Integer;
function HTMLLengthStrToPts(AValue: String): Double;
function TwipsToPts(AValue: Integer): Single; inline;
function PtsToTwips(AValue: Single): Integer; inline;
function cmToPts(AValue: Double): Double; inline;
function PtsToCm(AValue: Double): Double; inline;
function InToMM(AValue: Double): Double; inline;
function InToPts(AValue: Double): Double; inline;
function PtsToIn(AValue: Double): Double; inline;
function mmToPts(AValue: Double): Double; inline;
function mmToIn(AValue: Double): Double; inline;
function PtsToMM(AValue: Double): Double; inline;
function pxToPts(AValue, AScreenPixelsPerInch: Integer): Double; inline;
function PtsToPx(AValue: Double; AScreenPixelsPerInch: Integer): Integer; inline;
function HTMLLengthStrToPts(AValue: String; DefaultUnits: String = 'pt'): Double;
function HTMLColorStrToColor(AValue: String): TsColorValue;
function ColorToHTMLColorStr(AValue: TsColorValue; AExcelDialect: Boolean = false): String;
@ -165,6 +167,7 @@ procedure FixHyperlinkPathDelims(var ATarget: String);
procedure InitCell(out ACell: TCell); overload;
procedure InitCell(ARow, ACol: Cardinal; out ACell: TCell); overload;
procedure InitFormatRecord(out AValue: TsCellFormat);
procedure InitPageLayout(out APageLayout: TsPageLayout);
procedure AppendToStream(AStream: TStream; const AString: String); inline; overload;
procedure AppendToStream(AStream: TStream; const AString1, AString2: String); inline; overload;
@ -1977,6 +1980,28 @@ begin
Result := AValue / 72 * 2.54;
end;
{@@ ----------------------------------------------------------------------------
Converts inches to millimeters
@param AValue Length value in inches
@return Value converted to mm
-------------------------------------------------------------------------------}
function InToMM(AValue: Double): Double;
begin
Result := AValue * 25.4;
end;
{@@ ----------------------------------------------------------------------------
Converts millimeters to inches
@param AValue Length value in millimeters
@return Value converted to inches
-------------------------------------------------------------------------------}
function mmToIn(AValue: Double): Double;
begin
Result := AValue / 25.4;
end;
{@@ ----------------------------------------------------------------------------
Converts inches to points (72 pts = 1 inch)
@ -2052,9 +2077,11 @@ end;
such as '1.25in'. These unit codes are accepted:
'px' (pixels), 'pt' (points), 'in' (inches), 'mm' (millimeters),
'cm' (centimeters).
@param DefaultUnits String identifying the units to be used if not contained
in AValue.
@return Extracted length in points
-------------------------------------------------------------------------------}
function HTMLLengthStrToPts(AValue: String): Double;
function HTMLLengthStrToPts(AValue: String; DefaultUnits: String = 'pt'): Double;
var
units: String;
x: Double;
@ -2062,10 +2089,11 @@ var
begin
if (Length(AValue) > 1) and (AValue[Length(AValue)] in ['a'..'z', 'A'..'Z']) then begin
units := lowercase(Copy(AValue, Length(AValue)-1, 2));
if units = '' then units := DefaultUnits;
val(copy(AValue, 1, Length(AValue)-2), x, res);
// No hasseling with the decimal point...
end else begin
units := '';
units := DefaultUnits;
val(AValue, x, res);
end;
if res <> 0 then
@ -2376,6 +2404,30 @@ begin
AValue.NumberFormatIndex := -1; // GENERAL format not contained in NumFormatList
end;
{@@ ----------------------------------------------------------------------------
Initializes the fields of a TsPageLayout record
-------------------------------------------------------------------------------}
procedure InitPageLayout(out APageLayout: TsPageLayout);
begin
with APageLayout do begin
Orientation := spoPortrait;
PageWidth := 210;
PageHeight := 297;
LeftMargin := InToMM(0.7);
RightMargin := InToMM(0.7);
TopMargin := InToMM(0.78740157499999996);
BottomMargin := InToMM(0.78740157499999996);
HeaderMargin := InToMM(0.3);
FooterMargin := InToMM(0.3);
StartPageNumber := 1;
ScalingFactor := 100; // Percent
FitWidthToPages := 0; // use as many pages as needed
FitHeightToPages := 0;
Copies := 1;
Options := [];
end;
end;
{@@ ----------------------------------------------------------------------------
Appends a string to a stream

View File

@ -455,27 +455,33 @@ begin
case RecordType of
INT_EXCEL_ID_BLANK : ReadBlank(AStream);
INT_EXCEL_ID_BOF : ;
INT_EXCEL_ID_BOOLERROR : ReadBool(AStream);
INT_EXCEL_ID_BOTTOMMARGIN: ReadBottomMargin(AStream);
INT_EXCEL_ID_CODEPAGE : ReadCodePage(AStream);
INT_EXCEL_ID_NOTE : ReadComment(AStream);
INT_EXCEL_ID_COLWIDTH : ReadColWidth(AStream);
INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream);
INT_EXCEL_ID_EOF : BIFF2EOF := True;
INT_EXCEL_ID_FONT : ReadFont(AStream);
INT_EXCEL_ID_FONTCOLOR : ReadFontColor(AStream);
INT_EXCEL_ID_FORMAT : ReadFormat(AStream);
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
INT_EXCEL_ID_INTEGER : ReadInteger(AStream);
INT_EXCEL_ID_IXFE : ReadIXFE(AStream);
INT_EXCEL_ID_NUMBER : ReadNumber(AStream);
INT_EXCEL_ID_LABEL : ReadLabel(AStream);
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
INT_EXCEL_ID_STRING : ReadStringRecord(AStream);
INT_EXCEL_ID_COLWIDTH : ReadColWidth(AStream);
INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream);
INT_EXCEL_ID_LEFTMARGIN : ReadLeftMargin(AStream);
INT_EXCEL_ID_NOTE : ReadComment(AStream);
INT_EXCEL_ID_NUMBER : ReadNumber(AStream);
INT_EXCEL_ID_PANE : ReadPane(AStream);
INT_EXCEL_ID_PRINTGRID : ReadPrintGridLines(AStream);
INT_EXCEL_ID_PRINTHEADERS: ReadPrintHeaders(AStream);
INT_EXCEL_ID_RIGHTMARGIN : ReadRightMargin(AStream);
INT_EXCEL_ID_ROW : ReadRowInfo(AStream);
INT_EXCEL_ID_STRING : ReadStringRecord(AStream);
INT_EXCEL_ID_TOPMARGIN : ReadTopMargin(AStream);
INT_EXCEL_ID_DEFROWHEIGHT: ReadDefRowHeight(AStream);
INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream);
INT_EXCEL_ID_PANE : ReadPane(AStream);
INT_EXCEL_ID_XF : ReadXF(AStream);
INT_EXCEL_ID_BOF : ;
INT_EXCEL_ID_EOF : BIFF2EOF := True;
else
// nothing
end;
@ -1229,8 +1235,18 @@ begin
raise Exception.Create(rsWorksheetNotFound1);
WriteBOF(AStream);
WriteFonts(AStream);
WriteCodePage(AStream, FCodePage);
WritePrintHeaders(AStream);
WritePrintGridLines(AStream);
WriteFonts(AStream);
// Page settings block
WriteLeftMargin(AStream);
WriteRightMargin(AStream);
WriteTopMargin(AStream);
WriteBottomMargin(AStream);
// WritePageSetup(AStream); // does not exist in BIFF2
WriteFormatCount(AStream);
WriteNumFormats(AStream);
WriteXFRecords(AStream);

View File

@ -390,25 +390,34 @@ begin
case RecordType of
INT_EXCEL_ID_BLANK : ReadBlank(AStream);
INT_EXCEL_ID_BOF : ;
INT_EXCEL_ID_BOOLERROR : ReadBool(AStream);
INT_EXCEL_ID_BOTTOMMARGIN : ReadBottomMargin(AStream);
INT_EXCEL_ID_COLINFO : ReadColInfo(AStream);
INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream);
INT_EXCEL_ID_EOF : SectionEOF := True;
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
INT_EXCEL_ID_HCENTER : ReadHCENTER(AStream);
INT_EXCEL_ID_LABEL : ReadLabel(AStream);
INT_EXCEL_ID_LEFTMARGIN : ReadLeftMargin(AStream);
INT_EXCEL_ID_MULBLANK : ReadMulBlank(AStream);
INT_EXCEL_ID_MULRK : ReadMulRKValues(AStream);
INT_EXCEL_ID_NOTE : ReadComment(AStream);
INT_EXCEL_ID_NUMBER : ReadNumber(AStream);
INT_EXCEL_ID_LABEL : ReadLabel(AStream);
INT_EXCEL_ID_RSTRING : ReadRichString(AStream); //(RSTRING) This record stores a formatted text cell (Rich-Text). In BIFF8 it is usually replaced by the LABELSST record. Excel still uses this record, if it copies formatted text cells to the clipboard.
INT_EXCEL_ID_RK : ReadRKValue(AStream); //(RK) This record represents a cell that contains an RK value (encoded integer or floating-point value). If a floating-point value cannot be encoded to an RK value, a NUMBER record will be written. This record replaces the record INTEGER written in BIFF2.
INT_EXCEL_ID_MULRK : ReadMulRKValues(AStream);
INT_EXCEL_ID_COLINFO : ReadColInfo(AStream);
INT_EXCEL_ID_STANDARDWIDTH : ReadStandardWidth(AStream, FWorksheet);
INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream);
INT_EXCEL_ID_ROW : ReadRowInfo(AStream);
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
INT_EXCEL_ID_SHAREDFMLA : ReadSharedFormula(AStream);
INT_EXCEL_ID_STRING : ReadStringRecord(AStream);
INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream);
INT_EXCEL_ID_PANE : ReadPane(AStream);
INT_EXCEL_ID_BOF : ;
INT_EXCEL_ID_EOF : SectionEOF := True;
INT_EXCEL_ID_PAGESETUP : ReadPageSetup(AStream);
INT_EXCEL_ID_PRINTGRID : ReadPrintGridLines(AStream);
INT_EXCEL_ID_PRINTHEADERS : ReadPrintHeaders(AStream);
INT_EXCEL_ID_RIGHTMARGIN : ReadRightMargin(AStream);
INT_EXCEL_ID_RK : ReadRKValue(AStream); //(RK) This record represents a cell that contains an RK value (encoded integer or floating-point value). If a floating-point value cannot be encoded to an RK value, a NUMBER record will be written. This record replaces the record INTEGER written in BIFF2.
INT_EXCEL_ID_ROW : ReadRowInfo(AStream);
INT_EXCEL_ID_RSTRING : ReadRichString(AStream); //(RSTRING) This record stores a formatted text cell (Rich-Text). In BIFF8 it is usually replaced by the LABELSST record. Excel still uses this record, if it copies formatted text cells to the clipboard.
INT_EXCEL_ID_SHAREDFMLA : ReadSharedFormula(AStream);
INT_EXCEL_ID_STANDARDWIDTH : ReadStandardWidth(AStream, FWorksheet);
INT_EXCEL_ID_TOPMARGIN : ReadTopMargin(AStream);
INT_EXCEL_ID_STRING : ReadStringRecord(AStream);
INT_EXCEL_ID_VCENTER : ReadVCENTER(AStream);
INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream);
{$IFDEF FPSPREADDEBUG} // Only write out if debugging
else
@ -1030,9 +1039,19 @@ begin
AStream.Position := CurrentPos;
WriteBOF(AStream, INT_BOF_SHEET);
WriteIndex(AStream);
// WritePageSetup(AStream);
WritePrintHeaders(AStream);
WritePrintGridLines(AStream);
// Page settings block
WriteHCenter(AStream);
WriteVCenter(AStream);
WriteLeftMargin(AStream);
WriteRightMargin(AStream);
WriteTopMargin(AStream);
WriteBottomMargin(AStream);
WritePageSetup(AStream);
WriteColInfos(AStream, FWorksheet);
WriteDimensions(AStream, FWorksheet);
WriteWindow2(AStream, FWorksheet);

View File

@ -120,6 +120,7 @@ type
TsSpreadBIFF8Writer = class(TsSpreadBIFFWriter)
protected
function GetPrintOptions: Word; override;
{ Record writing methods }
procedure WriteBOF(AStream: TStream; ADataType: Word);
function WriteBoundsheet(AStream: TStream; ASheetName: string): Int64;
@ -675,39 +676,51 @@ begin
case RecordType of
INT_EXCEL_ID_BLANK : ReadBlank(AStream);
INT_EXCEL_ID_BOOLERROR : ReadBool(AStream);
INT_EXCEL_ID_CONTINUE : ReadCONTINUE(AStream);
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
INT_EXCEL_ID_HYPERLINK : ReadHyperlink(AStream);
INT_EXCEL_ID_HLINKTOOLTIP: ReadHyperlinkToolTip(AStream);
INT_EXCEL_ID_LABEL : ReadLabel(AStream);
INT_EXCEL_ID_MULBLANK : ReadMulBlank(AStream);
INT_EXCEL_ID_NOTE : ReadNOTE(AStream);
INT_EXCEL_ID_NUMBER : ReadNumber(AStream);
INT_EXCEL_ID_OBJ : ReadOBJ(AStream);
INT_EXCEL_ID_SHAREDFMLA : ReadSharedFormula(AStream);
INT_EXCEL_ID_STRING : ReadStringRecord(AStream);
INT_EXCEL_ID_TXO : ReadTXO(AStream);
INT_EXCEL_ID_BLANK : ReadBlank(AStream);
INT_EXCEL_ID_BOF : ;
INT_EXCEL_ID_BOOLERROR : ReadBool(AStream);
INT_EXCEL_ID_BOTTOMMARGIN : ReadBottomMargin(AStream);
INT_EXCEL_ID_COLINFO : ReadColInfo(AStream);
INT_EXCEL_ID_CONTINUE : ReadCONTINUE(AStream);
INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream);
INT_EXCEL_ID_EOF : SectionEOF := True;
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
INT_EXCEL_ID_HCENTER : ReadHCENTER(AStream);
INT_EXCEL_ID_HLINKTOOLTIP : ReadHyperlinkToolTip(AStream);
INT_EXCEL_ID_HYPERLINK : ReadHyperlink(AStream);
INT_EXCEL_ID_LABEL : ReadLabel(AStream);
INT_EXCEL_ID_LABELSST : ReadLabelSST(AStream);
INT_EXCEL_ID_LEFTMARGIN : ReadLeftMargin(AStream);
INT_EXCEL_ID_MERGEDCELLS : ReadMergedCells(AStream);
INT_EXCEL_ID_MULBLANK : ReadMulBlank(AStream);
INT_EXCEL_ID_MULRK : ReadMulRKValues(AStream);
INT_EXCEL_ID_NOTE : ReadNOTE(AStream);
INT_EXCEL_ID_NUMBER : ReadNumber(AStream);
INT_EXCEL_ID_OBJ : ReadOBJ(AStream);
INT_EXCEL_ID_PAGESETUP : ReadPageSetup(AStream);
INT_EXCEL_ID_PANE : ReadPane(AStream);
INT_EXCEL_ID_PRINTGRID : ReadPrintGridLines(AStream);
INT_EXCEL_ID_PRINTHEADERS : ReadPrintHeaders(AStream);
INT_EXCEL_ID_RIGHTMARGIN : ReadRightMargin(AStream);
INT_EXCEL_ID_ROW : ReadRowInfo(AStream);
//(RSTRING) This record stores a formatted text cell (Rich-Text).
// In BIFF8 it is usually replaced by the LABELSST record. Excel still
// uses this record, if it copies formatted text cells to the clipboard.
INT_EXCEL_ID_RSTRING : ReadRichString(AStream);
INT_EXCEL_ID_RSTRING : ReadRichString(AStream);
// (RK) This record represents a cell that contains an RK value
// (encoded integer or floating-point value). If a floating-point
// value cannot be encoded to an RK value, a NUMBER record will be written.
// This record replaces the record INTEGER written in BIFF2.
INT_EXCEL_ID_RK : ReadRKValue(AStream);
INT_EXCEL_ID_MULRK : ReadMulRKValues(AStream);
INT_EXCEL_ID_LABELSST : ReadLabelSST(AStream); //BIFF8 only
INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream);
INT_EXCEL_ID_COLINFO : ReadColInfo(AStream);
INT_EXCEL_ID_MERGEDCELLS : ReadMergedCells(AStream);
INT_EXCEL_ID_ROW : ReadRowInfo(AStream);
INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream);
INT_EXCEL_ID_PANE : ReadPane(AStream);
INT_EXCEL_ID_BOF : ;
INT_EXCEL_ID_EOF : SectionEOF := True;
INT_EXCEL_ID_RK : ReadRKValue(AStream);
INT_EXCEL_ID_SHAREDFMLA : ReadSharedFormula(AStream);
INT_EXCEL_ID_STRING : ReadStringRecord(AStream);
INT_EXCEL_ID_TOPMARGIN : ReadTopMargin(AStream);
INT_EXCEL_ID_TXO : ReadTXO(AStream);
INT_EXCEL_ID_VCENTER : ReadVCENTER(AStream);
INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream);
else
// nothing
end;
@ -1614,6 +1627,17 @@ begin
FDateMode := Excel8Settings.DateMode;
end;
function TsSpreadBIFF8Writer.GetPrintOptions: Word;
Begin
Result := inherited GetPrintOptions;
{ The following flags are valid for BIFF8 only:
Bit 9: 0 = Print notes as displayed; 1 = Print notes at end of sheet
Bit 11-10: 00 = Print errors as displayed; 1 = Do not print errors
2 = Print errors as “--”; 3 = Print errors as “#N/A” }
if poCommentsAtEnd in FWorksheet.PageLayout.Options then
Result := Result or $0200;
end;
{@@ ----------------------------------------------------------------------------
Writes an Excel BIFF8 file to the disc
@ -1695,8 +1719,20 @@ begin
WriteBOF(AStream, INT_BOF_SHEET);
WriteIndex(AStream);
WritePrintHeaders(AStream);
WritePrintGridLines(AStream);
//WriteSheetPR(AStream);
// WritePageSetup(AStream);
// Page setting block
WriteHCenter(AStream);
WriteVCenter(AStream);
WriteLeftMargin(AStream);
WriteRightMargin(AStream);
WriteTopMargin(AStream);
WriteBottomMargin(AStream);
WritePageSetup(AStream);
WriteColInfos(AStream, FWorksheet);
WriteDimensions(AStream, FWorksheet);
//WriteRowAndCellBlock(AStream, sheet);

View File

@ -20,6 +20,12 @@ const
INT_EXCEL_ID_NOTE = $001C;
INT_EXCEL_ID_SELECTION = $001D;
INT_EXCEL_ID_DATEMODE = $0022;
INT_EXCEL_ID_LEFTMARGIN = $0026;
INT_EXCEL_ID_RIGHTMARGIN = $0027;
INT_EXCEL_ID_TOPMARGIN = $0028;
INT_EXCEL_ID_BOTTOMMARGIN= $0029;
INT_EXCEL_ID_PRINTHEADERS= $002A;
INT_EXCEL_ID_PRINTGRID = $002B;
INT_EXCEL_ID_CONTINUE = $003C;
INT_EXCEL_ID_WINDOW1 = $003D;
INT_EXCEL_ID_PANE = $0041;
@ -33,6 +39,8 @@ const
{ RECORD IDs which did not change across version 3-8}
INT_EXCEL_ID_COLINFO = $007D; // does not exist in BIFF2
INT_EXCEL_ID_SHEETPR = $0081; // does not exist in BIFF2
INT_EXCEL_ID_HCENTER = $0083; // does not exist in BIFF2
INT_EXCEL_ID_VCENTER = $0084; // does not exist in BIFF2
INT_EXCEL_ID_COUNTRY = $008C; // does not exist in BIFF2
INT_EXCEL_ID_PALETTE = $0092; // does not exist in BIFF2
INT_EXCEL_ID_DIMENSIONS = $0200; // BIFF2: $0000
@ -215,6 +223,100 @@ const
{ Index of last built-in XF format record }
LAST_BUILTIN_XF = 15;
PAPER_SIZES: array[0..90] of array[0..1] of Double = ( // Dimensions in mm
( 0.0 , 0.0 ), // 0 - undefined
(2.54* 8.5 , 11.0 *2.54), // 1 - Letter
(2.54* 8.5 , 11.0 *2.54), // 2 - Letter small
(2.54* 11.0 , 17.0 *2.54), // 3 - Tabloid
(2.54* 17.0 , 11.0 *2.54), // 4 - Ledger
(2.54* 8.5 , 14.0 *2.54), // 5 - Legal
(2.54* 5.5 , 8.5 *2.54), // 6 - Statement
(2.54* 7.25 , 10.5 *2.54), // 7 - Executive
( 297.0 , 420.0 ), // 8 - A3
( 210.0 , 297.0 ), // 9 - A4
( 210.0 , 297.0 ), // 10 - A4 small
( 148.0 , 210.0 ), // 11 - A5
( 257.0 , 364.0 ), // 12 - B4 (JIS)
( 182.0 , 257.0 ), // 13 - B5 (JIS)
(2.54* 8.5 , 13.0 *2.54), // 14 - Folie
( 215.0 , 275.0 ), // 15 - Quarto
(2.54* 10.0 , 14.0 *2.54), // 16 - 10x14
(2.54* 11.0 , 17.0 *2.54), // 17 - 11x17
(2.54* 8.5 , 11.0 *2.54), // 18 - Note
(2.54* 3.875, 8.875*2.54), // 19 - Envelope #9
(2.54* 4.125, 9.5 *2.54), // 20 - Envelope #10
(2.54* 4.5 , 10.375*2.54), // 21 - Envelope #11
(2.54* 4.75 , 11.0 *2.54), // 22 - Envelope #12
(2.54* 5.0 , 11.5 *2.54), // 23 - Envelope #14
(2.54* 17.0 , 22.0 *2.54), // 24 - C
(2.54* 22.0 , 34.0 *2.54), // 25 - D
(2.54* 34.0 , 44.0 *2.54), // 26 - E
( 110.0 , 220.0 ), // 27 - Envelope DL
( 162.0 , 229.0 ), // 28 - Envelope C5
( 324.0 , 458.0 ), // 29 - Envelope C3
( 229.0 , 324.0 ), // 30 - Envelope C4
( 114.0 , 162.0 ), // 31 - Envelope C6
( 114.0 , 229.0 ), // 32 - Envelope C6/C5
( 250.0 , 353.0 ), // 33 - B4 (ISO)
( 176.0 , 250.0 ), // 34 - B5 (ISO)
( 125.0 , 176.0 ), // 35 - B6 (ISO)
( 110.0 , 230.0 ), // 36 - Envelope Italy
(2.54* 3.875, 7.5 *2.54), // 37 - Envelope Monarch
(2.54* 3.625, 6.5 *2.54), // 38 - 6 3/4 Envelope
(2.54* 14.875, 11.0 *2.54), // 39 - US Standard Fanfold
(2.54* 8.5 , 12.0 *2.54), // 40 - German Std Fanfold
(2.54* 8.5 , 13.0 *2.54), // 41 - German Legal Fanfold
( 250.0 , 353.0 ), // 42 - B4 (ISO)
( 100.0 , 148.0 ), // 43 - Japanese Postcard
(2.54* 9.0 , 11.0 *2.54), // 44 - 9x11
(2.54* 10.0 , 11.0 *2.54), // 45 - 10x11
(2.54* 15.0 , 11.0 *2.54), // 46 - 15x11
( 220.0 , 220.0 ), // 47 - Envelope Invite
( 0.0 , 0.0 ), // 48 - undefined
( 0.0 , 0.0 ), // 49 - undefined
(2.54* 9.5 , 11.0 *2.54), // 50 - Letter Extra
(2.54* 9.5 , 15.0 *2.54), // 51 - Legal Extra
(2.54* 11.6875, 18.0 *2.54), // 52 - Tabloid Extra
( 235.0 , 322.0 ), // 53 - A4 Extra
(2.54* 8.5 , 11.0 *2.54), // 54 - Letter Transverse
( 210.0 , 297.0 ), // 55 - A4 Transverse
(2.54* 9.5 , 11.0 *2.54), // 56 - Letter Extra Transverse
( 227.0 , 356.0 ), // 57 - Super A/A4
( 305.0 , 487.0 ), // 58 - Super B/B4
(2.54* 8.5 , 12.6875*2.54), // 59 - Letter plus
( 210.0 , 330.0 ), // 60 - A4 plus
( 148.0 , 210.0 ), // 61 - A5 transverse
( 182.0 , 257.0 ), // 62 - B5 (JIS) transverse
( 322.0 , 445.0 ), // 63 - A3 Extra
( 174.0 , 235.0 ), // 64 - A5 Extra
( 201.0 , 276.0 ), // 65 - B5 (ISO) Extra
( 420.0 , 594.0 ), // 66 - A2
( 297.0 , 420.0 ), // 67 - A3 Transverse
( 322.0 , 445.0 ), // 68 - A3 Extra Transverse
( 200.0 , 148.0 ), // 69 - Double Japanese Postcard
( 105.0 , 148.0 ), // 70 - A6
( 0.0 , 0.0 ), // 71 - undefined
( 0.0 , 0.0 ), // 72 - undefined
( 0.0 , 0.0 ), // 73 - undefined
( 0.0 , 0.0 ), // 74 - undefined
(2.54* 11.0 , 8.5 *2.54), // 75 - Letter rotated
( 420.0 , 297.0 ), // 76 - A3 rotated
( 297.0 , 210.0 ), // 77 - A4 rotated
( 210.0 , 148.0 ), // 78 - A5 rotated
( 364.0 , 257.0 ), // 79 - B4 (JIS) rotated
( 257.0 , 182.0 ), // 80 - B5 (JIS) rotated
( 148.0 , 100.0 ), // 81 - Japanese Postcard rotated
( 148.0 , 200.0 ), // 82 - Double Japanese Postcard rotated
( 148.0 , 105.0 ), // 83 - A6 rotated
( 0.0 , 0.0 ), // 84 - undefined
( 0.0 , 0.0 ), // 85 - undefined
( 0.0 , 0.0 ), // 86 - undefined
( 0.0 , 0.0 ), // 87 - undefined
( 128.0 , 182.0 ), // 88 - B6 (JIS)
( 182.0 , 128.0 ), // 89 - B6 (JIS) rotated
(2.54* 12.0 , 11.0 *2.54) // 90 - 12x11
);
type
TDateMode=(dm1900,dm1904); //DATEMODE values, 5.28
@ -263,6 +365,7 @@ type
// Read a blank cell
procedure ReadBlank(AStream: TStream); override;
procedure ReadBool(AStream: TStream); override;
procedure ReadBottomMargin(AStream: TStream);
procedure ReadCodePage(AStream: TStream);
// Read column info
procedure ReadColInfo(const AStream: TStream);
@ -278,6 +381,8 @@ type
procedure ReadFormat(AStream: TStream); virtual;
// Read FORMULA record
procedure ReadFormula(AStream: TStream); override;
procedure ReadHCENTER(AStream: TStream);
procedure ReadLeftMargin(AStream: TStream);
// Read multiple blank cells
procedure ReadMulBlank(AStream: TStream);
// Read multiple RK cells
@ -286,8 +391,13 @@ type
procedure ReadNumber(AStream: TStream); override;
// Read palette
procedure ReadPalette(AStream: TStream);
// Read page setup
procedure ReadPageSetup(AStream: TStream);
// Read PANE record
procedure ReadPane(AStream: TStream);
procedure ReadPrintGridLines(AStream: TStream);
procedure ReadPrintHeaders(AStream: TStream);
procedure ReadRightMargin(AStream: TStream);
// Read an RK value cell
procedure ReadRKValue(AStream: TStream);
// Read the row, column, and XF index at the current stream position
@ -310,11 +420,13 @@ type
ASharedFormulaBase: PCell = nil): Boolean;
function ReadRPNTokenArraySize(AStream: TStream): word; virtual;
procedure ReadSharedFormula(AStream: TStream);
procedure ReadTopMargin(AStream: TStream);
// Helper function for reading a string with 8-bit length
function ReadString_8bitLen(AStream: TStream): String; virtual;
// Read STRING record (result of string formula)
procedure ReadStringRecord(AStream: TStream); virtual;
procedure ReadVCENTER(AStream: TStream);
// Read WINDOW2 record (gridlines, sheet headers)
procedure ReadWindow2(AStream: TStream); virtual;
@ -335,6 +447,7 @@ type
function FixColor(AColor: TsColor): TsColor; override;
function GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
function GetLastColIndex(AWorksheet: TsWorksheet): Word;
function GetPrintOptions: Word; virtual;
// Helper function for writing the BIFF header
procedure WriteBIFFHeader(AStream: TStream; ARecID, ARecSize: Word);
@ -347,6 +460,8 @@ type
// Write out BOOLEAN cell record
procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: Boolean; ACell: PCell); override;
// Writes out bottom page margin for printing
procedure WriteBottomMargin(AStream: TStream);
// Writes out used codepage for character encoding
procedure WriteCodePage(AStream: TStream; ACodePage: String); virtual;
// Writes out column info(s)
@ -365,6 +480,9 @@ type
// Writes out a FORMULA record; formula is stored in cell already
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); override;
procedure WriteHCenter(AStream: TStream);
// Writes out left page margin for printing
procedure WriteLeftMargin(AStream: TStream);
// Writes out a FORMAT record
procedure WriteNumFormat(AStream: TStream; ANumFormatStr: String;
ANumFormatIndex: Integer); virtual;
@ -379,6 +497,11 @@ type
// Writes out a PANE record
procedure WritePane(AStream: TStream; ASheet: TsWorksheet; IsBiff58: Boolean;
out ActivePane: Byte);
// Writes out whether grid lines are printed
procedure WritePrintGridLines(AStream: TStream);
procedure WritePrintHeaders(AStream: TStream);
// Writes out right page margin for printing
procedure WriteRightMargin(AStream: TStream);
// Writes out a ROW record
procedure WriteRow(AStream: TStream; ASheet: TsWorksheet;
ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow); virtual;
@ -414,6 +537,9 @@ type
*)
procedure WriteSheetPR(AStream: TStream);
procedure WriteStringRecord(AStream: TStream; AString: String); virtual;
// Writes out the top page margin used when printing
procedure WriteTopMargin(AStream: TStream);
procedure WriteVCenter(AStream: TStream);
// Writes cell content received by workbook in OnNeedCellData event
procedure WriteVirtualCells(AStream: TStream);
// Writes out a WINDOW1 record
@ -510,6 +636,7 @@ type
TextLen: Word;
end;
function ConvertExcelDateTimeToDateTime(const AExcelDateNum: Double;
ADateMode: TDateMode): TDateTime;
begin
@ -851,6 +978,18 @@ begin
Workbook.OnReadCellData(Workbook, r, c, cell);
end;
{@@ ----------------------------------------------------------------------------
Reads the bottom page margin of the current worksheet (for printing).
The file value is in inches.
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.ReadBottomMargin(AStream: TStream);
var
dbl: Double;
begin
AStream.ReadBuffer(dbl, SizeOf(dbl));
FWorksheet.PageLayout.BottomMargin := InToMM(dbl);
end;
{@@ ----------------------------------------------------------------------------
Reads the code page used in the xls file
In BIFF8 it seams to always use the UTF-16 codepage
@ -1141,6 +1280,30 @@ begin
Workbook.OnReadCellData(Workbook, ARow, ACol, cell);
end;
{@@ ----------------------------------------------------------------------------
Reads whether the page is to be centered horizontally for printing
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.ReadHCENTER(AStream: TStream);
var
w: word;
begin
w := WordLEToN(AStream.ReadWord);
if w = 1 then Include(FWorksheet.PageLayout.Options, poHorCentered);
end;
{@@ ----------------------------------------------------------------------------
Reads the left page margin of the current worksheet (for printing).
The file value is in inches.
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.ReadLeftMargin(AStream: TStream);
var
dbl: Double;
begin
AStream.ReadBuffer(dbl, SizeOf(dbl));
FWorksheet.PageLayout.LeftMargin := InToMM(dbl);
end;
{@@ ----------------------------------------------------------------------------
Reads multiple blank cell records
Valid for BIFF5 and BIFF8 (does not exist before)
@ -1294,6 +1457,72 @@ begin
FPaletteFound := true;
end;
{@@ ----------------------------------------------------------------------------
Reads the page setup record containing some parameters for printing
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.ReadPageSetup(AStream: TStream);
var
w: Word;
dbl: Double;
begin
// Paper size
w := WordLEToN(AStream.ReadWord);
if (w >= 0) and (w <= High(PAPER_SIZES)) then
begin
FWorksheet.PageLayout.PageWidth := PAPER_SIZES[w, 0];
FWorksheet.PageLayout.PageHeight := PAPER_SIZES[w, 1];
end;
// Scaling factor in percent
FWorksheet.PageLayout.ScalingFactor := WordLEToN(AStream.ReadWord);
// Start page number
FWorksheet.PageLayout.StartPageNumber := WordLEToN(AStream.ReadWord);
// Fit worksheet width to this number of pages (0 = use as many as neede)
FWorksheet.PageLayout.FitWidthToPages := WordLEToN(AStream.ReadWord);
// Fit worksheet height to this number of pages (0 = use as many as needed)
FWorksheet.PageLayout.FitHeightToPages := WordLEToN(AStream.ReadWord);
// Option flags
w := WordLEToN(AStream.ReadWord);
if w and $0001 <> 0 then
Include(FWorksheet.pageLayout.Options, poPrintPagesByRows);
if w and $0002 <> 0 then
FWorksheet.PageLayout.Orientation := spoPortrait else
FWorksheet.PageLayout.Orientation := spoLandscape;
if w and $0008 <> 0 then
Include(FWorksheet.PageLayout.Options, poMonochrome);
if w and $0010 <> 0 then
Include(FWorksheet.PageLayout.Options, poDraftQuality);
if w and $0020 <> 0 then
Include(FWorksheet.PageLayout.Options, poPrintCellComments);
if w and $0040 <> 0 then
Include(FWorksheet.PageLayout.Options, poDefaultOrientation);
if w and $0080 <> 0 then
Include(FWorksheet.PageLayout.Options, poUseStartPageNumber);
if w and $0200 <> 0 then
Include(FWorksheet.Pagelayout.Options, poCommentsAtEnd);
// Print resolution in dpi -- ignoried
w := WordLEToN(AStream.ReadWord);
// Vertical print resolution in dpi -- ignored
w := WordLEToN(AStream.ReadWord);
// Header margin
AStream.ReadBuffer(dbl, SizeOf(dbl));
FWorksheet.PageLayout.HeaderMargin := InToMM(dbl);
// Footer margin
AStream.ReadBuffer(dbl, SizeOf(dbl));
FWorksheet.PageLayout.FooterMargin := InToMM(dbl);
// Number of copies
FWorksheet.PageLayout.Copies := WordLEToN(AStream.ReadWord);
end;
{@@ ----------------------------------------------------------------------------
Reads pane sizes
Valid for all BIFF versions
@ -1321,6 +1550,30 @@ begin
[9] 1 Not used (BIFF5-BIFF8 only, not written in BIFF2-BIFF4) }
end;
{@@ ----------------------------------------------------------------------------
Reads whether the gridlines are printed or not
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.ReadPrintGridLines(AStream: TStream);
var
w: Word;
begin
w := WordLEToN(AStream.ReadWord);
if w = 1 then
Include(FWorksheet.PageLayout.Options, poPrintGridLines);
end;
{@@ ----------------------------------------------------------------------------
Reads whether the spreadsheet row/column headers are printed or not
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.ReadPrintHeaders(AStream: TStream);
var
w: Word;
begin
w := WordLEToN(AStream.ReadWord);
if w = 1 then
Include(FWorksheet.PageLayout.Options , poPrintHeaders);
end;
{@@ ----------------------------------------------------------------------------
Reads the row, column and xf index
NOT VALID for BIFF2
@ -1336,6 +1589,18 @@ begin
AXF := WordLEtoN(AStream.ReadWord);
end;
{@@ ----------------------------------------------------------------------------
Reads the right page margin of the current worksheet (for printing).
The file value is in inches.
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.ReadRightMargin(AStream: TStream);
var
dbl: Double;
begin
AStream.ReadBuffer(dbl, SizeOf(dbl));
FWorksheet.PageLayout.RightMargin := InToMM(dbl);
end;
{@@ ----------------------------------------------------------------------------
Reads an RK value cell from the stream
Valid since BIFF3.
@ -1799,6 +2064,29 @@ begin
Unused(AStream);
end;
{@@ ----------------------------------------------------------------------------
Reads the top page margin of the current worksheet (for printing).
The file value is in inches.
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.ReadTopMargin(AStream: TStream);
var
dbl: Double;
begin
AStream.ReadBuffer(dbl, SizeOf(dbl));
FWorksheet.PageLayout.TopMargin := InToMM(dbl);
end;
{@@ ----------------------------------------------------------------------------
Reads whether the page is to be centered vertically for printing
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.ReadVCENTER(AStream: TStream);
var
w: word;
begin
w := WordLEToN(AStream.ReadWord);
if w = 1 then Include(FWorksheet.PageLayout.Options, poVertCentered);
end;
{@@ ----------------------------------------------------------------------------
Reads the WINDOW2 record containing information like "show grid lines",
"show sheet headers", "panes are frozen", etc.
@ -1893,6 +2181,46 @@ begin
Result := AWorksheet.GetLastColIndex;
end;
{@@ ----------------------------------------------------------------------------
Converts the Options of the worksheet's PageLayout to the bitmap required
by the PageSetup record
Is overridden by BIFF8 which uses more bits. Not used by BIFF2.
-------------------------------------------------------------------------------}
function TsSpreadBIFFWriter.GetPrintOptions: Word;
begin
{ Options:
Bit 0: 0 = Print pages in columns; 1 = Print pages in rows
Bit 1: 0 = Landscape; 1 = Portrait
Bit 2: 1 = Paper size, scaling factor, paper orientation (portrait/landscape),
print resolution and number of copies are not initialised
Bit 3: 0 = Print coloured; 1 = Print black and white
Bit 4: 0 = Default print quality; 1 = Draft quality
Bit 5: 0 = Do not print cell notes; 1 = Print cell notes
Bit 6: 0 = Use paper orientation (portrait/landscape) flag above
1 = Use default paper orientation (landscape for chart sheets, portrait otherwise)
Bit 7: 0 = Automatic page numbers; 1 = Use start page number above
The following flags are valid for BIFF8 only:
Bit 9: 0 = Print notes as displayed; 1 = Print notes at end of sheet
Bit 11-10: 00 = Print errors as displayed; 1 = Do not print errors
2 = Print errors as “--”; 3 = Print errors as “#N/A” }
Result := 0;
if poPrintPagesByRows in FWorksheet.PageLayout.Options then
Result := Result or $0001;
if FWorksheet.PageLayout.Orientation = spoPortrait then
Result := Result or $0002;
if poMonochrome in FWorksheet.PageLayout.Options then
Result := Result or $0008;
if poDraftQuality in FWorksheet.PageLayout.Options then
Result := Result or $0010;
if poPrintCellComments in FWorksheet.PageLayout.Options then
Result := Result or $0020;
if poDefaultOrientation in FWorksheet.PageLayout.Options then
Result := Result or $0040;
if poUseStartPageNumber in FWorksheet.PageLayout.Options then
Result := Result or $0080;
end;
{@@ ----------------------------------------------------------------------------
Writes the BIFF record header consisting of the record ID and the size of
data to be written immediately afterwards.
@ -1970,6 +2298,21 @@ begin
AStream.WriteBuffer(rec, SizeOf(rec));
end;
{@@ ----------------------------------------------------------------------------
Write the bottom margin of the printed page (in inches)
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.WriteBottomMargin(AStream: TStream);
var
dbl: double;
begin
{ BIFF record header }
WriteBIFFHeader(AStream, INT_EXCEL_ID_BOTTOMMARGIN, SizeOf(Double));
{ Page margin value, written in inches }
dbl := mmToIn(FWorksheet.PageLayout.BottomMargin);
AStream.WriteBuffer(dbl, SizeOf(dbl));
end;
{@@ ----------------------------------------------------------------------------
Writes the code page identifier defined by the workbook to the stream.
BIFF2 has to be overridden because is uses cp1252, but has a different
@ -2192,6 +2535,21 @@ begin
AStream.WriteBuffer(rec, SizeOf(rec));
end;
{@@ ----------------------------------------------------------------------------
Write the left margin of the printed page (in inches)
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.WriteLeftMargin(AStream: TStream);
var
dbl: double;
begin
{ BIFF record header }
WriteBIFFHeader(AStream, INT_EXCEL_ID_LEFTMARGIN, SizeOf(Double));
{ Page margin value, written in inches }
dbl := mmToIn(FWorksheet.PageLayout.LeftMargin);
AStream.WriteBuffer(dbl, SizeOf(dbl));
end;
{@@ ----------------------------------------------------------------------------
Writes a BIFF number format record defined in the specified format string
(in Excel dialect).
@ -2245,6 +2603,22 @@ begin
SetLength(formula, 0);
end;
{@@ ----------------------------------------------------------------------------
Writes an Excel HCENTER record which determines whether the page is to be
centered horizontally for printing
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.WriteHCenter(AStream: TStream);
var
w: Word;
begin
{ BIFF record header }
WriteBIFFHeader(AStream, INT_EXCEL_ID_HCENTER, SizeOf(w));
{ Data }
if poHorCentered in FWorksheet.PageLayout.Options then w := 1 else w := 0;
AStream.WriteWord(WordToLE(w));
end;
{@@ ----------------------------------------------------------------------------
Writes a 64-bit floating point NUMBER record.
Valid for BIFF5 and BIFF8 (BIFF2 has a different record structure).
@ -2304,30 +2678,64 @@ end;
{@@ ----------------------------------------------------------------------------
Writes a PAGESETUP record containing information on printing
Valid for BIFF5-8
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.WritePageSetup(AStream: TStream);
var
dbl: Double;
i: Integer;
w: Word;
begin
{ BIFF record header }
WriteBIFFHeader(AStream, INT_EXCEL_ID_PAGESETUP, 9*2 + 2*8);
{ Paper size }
AStream.WriteWord(WordToLE(0)); // 1 = Letter, 9 = A4
w := 0;
for i:=0 to High(PAPER_SIZES) do
if (SameValue(PAPER_SIZES[i,0], FWorksheet.PageLayout.PageHeight) and
SameValue(PAPER_SIZES[i,1], FWorksheet.PageLayout.PageWidth))
or (SameValue(PAPER_SIZES[i,1], FWorksheet.PageLayout.PageHeight) and
SameValue(PAPER_SIZES[i,0], FWorksheet.PageLayout.PageWidth))
then begin
w := i;
break;
end;
AStream.WriteWord(WordToLE(w));
{ Scaling factor in percent }
AStream.WriteWord(WordToLE(100)); // 100 %
w := Round(FWorksheet.PageLayout.ScalingFactor);
AStream.WriteWord(WordToLE(w));
{ Start page number }
AStream.WriteWord(WordToLE(1)); // starting at page 1
w := FWorksheet.PageLayout.StartPageNumber;
AStream.WriteWord(WordToLE(w));
{ Fit worksheet width to this number of pages, 0 = use as many as needed }
AStream.WriteWord(WordToLE(0));
w := FWorksheet.PageLayout.FitWidthToPages;
AStream.WriteWord(WordToLE(w));
{ Fit worksheet height to this number of pages, 0 = use as many as needed }
AStream.WriteWord(WordToLE(0));
w := FWorksheet.PageLayout.FitHeightToPages;
AStream.WriteWord(WordToLE(w));
AStream.WriteWord(WordToLE(0));
{ Options:
Bit 0: 0 = Print pages in columns; 1 = Print pages in rows
Bit 1: 0 = Landscape; 1 = Portrait
Bit 2: 1 = Paper size, scaling factor, paper orientation (portrait/landscape),
print resolution and number of copies are not initialised
Bit 3: 0 = Print coloured; 1 = Print black and white
Bit 4: 0 = Default print quality; 1 = Draft quality
Bit 5: 0 = Do not print cell notes; 1 = Print cell notes
Bit 6: 0 = Use paper orientation (portrait/landscape) flag above
1 = Use default paper orientation (landscape for chart sheets, portrait otherwise)
Bit 7: 0 = Automatic page numbers; 1 = Use start page number above
The following flags are valid for BIFF8 only:
Bit 9: 0 = Print notes as displayed; 1 = Print notes at end of sheet
Bit 11-10: 00 = Print errors as displayed; 1 = Do not print errors
2 = Print errors as “--”; 3 = Print errors as “#N/A” }
w := GetPrintOptions;
AStream.WriteWord(WordToLE(w));
{ Print resolution in dpi }
AStream.WriteWord(WordToLE(600));
@ -2336,13 +2744,16 @@ begin
AStream.WriteWord(WordToLE(600));
{ Header margin }
dbl := 0.5;
dbl := mmToIn(FWorksheet.PageLayout.HeaderMargin);
AStream.WriteBuffer(dbl, SizeOf(dbl));
{ Footer margin }
dbl := mmToIn(FWorksheet.PageLayout.FooterMargin);
AStream.WriteBuffer(dbl, SizeOf(dbl));
{ Number of copies to print }
AStream.WriteWord(WordToLE(1)); // 1 copy
w := FWorksheet.PageLayout.Copies;
AStream.WriteWord(WordToLE(w));
end;
{@@ ----------------------------------------------------------------------------
@ -2418,6 +2829,36 @@ begin
{ Not used (BIFF5-BIFF8 only, not written in BIFF2-BIFF4 }
end;
{@@ ----------------------------------------------------------------------------
Writes out whether grid lines are printed or not
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.WritePrintGridLines(AStream: TStream);
var
w: Word;
begin
{ Biff record header }
WriteBIFFHeader(AStream, INT_EXCEL_ID_PRINTGRID, SizeOf(w));
{ Data }
if poPrintGridLines in FWorksheet.PageLayout.Options then w := 1 else w := 0;
AStream.WriteWord(WordToLE(w));
end;
{@@ ----------------------------------------------------------------------------
Writes out whether column and row headers are printed or not
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.WritePrintHeaders(AStream: TStream);
var
w: Word;
begin
{ Biff record header }
WriteBIFFHeader(AStream, INT_EXCEL_ID_PRINTHEADERS, SizeOf(w));
{ Data }
if poPrintHeaders in FWorksheet.PageLayout.Options then w := 1 else w := 0;
AStream.WriteWord(WordToLE(w));
end;
{@@ ----------------------------------------------------------------------------
Writes the address of a cell as used in an RPN formula and returns the
count of bytes written.
@ -2426,7 +2867,7 @@ end;
function TsSpreadBIFFWriter.WriteRPNCellAddress(AStream: TStream;
ARow, ACol: Cardinal; AFlags: TsRelFlags): Word;
var
r: Cardinal; // row index containing encoded relativ/absolute address info
r: Cardinal; // row index containing encoded relative/absolute address info
begin
// Encoded row address
r := ARow and MASK_EXCEL_ROW;
@ -2813,6 +3254,22 @@ begin
AStream.WriteWord(WordToLE(ASize));
end;
{@@ ----------------------------------------------------------------------------
Writes the right margin of the printed page (in inches)
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.WriteRightMargin(AStream: TStream);
var
dbl: double;
begin
{ BIFF record header }
WriteBIFFHeader(AStream, INT_EXCEL_ID_RIGHTMARGIN, SizeOf(Double));
{ Page margin value, written in inches }
dbl := mmToIn(FWorksheet.PageLayout.RightMargin);
AStream.WriteBuffer(dbl, SizeOf(dbl));
end;
{@@ ----------------------------------------------------------------------------
Writes an Excel 3-8 ROW record
Valid for BIFF3-BIFF8
@ -3106,6 +3563,38 @@ begin
Unused(AStream, AString);
end;
{@@ ----------------------------------------------------------------------------
Write the top margin of the printed page (in inches)
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.WriteTopMargin(AStream: TStream);
var
dbl: double;
begin
{ BIFF record header }
WriteBIFFHeader(AStream, INT_EXCEL_ID_TOPMARGIN, SizeOf(Double));
{ Page margin value, written in inches }
dbl := mmToIn(FWorksheet.PageLayout.TopMargin);
AStream.WriteBuffer(dbl, SizeOf(dbl));
end;
{@@ ----------------------------------------------------------------------------
Writes an Excel VCENTER record which determines whether the page is to be
centered vertically for printing
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.WriteVCenter(AStream: TStream);
var
w: Word;
begin
{ BIFF record header }
WriteBIFFHeader(AStream, INT_EXCEL_ID_VCENTER, SizeOf(w));
{ Data }
if poVertCentered in FWorksheet.PageLayout.Options then w := 1 else w := 0;
AStream.WriteWord(WordToLE(w));
end;
procedure TsSpreadBIFFWriter.WriteVirtualCells(AStream: TStream);
var
r,c: Cardinal;

View File

@ -79,6 +79,7 @@ type
procedure ReadNumFormats(ANode: TDOMNode);
procedure ReadPageMargins(ANode: TDOMNode; AWorksheet: TsWorksheet);
procedure ReadPalette(ANode: TDOMNode);
procedure ReadPrintOptions(ANode: TDOMNode; AWorksheet: TsWorksheet);
procedure ReadRowHeight(ANode: TDOMNode; AWorksheet: TsWorksheet);
procedure ReadSharedStrings(ANode: TDOMNode);
procedure ReadSheetFormatPr(ANode: TDOMNode; AWorksheet: TsWorksheet);
@ -131,7 +132,10 @@ type
procedure WriteNumFormatList(AStream: TStream);
procedure WritePalette(AStream: TStream);
procedure WritePageMargins(AStream: TStream; AWorksheet: TsWorksheet);
procedure WritePageSetup(AStream: TStream; AWorksheet: TsWorksheet);
procedure WritePrintOptions(AStream: TStream; AWorksheet: TsWorksheet);
procedure WriteSheetData(AStream: TStream; AWorksheet: TsWorksheet);
procedure WriteSheetPr(AStream: TStream; AWorksheet: TsWorksheet);
procedure WriteSheetViews(AStream: TStream; AWorksheet: TsWorksheet);
procedure WriteStyleList(AStream: TStream; ANodeName: String);
procedure WriteVmlDrawings(AWorksheet: TsWorksheet);
@ -1307,38 +1311,33 @@ procedure TsSpreadOOXMLReader.ReadPageMargins(ANode: TDOMNode;
AWorksheet: TsWorksheet);
var
s: String;
layout: TsPageLayout;
begin
if ANode = nil then
if (ANode = nil) or (AWorksheet = nil) then // just to make sure...
exit;
layout := AWorksheet.PageLayout;
s := GetAttrValue(ANode, 'left');
if s <> '' then
layout.LeftMargin := HtmlLengthStrToPts(s);
AWorksheet.PageLayout.LeftMargin := PtsToMM(HtmlLengthStrToPts(s, 'in'));
s := GetAttrValue(ANode, 'right');
if s <> '' then
layout.RightMargin := HtmlLengthStrToPts(s);
AWorksheet.PageLayout.RightMargin := PtsToMM(HtmlLengthStrToPts(s, 'in'));
s := GetAttrValue(ANode, 'top');
if s <> '' then
layout.TopMargin := HtmlLengthStrToPts(s);
AWorksheet.PageLayout.TopMargin := PtsToMM(HtmlLengthStrToPts(s, 'in'));
s := GetAttrValue(ANode, 'bottom');
if s <> '' then
layout.BottomMargin := HtmlLengthStrToPts(s);
AWorksheet.PageLayout.BottomMargin := PtsToMM(HtmlLengthStrToPts(s, 'in'));
s := GetAttrValue(ANode, 'header');
if s <> '' then
layout.HeaderDistance := HtmlLengthStrToPts(s);
AWorksheet.PageLayout.HeaderMargin := PtsToMM(HtmlLengthStrToPts(s, 'in'));
s := GetAttrValue(ANode, 'footer');
if s <> '' then
layout.FooterDistance := HtmlLengthStrToPts(s);
AWorksheet.PageLayout := layout;
AWorksheet.PageLayout.FooterMargin := PtsToMM(HtmlLengthStrToPts(s, 'in'));
end;
procedure TsSpreadOOXMLReader.ReadPalette(ANode: TDOMNode);
@ -1385,6 +1384,22 @@ begin
end;
end;
procedure TsSpreadOOXMLReader.ReadPrintOptions(ANode: TDOMNode;
AWorksheet: TsWorksheet);
var
s: String;
begin
if ANode = nil then
exit;
s := GetAttrValue(ANode, 'headings');
if (s = '1') then
Include(AWorksheet.PageLayout.Options, poPrintHeaders);
s := GetAttrValue(ANode, 'gridLines');
if (s = '1') then
Include(AWorksheet.PageLayout.Options, poPrintGridLines);
end;
procedure TsSpreadOOXMLReader.ReadRowHeight(ANode: TDOMNode; AWorksheet: TsWorksheet);
var
s: String;
@ -1709,6 +1724,7 @@ begin
ReadWorksheet(Doc.DocumentElement.FindNode('sheetData'), FWorksheet);
ReadMergedCells(Doc.DocumentElement.FindNode('mergeCells'), FWorksheet);
ReadHyperlinks(Doc.DocumentElement.FindNode('hyperlinks'));
ReadPrintOptions(Doc.DocumentElement.FindNode('printOptions'), FWorksheet);
ReadPageMargins(Doc.DocumentElement.FindNode('pageMargins'), FWorksheet);
FreeAndNil(Doc);
@ -2309,12 +2325,87 @@ begin
with AWorksheet.PageLayout do
AppendToStream(AStream, Format(
'<pageMargins left="%g" right="%g" top="%g" bottom="%g" header="%g" footer="%g" />', [
PtsToIn(LeftMargin), PtsToIn(RightMargin), PtsToIn(TopMargin), PtsToIn(BottomMargin),
PtsToIn(HeaderDistance), PtsToIn(FooterDistance) ],
mmToIn(LeftMargin), mmToIn(RightMargin), mmToIn(TopMargin), mmToIn(BottomMargin),
mmToIn(HeaderMargin), mmToIn(FooterMargin) ],
FPointSeparatorSettings
));
end;
procedure TsSpreadOOXMLWriter.WritePageSetup(AStream: TStream;
AWorksheet: TsWorksheet);
var
s: String;
i: Integer;
begin
s := '';
// Paper size
for i:=0 to High(PAPER_SIZES) do
if (SameValue(PAPER_SIZES[i,0], AWorksheet.PageLayout.PageHeight) and
SameValue(PAPER_SIZES[i,1], AWorksheet.PageLayout.PageWidth))
or (SameValue(PAPER_SIZES[i,1], AWorksheet.PageLayout.PageHeight) and
SameValue(PAPER_SIZES[i,0], AWorksheet.PageLayout.PageWidth))
then begin
s := Format('%s paperSize="%d"', [s, i]);
break;
end;
// Scaling factor
if AWorksheet.PageLayout.ScalingFactor <> 100 then
s := Format('%s scale="%.0f" fitToHeight="0" fitToWidth="0"', [
s, AWorksheet.PageLayout.ScalingFactor
], FPointSeparatorSettings);
// Fit width pages
if AWorksheet.PageLayout.FitWidthToPages > 0 then
s := Format('%s fitToWidth="%d"', [s, AWorksheet.PageLayout.FitWidthToPages]);
// Fit height pages
if AWorksheet.PageLayout.FitHeightToPages > 0 then
s := Format('%s fitToHeight="%d"', [s, AWorksheet.PageLayout.FitHeightToPages]);
// Orientation
s := Format('%s orientation="%s"', [
s, IfThen(AWorksheet.PageLayout.Orientation = spoPortrait, 'portrait', 'landscape')
]);
// First page number
if poUseStartPageNumber in FWorksheet.PageLayout.Options then
s := Format('%s useFirstPageNumber="%d"', [s, AWorksheet.PageLayout.StartPageNumber]);
// Print order
if poPrintPagesByRows in AWorksheet.PageLayout.Options then
s := s + ' pageOrder="overThenDown"';
// Monochrome
if poMonochrome in AWorksheet.PageLayout.Options then
s := s + ' blackAndWhite="1"';
// Quality
if poDraftQuality in AWOrksheet.PageLayout.Options then
s := s + ' draft="1"';
if s <> '' then
AppendToStream(AStream,
'<pageSetup' + s + ' />');
end;
procedure TsSpreadOOXMLWriter.WritePrintOptions(AStream: TStream;
AWorksheet: TsWorksheet);
var
s: String;
begin
s := '';
if poPrintGridLines in AWorksheet.PageLayout.Options then
s := s + ' gridLines="1"';
if poPrintHeaders in AWorksheet.PageLayout.Options then
s := s + ' headings="1"';
if s <> '' then
AppendToStream(AStream,
'<printOptions' + s + ' />');
end;
procedure TsSpreadOOXMLWriter.WriteSheetData(AStream: TStream;
AWorksheet: TsWorksheet);
var
@ -2424,6 +2515,21 @@ begin
'</sheetData>');
end;
procedure TsSpreadOOXMLWriter.WriteSheetPr(AStream: TStream; AWorksheet: TsWorksheet);
var
s: String;
begin
s := '';
if (AWorksheet.PageLayout.FitWidthToPages > 0) or
(AWorksheet.PageLayout.FitHeightToPages > 0) then
s := s + ' fitToPage="1"';
if s <> '' then s := '<pageSetUpPr' + s + ' />';
if s <> '' then
AppendToStream(AStream,
'<sheetPr>' + s + '</sheetPr>');
end;
procedure TsSpreadOOXMLWriter.WriteSheetViews(AStream: TStream;
AWorksheet: TsWorksheet);
var
@ -2936,13 +3042,16 @@ begin
AppendToStream(FSSheets[FCurSheetNum], Format(
'<worksheet xmlns="%s" xmlns:r="%s">', [SCHEMAS_SPREADML, SCHEMAS_DOC_RELS]));
WriteSheetPr(FSSheets[FCurSheetNum], AWorksheet);
WriteDimension(FSSheets[FCurSheetNum], AWorksheet);
WriteSheetViews(FSSheets[FCurSheetNum], AWorksheet);
WriteCols(FSSheets[FCurSheetNum], AWorksheet);
WriteSheetData(FSSheets[FCurSheetNum], AWorksheet);
WriteHyperlinks(FSSheets[FCurSheetNum], AWorksheet);
WriteMergedCells(FSSheets[FCurSheetNum], AWorksheet);
WritePrintOptions(FSSheets[FCurSheetNum], AWorksheet);
WritePageMargins(FSSheets[FCurSheetNum], AWorksheet);
WritePageSetup(FSSheets[FCurSheetNum], AWorksheet);
// Footer
if AWorksheet.Comments.Count > 0 then