fpspreadsheet: Add row height type to row record to distinguish custom from auto-calculated row heights. Add grid property AutoCalcRowHeights to use pre-calculated row height instead of always automatically recalculating row heights.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5233 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2016-10-01 12:20:18 +00:00
parent 5a55a015c0
commit 863be778f9
12 changed files with 289 additions and 176 deletions

View File

@ -27,6 +27,7 @@ object MainForm: TMainForm
Height = 493
Top = 23
Width = 690
AutoCalcRowHeights = False
FrozenCols = 0
FrozenRows = 0
ReadFormulas = True

View File

@ -1393,8 +1393,13 @@ var
begin
h := FWorksheet.ReadDefaultRowHeight(suPoints);
row := FWorksheet.FindRow(ARowIndex);
if row <> nil then
h := FWorkbook.ConvertUnits(row^.Height, FWorkbook.Units, suPoints);
if row <> nil then begin
h := abs(FWorkbook.ConvertUnits(row^.Height, FWorkbook.Units, suPoints));
if row^.RowHeightType = rhtDefault then begin
Result := '';
exit;
end;
end;
Result := Format(' height="%.1fpt"', [h], FPointSeparatorSettings);
end;

View File

@ -359,8 +359,8 @@ type
TRowStyleData = class
public
Name: String;
RowHeight: Double; // in workbook units
AutoRowHeight: Boolean;
RowHeight: Double; // in workbook units
RowHeightType: TsRowHeightType;
end;
{ PageLayout items stored in PageLayoutList }
@ -3241,7 +3241,7 @@ var
rowStyleIndex: Integer;
rowStyle: TRowStyleData;
rowHeight: Double;
autoRowHeight: Boolean;
rowHeightType: TsRowHeightType;
col: Integer;
cellNode: TDOMNode;
nodeName: String;
@ -3260,9 +3260,11 @@ var
begin
rowStyle := TRowStyleData(FRowStyleList[rowStyleIndex]);
rowHeight := rowStyle.RowHeight; // in Workbook units (see ReadRowStyles)
autoRowHeight := rowStyle.AutoRowHeight;
end else
autoRowHeight := true;
rowHeightType := rowStyle.RowHeightType;
end else begin
rowHeight := FWorksheet.DefaultRowHeight;
rowHeightTYpe := rhtDefault;
end;
col := 0;
@ -3362,12 +3364,11 @@ var
// Transfer non-default row heights to sheet's rows
// This first "if" is a workaround for a bug of LO/OO whichs extends imported
// xlsx files with blank rows up to their specification limit.
// React some rows earlier because the added row range is sometimes split
// Process some rows earlier because the added row range is sometimes split
// into two parts.
if row + rowsRepeated < LongInt(FLimitations.MaxRowCount) - 10 then
if not autoRowHeight then
for i:=1 to rowsRepeated do
FWorksheet.WriteRowHeight(row + i - 1, rowHeight, FWorkbook.Units);
for i:=1 to rowsRepeated do
FWorksheet.WriteRowHeight(row + i - 1, rowHeight, FWorkbook.Units, rowHeightType);
row := row + rowsRepeated;
end;
@ -3412,14 +3413,14 @@ var
styleName, nodename: String;
styleChildNode: TDOMNode;
rowHeight: Double;
auto: Boolean;
s: String;
rowStyle: TRowStyleData;
rowHeightType: TsRowHeightType;
begin
styleName := GetAttrValue(AStyleNode, 'style:name');
styleChildNode := AStyleNode.FirstChild;
rowHeight := -1;
auto := false;
rowHeight := 0;
rowHeightType := rhtCustom;
while Assigned(styleChildNode) do
begin
@ -3432,7 +3433,7 @@ begin
// convert to workbook units
s := GetAttrValue(styleChildNode, 'style:use-optimal-row-height');
if s = 'true' then
auto := true;
rowHeightType := rhtAuto;
end;
styleChildNode := styleChildNode.NextSibling;
end;
@ -3440,7 +3441,7 @@ begin
rowStyle := TRowStyleData.Create;
rowStyle.Name := styleName;
rowStyle.RowHeight := rowHeight;
rowStyle.AutoRowHeight := auto;
rowStyle.RowHeightType := rowHeightType;
FRowStyleList.Add(rowStyle);
end;
@ -4331,7 +4332,7 @@ begin
rowStyle := TRowStyleData.Create;
rowStyle.Name := 'ro1';
rowStyle.RowHeight := FWorkbook.ConvertUnits(15, suPoints, FWorkbook.Units);
rowStyle.AutoRowHeight := true;
rowStyle.RowHeightType := rhtAuto;
FRowStyleList.Add(rowStyle);
for i:=0 to Workbook.GetWorksheetCount-1 do
@ -4346,9 +4347,9 @@ begin
// Look for this height in the current RowStyleList
found := false;
for j:=0 to FRowStyleList.Count-1 do
if SameValue(TRowStyleData(FRowStyleList[j]).RowHeight, h, ROWHEIGHT_EPS) and
(not TRowStyleData(FRowStyleList[j]).AutoRowHeight) then
begin
if SameValue(TRowStyleData(FRowStyleList[j]).RowHeight, h, ROWHEIGHT_EPS)
and (TRowStyleData(FRowStyleList[j]).RowHeightType = row^.RowHeightType)
then begin
found := true;
break;
end;
@ -4358,22 +4359,12 @@ begin
rowStyle := TRowStyleData.Create;
rowStyle.Name := Format('ro%d', [FRowStyleList.Count+1]);
rowStyle.RowHeight := h;
rowStyle.AutoRowHeight := false;
rowStyle.RowHeightType := row^.RowHeightType;
FRowStyleList.Add(rowStyle);
end;
end;
end;
end;
(*
{ fpspreadsheet's row heights are measured as line count of the default font.
Using the default font size (which is in points) we convert the line count
to points and then to millimeters as needed by ods. }
multiplier := Workbook.GetDefaultFontSize;;
for i:=0 to FRowStyleList.Count-1 do
begin
h := (TRowStyleData(FRowStyleList[i]).RowHeight + ROW_HEIGHT_CORRECTION) * multiplier;
TRowStyleData(FRowStyleList[i]).RowHeight := PtsToMM(h);
end; *)
end;
{ Is called before zipping the individual file parts. Rewinds the streams. }
@ -5297,8 +5288,9 @@ begin
for k := 0 to FRowStyleList.Count-1 do begin
rowStyleData := TRowStyleData(FRowStyleList[k]);
// Compare row heights, but be aware of rounding errors
if SameValue(rowStyleData.RowHeight, h, ROWHEIGHT_EPS) then
begin
if SameValue(rowStyleData.RowHeight, h, ROWHEIGHT_EPS) and
(rowstyleData.RowHeightType = row^.RowHeightType)
then begin
styleName := rowStyleData.Name;
break;
end;
@ -5443,14 +5435,16 @@ begin
AppendToStream(AStream, Format(
'<style:style style:name="%s" style:family="table-row">', [rowStyle.Name]));
// Column width
// Row height
AppendToStream(AStream, Format(
'<style:table-row-properties style:row-height="%.3fmm" ',
[FWorkbook.ConvertUnits(rowStyle.RowHeight, FWorkbook.Units, suMillimeters)],
FPointSeparatorSettings));
if rowStyle.AutoRowHeight then
AppendToStream(AStream, 'style:use-optimal-row-height="true" ');
AppendToStream(AStream, 'fo:break-before="auto"/>');
AppendToStream(AStream, Format(
'style:use-optimal-row-height="%s" ', [FALSE_TRUE[rowstyle.RowHeightType <> rhtCustom]]));
// row height < 0 means: automatic row height
AppendToStream(AStream,
'fo:break-before="auto"/>');
// End
AppendToStream(AStream,

View File

@ -41,26 +41,15 @@ type
TsWorksheet = class;
TsWorkbook = class;
{**
Type: TRow -- record containing information about a spreadsheet row
Members:
- Row -- The index of the row (beginning with 0)
- Height -- The height of the row (expressed as line count of the default font)
Notes:
- Only rows with heights that cannot be derived from the font height have
a row record.
}
{@@ The record TRow contains information about a spreadsheet row:
@param Row The index of the row (beginning with 0)
@param Height The height of the row (expressed in the units defined in the workbook)
Only rows with heights that cannot be derived from the font height have a
row record. }
@param Height The height of the row (expressed in the units defined by
the workbook)
@param RowHeightType Specifies default, automatic or custom row height }
TRow = record
Row: Cardinal;
Height: Single;
RowHeightType: TsRowHeightType;
end;
{@@ Pointer to a TRow record }
@ -447,6 +436,7 @@ type
function GetRow(ARow: Cardinal): PRow;
function GetRowHeight(ARow: Cardinal; AUnits: TsSizeUnits): Single; overload;
function GetRowHeight(ARow: Cardinal): Single; overload; deprecated 'Use version with parameter AUnits.';
function GetRowHeightType(ARow: Cardinal): TsRowHeightType;
function GetCol(ACol: Cardinal): PCol;
function GetColWidth(ACol: Cardinal; AUnits: TsSizeUnits): Single; overload;
function GetColWidth(ACol: Cardinal): Single; overload; deprecated 'Use version with parameter AUnits.';
@ -463,8 +453,10 @@ type
procedure WriteDefaultColWidth(AValue: Single; AUnits: TsSizeUnits);
procedure WriteDefaultRowHeight(AValue: Single; AUnits: TsSizeUnits);
procedure WriteRowInfo(ARow: Cardinal; AData: TRow);
procedure WriteRowHeight(ARow: Cardinal; AHeight: Single; AUnits: TsSizeUnits); overload;
procedure WriteRowHeight(ARow: Cardinal; AHeight: Single); overload; deprecated 'Use version with parameter AUnits';
procedure WriteRowHeight(ARow: Cardinal; AHeight: Single; AUnits: TsSizeUnits;
ARowHeightType: TsRowHeightType = rhtCustom); overload;
procedure WriteRowHeight(ARow: Cardinal; AHeight: Single;
ARowHeightType: TsRowHeightType = rhtCustom); overload; deprecated 'Use version with parameter AUnits';
procedure WriteColInfo(ACol: Cardinal; AData: TCol);
procedure WriteColWidth(ACol: Cardinal; AWidth: Single; AUnits: TsSizeUnits); overload;
procedure WriteColWidth(ACol: Cardinal; AWidth: Single); overload; deprecated 'Use version with parameter AUnits';
@ -6275,8 +6267,8 @@ begin
Result := 0;
for cell in Cells.GetRowEnumerator(ARow) do
Result := Max(Result, ReadCellFont(cell).Size);
Result := FWorkbook.ConvertUnits(Result, suPoints, FWorkbook.Units);
// FixMe: This is not correct if text is rotated or wrapped
Result := FWorkbook.ConvertUnits(Result, suPoints, FWorkbook.Units);
end;
function TsWorksheet.CalcRowHeight(ARow: Cardinal): Single;
@ -6461,6 +6453,9 @@ end;
@param ARow Index of the row considered
@param AUnits Units for the row height.
@return Height of the row
Note that the row height value can be negative to indicate that this
is an auto-calculated value (i.e. the value can change for example
when the font size changes).
-------------------------------------------------------------------------------}
function TsWorksheet.GetRowHeight(ARow: Cardinal; AUnits: TsSizeUnits): Single;
var
@ -6484,6 +6479,28 @@ begin
Result := GetRowHeight(ARow, suLines);
end;
{@@ ----------------------------------------------------------------------------
Returns the type of rowheight of a specific row.
If there is no row record then rhtDefault is returned.
@param ARow Index of the row considered
@param AUnits Units for the row height.
@return Height of the row
Note that the row height value can be negative to indicate that this
is an auto-calculated value (i.e. the value can change for example
when the font size changes).
-------------------------------------------------------------------------------}
function TsWorksheet.GetRowHeightType(ARow: Cardinal): TsRowHeightType;
var
lRow: PRow;
begin
lRow := FindRow(ARow);
if lRow = nil then
Result := rhtDefault
else
Result := lRow^.RowHeightType;
end;
{@@ ----------------------------------------------------------------------------
Deletes the column at the index specified. Cells with greader column indexes
are moved one column to the left. Merged cell blocks and cell references in
@ -6884,6 +6901,9 @@ end;
@param ARow Index of the row record which will be created or modified
@param AData Data to be written. Expected to be already in the units
defined for the workbook
Note that the row height value can be negative to indicate
that this is an auto-calculated value (i.e. the value can
change for example when the font size changes).
-------------------------------------------------------------------------------}
procedure TsWorksheet.WriteRowInfo(ARow: Cardinal; AData: TRow);
var
@ -6900,9 +6920,11 @@ end;
@param ARow Index of the row to be considered
@param AHeight Row height to be assigned to the row.
@param AUnits Units measuring the row height.
@param ARowHeightType Specifies whether the row height is a default,
automatic or custom row height.
-------------------------------------------------------------------------------}
procedure TsWorksheet.WriteRowHeight(ARow: Cardinal; AHeight: Single;
AUnits: TsSizeUnits);
AUnits: TsSizeUnits; ARowHeightType: TsRowHeightType = rhtCustom);
var
AElement: PRow;
begin
@ -6910,6 +6932,7 @@ begin
exit;
AElement := GetRow(ARow);
AElement^.Height := FWorkbook.ConvertUnits(AHeight, AUnits, FWorkbook.FUnits);
AElement^.RowHeightType := ARowHeightType;
end;
{@@ ----------------------------------------------------------------------------
@ -6919,9 +6942,10 @@ end;
Note that this method is deprecated and will be removed.
Use the variant in which the units of the new height can be specified.
-------------------------------------------------------------------------------}
procedure TsWorksheet.WriteRowHeight(ARow: Cardinal; AHeight: Single);
procedure TsWorksheet.WriteRowHeight(ARow: Cardinal; AHeight: Single;
ARowHeightType: TsRowHeightType = rhtCustom);
begin
WriteRowHeight(ARow, AHeight, suLines);
WriteRowHeight(ARow, AHeight, suLines, ARowHeightType);
end;
{@@ ----------------------------------------------------------------------------

View File

@ -12,11 +12,6 @@
-------------------------------------------------------------------------------}
unit fpspreadsheetgrid;
{ Activate this define if the worksheet contains varying row heights and jumps
to specific rows do not meet the requested row. There is a speed penalty in
case of large worksheets. }
{.$DEFINE CALC_ALL_ROWHEIGHTS}
{$mode objfpc}{$H+}
{$I fps.inc}
@ -77,6 +72,7 @@ type
FDrawingCell: PCell;
FTextOverflowing: Boolean;
FAutoExpand: TsAutoExpandModes;
FAutoCalcRowHeights: Boolean;
FEnhEditMode: Boolean;
FSelPen: TsSelPen;
FHyperlinkTimer: TTimer;
@ -90,6 +86,7 @@ type
function CalcAutoRowHeight(ARow: Integer): Integer;
function CalcColWidthFromSheet(AWidth: Single): Integer;
function CalcRowHeightFromSheet(AHeight: Single): Integer;
function CalcRowHeightToSheet(AHeight: Integer): Single;
procedure ChangedCellHandler(ASender: TObject; ARow, ACol: Cardinal);
procedure ChangedFontHandler(ASender: TObject; ARow, ACol: Cardinal);
procedure FixNeighborCellBorders(ACell: PCell);
@ -246,14 +243,17 @@ type
procedure SetEditText(ACol, ARow: Longint; const AValue: string); override;
procedure Setup;
procedure Sort(AColSorting: Boolean; AIndex, AIndxFrom, AIndxTo:Integer); override;
procedure TopLeftChanged; override;
function TrimToCell(ACell: PCell): String;
procedure UpdateColWidths(AStartIndex: Integer = 0);
procedure UpdateRowHeight(ARow: Integer);
procedure UpdateRowHeight(ARow: Integer; EnforceCalcRowHeight: Boolean = false);
procedure UpdateRowHeights;
{@@ Automatically recalculate formulas whenever a cell value changes. }
property AutoCalc: Boolean read FAutoCalc write SetAutoCalc default false;
{@@ Automatically recalculate row heights after loading a file. Gets rid of
possibly incorrect row heights stored by the writing application. But:
slow in case of large files. }
property AutoCalcRowHeights: Boolean read FAutoCalcRowHeights write FAutoCalcRowHeights default true;
{@@ Automatically expand grid dimensions }
property AutoExpand: TsAutoExpandModes read FAutoExpand write FAutoExpand;
{@@ Displays column and row headers in the fixed col/row style of the grid.
@ -502,6 +502,10 @@ type
// inherited from TsCustomWorksheetGrid
{@@ Automatically recalculates the worksheet if a cell value changes. }
property AutoCalc;
{@@ Automatically recalculate row heights after loading a file. Gets rid of
possibly incorrect row heights stored by the writing application. But:
slow in case of large files. }
property AutoCalcRowHeights;
{@@ Automatically expand grid dimensions }
property AutoExpand default [aeData, aeNavigation];
{@@ Displays column and row headers in the fixed col/row style of the grid.
@ -1012,6 +1016,7 @@ begin
FSelPen.JoinStyle := pjsMiter;
FSelPen.OnChange := @SelPenChangeHandler;
FAutoExpand := [aeData, aeNavigation];
FAutoCalcRowHeights := true;
FHyperlinkTimer := TTimer.Create(self);
FHyperlinkTimer.Interval := HYPERLINK_TIMER_INTERVAL;
FHyperlinkTimer.OnTimer := @HyperlinkTimerElapsed;
@ -1190,10 +1195,18 @@ function TsCustomWorksheetGrid.CalcRowHeightFromSheet(AHeight: Single): Integer;
var
h_pts: Single;
begin
h_pts := Workbook.ConvertUnits(AHeight, Workbook.Units, suPoints);;
h_pts := Workbook.ConvertUnits(abs(AHeight), Workbook.Units, suPoints);;
Result := PtsToPx(h_pts, Screen.PixelsPerInch); // + 4;
end;
function TsCustomWorksheetGrid.CalcRowHeightToSheet(AHeight: Integer): Single;
var
h_pts: Single;
begin
h_pts := PxToPts(AHeight, Screen.PixelsPerInch);
Result := Workbook.ConvertUnits(h_pts, suPoints, Workbook.Units);
end;
{@@ ----------------------------------------------------------------------------
Converts the column height given in screen pixels to the units used by the
worksheet.
@ -3609,10 +3622,9 @@ begin
if GetGridRow(Worksheet.GetLastRowIndex + 1) >= RowCount then
RowCount := RowCount + 1;
r := GetWorksheetRow(AGridRow);
// r := AGridRow - FHeaderCount;
Worksheet.InsertRow(r);
UpdateRowHeight(AGridRow);
UpdateRowHeight(AGridRow, true);
// UpdateRowHeights(AGridRow);
end;
@ -4007,7 +4019,9 @@ procedure TsCustomWorksheetGrid.ListenerNotification(AChangedItems: TsNotificati
AData: Pointer = nil);
var
grow, gcol: Integer;
srow: Cardinal;
cell: PCell;
lRow: PRow;
begin
Unused(AData);
@ -4079,9 +4093,12 @@ begin
// Row height (after font change).
if (lniRow in AChangedItems) and (Worksheet <> nil) then
begin
grow := GetGridRow({%H-}PtrInt(AData));
srow := {%H-}PtrInt(AData); // sheet row
grow := GetGridRow(srow); // grid row
AutoExpandToRow(grow, aeData);
RowHeights[grow] := CalcAutoRowHeight(grow);
lRow := Worksheet.FindRow(srow);
if (lRow = nil) or (lRow^.RowHeightType <> rhtCustom) then
UpdateRowHeight(grow, true);
end;
end;
@ -4573,21 +4590,6 @@ begin
);
end;
{@@ ----------------------------------------------------------------------------
Inherited method called whenever to grid is scrolled, i.e. the top/left cell
changes.
Is overridden to calculate the row heights of the currently visible grid
-------------------------------------------------------------------------------}
procedure TsCustomWorksheetGrid.TopLeftChanged;
begin
{$IFNDEF CALC_ALL_ROWHEIGHTS}
if FOldTopRow <> TopRow then
UpdateRowHeights;
FOldTopRow := TopRow;
{$ENDIF}
inherited;
end;
{@@ ----------------------------------------------------------------------------
Modifies the text that is show for cells which are too narrow to hold the
entire text. The method follows the behavior of Excel and Open/LibreOffice:
@ -4753,48 +4755,74 @@ begin
end;
end;
procedure TsCustomWorksheetGrid.UpdateRowHeight(ARow: Integer);
{@@ ----------------------------------------------------------------------------
Updates the height if the specified row in the grid by the value stored in the
Worksheet and multiplied by the current zoom factor. If the stored rowheight
type is rhtAuto (meaning: "row height is auto-calculated") and row height
calculation is enabled (if internal flag FAutoCalcRowHeight is true, or the
parameter EnforceCalcRowHeight is true9, then the current row height is
calculated by iterating over all cells in the row.
-------------------------------------------------------------------------------}
procedure TsCustomWorksheetGrid.UpdateRowHeight(ARow: Integer;
EnforceCalcRowHeight: Boolean = false);
var
lRow: PRow;
h: Integer;
h: Integer; // Row height, in pixels. Contains zoom factor.
begin
h := 0;
if Worksheet <> nil then
begin
lRow := Worksheet.FindRow(ARow - FHeaderCount);
if (lRow <> nil) then
h := round(CalcRowHeightFromSheet(lRow^.Height) * ZoomFactor)
else
h := CalcAutoRowHeight(ARow); // ZoomFactor has already been applied to font heights
end else
h := DefaultRowHeight; // Zoom factor is applied by getter function
if (lRow <> nil) then begin
case lRow^.RowHeightType of
rhtCustom:
h := round(CalcRowHeightFromSheet(lRow^.Height) * ZoomFactor);
rhtAuto, rhtDefault:
if FAutoCalcRowHeights or EnforceCalcRowHeight then begin
// Calculate current grid row height in pixels by iterating over all cells in row
h := CalcAutoRowHeight(ARow); // ZoomFactor already applied to font heights
if h = 0 then begin
h := DefaultRowHeight; // Zoom factor applied by getter function
lRow^.RowHeightType := rhtDefault;
end else
lRow^.RowHeightType := rhtAuto;
// Calculate the unzoomed row height in workbook units and store
// in row record
lRow^.Height := CalcRowHeightToSheet(round(h / ZoomFactor));
end else
// If autocalc mode is off we just take the row height from the row record
h := round(CalcRowHeightFromSheet(lRow^.Height) * ZoomFactor);
end; // case
end else
// No row record so far.
if FAutoCalcRowHeights or EnforceCalcRowHeight then begin
h := CalcAutoRowHeight(ARow);
if h <> DefaultRowHeight then begin
lRow := Worksheet.GetRow(ARow - FHeaderCount);
lRow^.Height := CalcRowHeightToSheet(round(h / ZoomFactor));
lRow^.RowHeightType := rhtAuto;
end;
end;
end;
if h = 0 then
h := DefaultRowHeight; // Zoom factor is applied by getter function
inc(FZoomLock); // We don't want to modify the sheet row heights here.
RowHeights[ARow] := h;
dec(FZoomLock);
end;
{@@ ----------------------------------------------------------------------------
Updates row heights by using the data from the TRow records or by auto-
calculating the row height from the max of the cell heights
Because there may be many rows only the visible rows are updated. Therefore,
this method is called whenever the grid is scrolled and the coordinates of
the top-left cell changes.
Updates grid row heights by using the data from the TRow records.
-------------------------------------------------------------------------------}
procedure TsCustomWorksheetGrid.UpdateRowHeights;
const
DELTA = 10;
var
r, r1, r2: Integer;
r: Integer;
begin
if FRowHeightLock > 0 then
exit;
{$IFDEF CALC_ALL_ROWHEIGHTS}
r1 := FHeaderCount;
r2 := RowCount-1;
{$ELSE}
r1 := Max(FHeaderCount, TopRow - DELTA);
r2 := Min(RowCount-1, TopRow + VisibleRowCount + DELTA);
{$ENDIF}
for r:=r1 to r2 do
for r:=FHeaderCount to RowCount-1 do
UpdateRowHeight(r);
end;

View File

@ -770,6 +770,14 @@ type
TsStreamParam = (spClipboard, spWindowsClipboardHTML);
TsStreamParams = set of TsStreamParam;
{@@ Types of row heights
rhtDefault - default row height
rhtAuto - automatically determined row height, depends on font size,
text rotation, rich-text parameters, word-wrap
rhtCustom - user-determined row height (dragging the row header borders in
the grid }
TsRowHeightType = (rhtDefault, rhtAuto, rhtCustom);
implementation

View File

@ -33,7 +33,7 @@ var
SollDateTimeFormatStrings: array[0..8] of String;
SollColWidths: array[0..1] of Single;
SollRowHeights: Array[0..2] of Single;
SollRowHeights: Array[0..3] of Single;
SollBorders: array[0..19] of TsCellBorders;
SollBorderLineStyles: array[0..6] of TsLineStyle;
SollBorderColors: array[0..5] of TsColor;
@ -156,7 +156,7 @@ type
implementation
uses
TypInfo, fpsPatches, fpsutils, fpsnumformat, fpspalette, fpscsv;
Math, TypInfo, fpsPatches, fpsutils, fpsnumformat, fpspalette, fpscsv;
const
FmtNumbersSheet = 'NumbersFormat'; //let's distinguish it from the regular numbers sheet
@ -276,6 +276,7 @@ begin
SollRowHeights[0] := 1; // Lines of default font
SollRowHeights[1] := 2;
SollRowHeights[2] := 4;
SollRowHeights[3] := -2; // an autocalculated row height
// Cell borders
SollBorders[0] := [];
@ -1181,6 +1182,7 @@ var
ActualRowHeight: Single;
Row: Integer;
TempFile: string; //write xls/xml to this file and read back from it
rht: TsRowHeightType;
begin
{// Not needed: use workbook.writetofile with overwrite=true
if fileexists(TempFile) then
@ -1190,8 +1192,12 @@ begin
MyWorkbook := TsWorkbook.Create;
try
MyWorkSheet:= MyWorkBook.AddWorksheet(RowHeightSheet);
for Row := Low(SollRowHeights) to High(SollRowHeights) do
MyWorksheet.WriteRowHeight(Row, SollRowHeights[Row], suLines);
for Row := Low(SollRowHeights) to High(SollRowHeights) do begin
if SollRowHeights[Row] < 0 then
rht := rhtAuto else
rht := rhtCustom;
MyWorksheet.WriteRowHeight(Row, abs(SollRowHeights[Row]), suLines, rht);
end;
TempFile:=NewTempFile;
MyWorkBook.WriteToFile(TempFile, AFormat, true);
finally
@ -1211,11 +1217,18 @@ begin
for Row := Low(SollRowHeights) to High(SollRowHeights) do
begin
ActualRowHeight := MyWorksheet.GetRowHeight(Row, suLines);
rht := MyWorksheet.GetRowHeightType(Row);
// Take care of rounding errors - due to missing details of calculation
// they can be quite large...
if abs(ActualRowHeight - SollRowHeights[Row]) > 1e-2 then
if not SameValue(ActualRowHeight, abs(SollRowHeights[Row]), 1e-2) then
CheckEquals(SollRowHeights[Row], ActualRowHeight,
'Test saved row height mismatch, row '+RowNotation(MyWorkSheet,Row));
if (SollRowHeights[Row] < 0) and (rht <> rhtAuto) then
CheckEquals(ord(rht), ord(rhtAuto),
'Test saved row height type (rhtAuto) mismatch, row'+RowNotation(MyWorksheet,Row));
if (sollRowHeights[Row] > 0) and (rht <> rhtCustom) then
CheckEquals(ord(rht), ord(rhtCustom),
'Test saved row height type (rhtCustom) mismatch, row'+RowNotation(MyWorksheet,Row));
end;
finally
MyWorkbook.Free;

View File

@ -397,7 +397,6 @@ procedure TsWikiTableWriter.WriteToStrings_WikiMedia(AStrings: TStrings);
Result := Format('border-%s:%s', [BORDERNAMES[ABorder], LINESTYLES[ls]]);
if clr <> scBlack then
Result := Result + ' ' + ColorToHTMLColorStr(clr) + '; ';
// Result := Result + ' ' + FWorkbook.GetPaletteColorAsHTMLStr(clr) + '; ';
end;
const
@ -497,7 +496,7 @@ begin
if j = 0 then
begin
lRow := FWorksheet.FindRow(i);
if lRow <> nil then
if (lRow <> nil) and (lRow^.RowHeightType <> rhtDefault) then
lRowHeightStr := Format(' height="%.0fpt"',
[FWorkbook.ConvertUnits(lRow^.Height, FWorkbook.Units, suPoints)]);
end;

View File

@ -255,6 +255,7 @@ type
HorAlign_Border_BkGr: Byte;
end;
procedure InternalAddBuiltinNumFormats(AList: TStringList; AFormatSettings: TFormatSettings);
var
fs: TFormatSettings absolute AFormatSettings;
@ -784,25 +785,27 @@ var
rowrec: TRowRecord;
lRow: PRow;
h: word;
auto: Boolean;
rowheight: Single;
defRowHeight: Single;
begin
rowRec.RowIndex := 0; // to silence the compiler...
AStream.ReadBuffer(rowrec, SizeOf(TRowRecord));
h := WordLEToN(rowrec.Height);
if h and $8000 = 0 then // if this bit were set, rowheight would be default
begin
lRow := FWorksheet.GetRow(WordLEToN(rowrec.RowIndex));
// Row height is encoded into the 15 remaining bits in units "twips" (1/20 pt)
lRow^.Height := FWorkbook.ConvertUnits(
TwipsToPts(h and $7FFF), suPoints, FWorkbook.Units);
{
// We need it in "lines" units.
lRow^.Height := TwipsToPts(h and $7FFF) / Workbook.GetFont(0).Size;
if lRow^.Height > ROW_HEIGHT_CORRECTION then
lRow^.Height := lRow^.Height - ROW_HEIGHT_CORRECTION
else
lRow^.Height := 0;
}
end;
auto := h and $8000 <> 0;
rowheight := FWorkbook.ConvertUnits(TwipsToPts(h and $7FFF), suPoints, FWorkbook.Units);
defRowHeight := FWorksheet.ReadDefaultRowHeight(FWorkbook.Units);
// No row record if rowheight in file is the same as the default rowheight
if SameValue(rowheight, defRowHeight, ROWHEIGHT_EPS) then
exit;
// Otherwise: create a row record
lRow := FWorksheet.GetRow(WordLEToN(rowrec.RowIndex));
lRow^.Height := rowHeight;
if auto then
lRow^.RowHeightType := rhtAuto else
lRow^.RowHeightType := rhtCustom;
end;
{@@ ----------------------------------------------------------------------------
@ -1341,7 +1344,7 @@ begin
if (boVirtualMode in Workbook.Options) then
WriteVirtualCells(AStream, FWorksheet)
else begin
WriteRows(AStream, FWorksheet);
// WriteRows(AStream, FWorksheet);
WriteCellsToStream(AStream, FWorksheet.Cells);
end;
@ -1967,11 +1970,10 @@ end;
procedure TsSpreadBIFF2Writer.WriteRow(AStream: TStream; ASheet: TsWorksheet;
ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow);
const
EPS = 1E-2;
var
containsXF: Boolean;
rowheight: Word;
auto: Boolean;
w: Word;
begin
if (ARowIndex >= FLimitations.MaxRowCount) or (AFirstColIndex >= FLimitations.MaxColCount)
@ -1995,17 +1997,20 @@ begin
{ Index to column of the last cell which is described by a cell record, increased by 1 }
AStream.WriteWord(WordToLE(Word(ALastColIndex) + 1));
auto := true;
{ Row height (in twips, 1/20 point) and info on custom row height }
if (ARow = nil) or SameValue(ARow^.Height, ASheet.ReadDefaultRowHeight(FWorkbook.Units), EPS) then
if (ARow = nil) or (ARow^.RowHeightType = rhtDefault) then
rowheight := PtsToTwips(ASheet.ReadDefaultRowHeight(suPoints))
else
if (ARow^.Height = 0) then
rowheight := 0
else
rowheight := PtsToTwips(FWorkbook.ConvertUnits(
ARow^.Height, FWorkbook.Units, suPoints));
// rowheight := PtsToTwips((ARow^.Height + ROW_HEIGHT_CORRECTION) * h);
else begin
rowheight := PtsToTwips(FWorkbook.ConvertUnits(ARow^.Height, FWorkbook.Units, suPoints));
auto := ARow^.RowHeightType <> rhtCustom;
end;
w := rowheight and $7FFF;
if auto then
w := w or $8000;
AStream.WriteWord(WordToLE(w));
{ not used }

View File

@ -325,6 +325,8 @@ const
(2.54* 12.0 , 11.0 *2.54) // 90 - 12x11
);
ROWHEIGHT_EPS = 1E-2;
type
TDateMode=(dm1900,dm1904); //DATEMODE values, 5.28
@ -2080,17 +2082,30 @@ var
rowrec: TRowRecord;
lRow: PRow;
h: word;
hpts: Single;
hdef: Single;
isNonDefaultHeight: Boolean;
isAutoSizeHeight: Boolean;
begin
rowrec.RowIndex := 0; // to silence the compiler...
AStream.ReadBuffer(rowrec, SizeOf(TRowRecord));
// If bit 6 is set in the flags row height does not match the font size.
// Only for this case we create a row record for fpspreadsheet
if rowrec.Flags and $00000040 <> 0 then begin
h := WordLEToN(rowrec.Height) and $7FFF; // mask off "custom" bit
hpts := FWorkbook.ConvertUnits(TwipsToPts(h), suPoints, FWorkbook.Units);
hdef := FWorksheet.ReadDefaultRowHeight(FWorkbook.Units);
isNonDefaultHeight := not SameValue(hpts, hdef, ROWHEIGHT_EPS);
isAutoSizeHeight := WordLEToN(rowrec.Flags) and $00000040 = 0;
// If this bis is set then font size and row height do NOT match, i.e. NO autosize
// We only create a row record for fpspreadsheet if the row has a
// non-standard height (i.e. different from default row height).
if isNonDefaultHeight then begin
lRow := FWorksheet.GetRow(WordLEToN(rowrec.RowIndex));
// row height is encoded into the 15 lower bits in units "twips" (1/20 pt)
h := WordLEToN(rowrec.Height) and $7FFF;
lRow^.Height := FWorkbook.ConvertUnits(TwipsToPts(h), suPoints, FWorkbook.Units);
if isAutoSizeHeight then
lRow^.RowHeightType := rhtAuto else
lRow^.RowHeightType := rhtCustom;
lRow^.Height := hpts;
end;
end;
@ -4258,8 +4273,6 @@ end;
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.WriteRow(AStream: TStream; ASheet: TsWorksheet;
ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow);
const
EPS = 1E-2;
var
w: Word;
dw: DWord;
@ -4310,11 +4323,8 @@ begin
AStream.WriteWord(WordToLE(Word(ALastColIndex) + 1));
{ Row height (in twips, 1/20 point) and info on custom row height }
if (ARow = nil) or SameValue(ARow^.Height, ASheet.ReadDefaultRowHeight(FWorkbook.Units), EPS) then
if (ARow = nil) or (ARow^.RowHeightType = rhtDefault) then
rowheight := PtsToTwips(ASheet.ReadDefaultRowHeight(suPoints))
else
if (ARow^.Height = 0) then
rowheight := 0
else
rowheight := PtsToTwips(FWorkbook.ConvertUnits(ARow^.Height, FWorkbook.Units, suPoints));
w := rowheight and $7FFF;
@ -4327,8 +4337,10 @@ begin
dw := $00000100; // bit 8 is always 1
if spaceabove then dw := dw or $10000000;
if spacebelow then dw := dw or $20000000;
if (ARow <> nil) then
dw := dw or $00000040; // Row height and font height do not match
if (ARow <> nil) and (ARow^.RowHeightType = rhtCustom) then // Custom row height
dw := dw or $00000040; // Row height and font height do not match
{ Write out }
AStream.WriteDWord(DWordToLE(dw));
end;

View File

@ -710,14 +710,18 @@ begin
row := FWorksheet.FindRow(r);
// Row height is needed in pts.
if Assigned(row) then
begin
rowheightStr := Format(' ss:Height="%.2f"',
[FWorkbook.ConvertUnits(row^.Height, FWorkbook.Units, suPoints)],
FPointSeparatorSettings
)
else
rowheightStr := '';
);
if row^.RowHeightType = rhtCustom then
rowHeightStr := 'ss:AutoFitHeight="0"' + rowHeightStr else
rowHeightStr := 'ss:AutoFitHeight="1"' + rowHeightStr;
end else
rowheightStr := 'ss:AutoFitHeight="1"';
AppendToStream(AStream, ROW_INDENT + Format(
'<Row ss:AutoFitHeight="1"%s>' + LF, [rowheightStr]));
'<Row %s>' + LF, [rowheightStr]));
for c := c1 to c2 do
begin
cell := AWorksheet.FindCell(r, c);

View File

@ -286,6 +286,8 @@ const
LAST_PALETTE_INDEX = 63;
ROWHEIGHT_EPS = 1E-2;
type
TFillListData = class
PatternType: String;
@ -1717,19 +1719,35 @@ end;
procedure TsSpreadOOXMLReader.ReadRowHeight(ANode: TDOMNode; AWorksheet: TsWorksheet);
var
s: String;
ht: Single;
h: Single;
r: Cardinal;
rht: TsRowHeightType;
begin
if ANode = nil then
exit;
{ Row height value, in points - if there is no "ht" attribute we assume that
it is the custom row height which does not require a row record. }
s := GetAttrValue(ANode, 'ht');
if s = '' then
exit;
h := StrToFloat(s, FPointSeparatorSettings); // seems to be in "Points"
{ Row height type }
s := GetAttrValue(ANode, 'customHeight');
if s = '1' then begin
s := GetAttrValue(ANode, 'r');
r := StrToInt(s) - 1;
s := GetAttrValue(ANode, 'ht');
ht := StrToFloat(s, FPointSeparatorSettings); // seems to be in "Points"
AWorksheet.WriteRowHeight(r, ht, suPoints);
end;
if s = '1' then
rht := rhtCustom
else if SameValue(h, AWorksheet.ReadDefaultRowHeight(suPoints), ROWHEIGHT_EPS) then
rht := rhtDefault
else
rht := rhtAuto;
{ Row index }
s := GetAttrValue(ANode, 'r');
r := StrToInt(s) - 1;
{ Write out }
AWorksheet.WriteRowHeight(r, h, suPoints, rht);
end;
procedure TsSpreadOOXMLReader.ReadSharedStrings(ANode: TDOMNode);
@ -2912,11 +2930,12 @@ begin
then begin
for r := 0 to r2 do begin
row := AWorksheet.FindRow(r);
if row <> nil then
rh := Format(' ht="%.2f" customHeight="1"',
if row <> nil then begin
rh := Format(' ht="%.2f"',
[FWorkbook.ConvertUnits(row^.Height, FWorkbook.Units, suPoints)],
FPointSeparatorSettings)
else
FPointSeparatorSettings);
if row^.RowHeightType = rhtCustom then rh := rh + ' customHeight="1"';
end else
rh := '';
AppendToStream(AStream, Format(
'<row r="%d" spans="1:%d"%s>', [r+1, AWorksheet.VirtualColCount, rh]));
@ -2971,11 +2990,12 @@ begin
for r := r1 to r2 do begin
// If the row has a custom height add this value to the <row> specification
row := AWorksheet.FindRow(r);
if row <> nil then
rh := Format(' ht="%.2f" customHeight="1"',
if row <> nil then begin
rh := Format(' ht="%.2f"',
[FWorkbook.ConvertUnits(row^.Height, FWorkbook.Units, suPoints)],
FPointSeparatorSettings)
else
FPointSeparatorSettings);
if row^.RowHeightType = rhtCustom then rh := rh + ' customHeight="1"';
end else
rh := '';
AppendToStream(AStream, Format(
'<row r="%d" spans="%d:%d"%s>', [r+1, c1+1, c2+1, rh]));
@ -3071,7 +3091,7 @@ begin
actCell := '';
// Selected tab?
tabSel := StrUtils.IfThen(AWorksheet = FWorkbook.ActiveWorksheet, 'tabSelected="1" ', '');
tabSel := StrUtils.IfThen(AWorksheet = FWorkbook.ActiveWorksheet, ' tabSelected="1"', '');
// SheetView attributes
attr := showGridLines + showHeaders + tabSel + zoomScale + bidi;