You've already forked lazarus-ccr
fpspreadsheet: Re-do rich-text format (easier, less code). Fix rich-text issues with utf8 characters.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4257 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@@ -1198,9 +1198,9 @@ begin
|
||||
textp := textp + '<a href="' + target + '">';
|
||||
rtParam := ACell^.RichTextParams[0];
|
||||
// Part before first formatted section (has cell fnt)
|
||||
if rtParam.StartIndex > 0 then
|
||||
if rtParam.FirstIndex > 1 then
|
||||
begin
|
||||
txt := UTF8Copy(AValue, 1, rtParam.StartIndex);
|
||||
txt := UTF8Copy(AValue, 1, rtParam.FirstIndex - 1);
|
||||
ValidXMLText(txt);
|
||||
if cellfnt.Position <> fpNormal then
|
||||
txt := Format('<%0:s>%1:s</%0:s>', [ESCAPEMENT_TAG[cellFnt.Position], txt]);
|
||||
@@ -1214,31 +1214,14 @@ begin
|
||||
style := GetFontAsStyle(rtParam.FontIndex);
|
||||
if style <> '' then
|
||||
style := ' style="' + style +'"';
|
||||
n := rtParam.EndIndex - rtParam.StartIndex;
|
||||
txt := UTF8Copy(AValue, rtParam.StartIndex+1, n);
|
||||
if i = High(ACell^.RichTextParams) then
|
||||
n := len - rtParam.FirstIndex else
|
||||
n := ACell^.RichTextParams[i+1].FirstIndex - rtParam.FirstIndex;
|
||||
txt := UTF8Copy(AValue, rtParam.FirstIndex, n);
|
||||
ValidXMLText(txt);
|
||||
if fnt.Position <> fpNormal then
|
||||
txt := Format('<%0:s>%1:s</%0:s>', [ESCAPEMENT_TAG[fnt.Position], txt]);
|
||||
textp := textp + '<span' + style +'>' + txt + '</span>';
|
||||
// unformatted section before end
|
||||
if (rtParam.EndIndex < len) and (i = High(ACell^.RichTextParams)) then
|
||||
begin
|
||||
txt := UTF8Copy(AValue, rtParam.EndIndex+1, MaxInt);
|
||||
ValidXMLText(txt);
|
||||
if cellFnt.Position <> fpNormal then
|
||||
txt := Format('<%0:s>%1:s</%0:s>', [ESCAPEMENT_TAG[cellFnt.Position], txt]);
|
||||
textp := textp + txt;
|
||||
end else
|
||||
// unformatted section between two formatted sections
|
||||
if (i < High(ACell^.RichTextParams)) and (rtParam.EndIndex < ACell^.RichTextParams[i+1].StartIndex)
|
||||
then begin
|
||||
n := ACell^.RichTextParams[i+1].StartIndex - rtParam.EndIndex;
|
||||
txt := UTF8Copy(AValue, rtParam.EndIndex+1, n);
|
||||
ValidXMLText(txt);
|
||||
if cellFnt.Position <> fpNormal then
|
||||
txt := Format('<%0:s>%1:s</%0:s>', [ESCAPEMENT_TAG[cellFnt.Position], txt]);
|
||||
textp := textp + txt;
|
||||
end;
|
||||
end;
|
||||
if target <> '' then
|
||||
textp := textp + '</a></div>' else
|
||||
|
@@ -2261,12 +2261,12 @@ begin
|
||||
if idx > -1 then
|
||||
begin
|
||||
SetLength(rtParams, Length(rtParams)+1);
|
||||
rtParams[High(rtParams)].FirstIndex := UTF8Length(cellText) + 1; // 1-based character index
|
||||
rtParams[High(rtParams)].FontIndex := FCellFormatList[idx]^.FontIndex;
|
||||
rtParams[High(rtParams)].StartIndex := Length(cellText);
|
||||
rtParams[High(rtParams)].EndIndex := Length(cellText + spanText);
|
||||
rtParams[High(rtParams)].HyperlinkIndex := -1; // TO DO !!!!
|
||||
end;
|
||||
end;
|
||||
AddToCelLText(spanText);
|
||||
AddToCellText(spanText);
|
||||
end;
|
||||
end;
|
||||
subnode := subnode.NextSibling;
|
||||
@@ -5644,7 +5644,7 @@ var
|
||||
fntName: String;
|
||||
hyperlink: PsHyperlink;
|
||||
u: TUri;
|
||||
i, idx, fntidx, len: Integer;
|
||||
i, idx, endidx, fntidx, len: Integer;
|
||||
rtParam: TsRichTextParam;
|
||||
wideStr, txt: WideString;
|
||||
ch: WideChar;
|
||||
@@ -5750,16 +5750,16 @@ begin
|
||||
else
|
||||
begin
|
||||
// "Rich-text" formatting
|
||||
wideStr := UTF8Encode(AValue); // Convert to unicode
|
||||
// Before the first formatted section which has the cell's format
|
||||
wideStr := UTF8Decode(AValue); // Convert to unicode
|
||||
// Before the first formatted section having the cell's format
|
||||
len := Length(wideStr);
|
||||
totaltxt := '<text:p>';
|
||||
rtParam := ACell^.RichTextParams[0];
|
||||
idx := 1;
|
||||
txt := '';
|
||||
if rtParam.StartIndex > 0 then
|
||||
if rtParam.FirstIndex > 1 then
|
||||
begin
|
||||
while (idx <= len) and (idx <= rtParam.StartIndex) do
|
||||
while (idx <= len) and (idx < rtParam.FirstIndex) do
|
||||
begin
|
||||
ch := wideStr[idx];
|
||||
if NewLine(idx) then
|
||||
@@ -5770,17 +5770,19 @@ begin
|
||||
end;
|
||||
if txt <> '' then
|
||||
AppendTxt(false, '');
|
||||
// totaltxt := totaltxt + UTF8Encode(txt);
|
||||
end;
|
||||
txt := '';
|
||||
for i := 0 to High(ACell^.RichTextParams) do
|
||||
begin
|
||||
// Formatted part of the string according the RichTextParam
|
||||
// Formatted parts of the string according the RichTextParams
|
||||
rtParam := ACell^.RichTextParams[i];
|
||||
fnt := FWorkbook.GetFont(rtParam.FontIndex);
|
||||
fntidx := FRichTextFontList.IndexOfObject(fnt);
|
||||
fntName := FRichTextFontList[fntIdx];
|
||||
while (idx <= len) and (idx <= rtParam.EndIndex) do
|
||||
if i < High(ACell^.RichTextParams) then
|
||||
endidx := ACell^.RichTextParams[i+1].FirstIndex-1 else
|
||||
endidx := len;
|
||||
while (idx <= len) and (idx <= endidx) do
|
||||
begin
|
||||
ch := wideStr[idx];
|
||||
if NewLine(idx) then
|
||||
@@ -5791,36 +5793,6 @@ begin
|
||||
end;
|
||||
if txt <> '' then
|
||||
AppendTxt(false, fntName);
|
||||
// Unformatted part at end of string (cell's format)
|
||||
if (rtParam.EndIndex < len) and (i = High(ACell^.RichTextParams)) then
|
||||
begin
|
||||
while (idx <= len) do
|
||||
begin
|
||||
ch := wideStr[idx];
|
||||
if NewLine(idx) then
|
||||
AppendTxt(true, '')
|
||||
else
|
||||
txt := txt + ch;
|
||||
inc(idx);
|
||||
end;
|
||||
if txt <> '' then
|
||||
AppendTxt(false, '');
|
||||
end
|
||||
else
|
||||
// Unformatted part between formatted parts (cll's format)
|
||||
if (i < High(ACell^.RichTextParams)) and (rtParam.EndIndex < ACell^.RichTextParams[i+1].StartIndex)
|
||||
then begin
|
||||
while (idx <= len) and (idx <= ACell^.RichTextParams[i+1].StartIndex) do begin
|
||||
ch := wideStr[idx];
|
||||
if NewLine(idx) then
|
||||
AppendTxt(true, '')
|
||||
else
|
||||
txt := txt + ch;
|
||||
inc(idx);
|
||||
end;
|
||||
if txt <> '' then
|
||||
AppendTxt(false, '');
|
||||
end;
|
||||
end;
|
||||
totaltxt := totaltxt + '</text:p>';
|
||||
end;
|
||||
|
@@ -442,7 +442,8 @@ type
|
||||
|
||||
// Searching
|
||||
function Search(ASearchText: String; AOptions: TsSearchOptions;
|
||||
AStartRow: Cardinal = $FFFFFFFF; AStartCol: Cardinal = $FFFFFFFF): PCell;
|
||||
AStartRow: Cardinal = UNASSIGNED_ROW_COL_INDEX;
|
||||
AStartCol: Cardinal = UNASSIGNED_ROW_COL_INDEX): PCell;
|
||||
|
||||
// Comments
|
||||
function FindComment(ACell: PCell): PsComment;
|
||||
@@ -725,8 +726,8 @@ type
|
||||
|
||||
{ Searching }
|
||||
function Search(ASearchText: String; AOptions: TsSearchOptions;
|
||||
AStartSheet: TsWorksheet = nil; AStartRow: Cardinal = $FFFFFFFF;
|
||||
AStartCol: Cardinal = $FFFFFFFF): PCell;
|
||||
AStartSheet: TsWorksheet = nil; AStartRow: Cardinal = UNASSIGNED_ROW_COL_INDEX;
|
||||
AStartCol: Cardinal = UNASSIGNED_ROW_COL_INDEX): PCell;
|
||||
|
||||
{ Utilities }
|
||||
procedure UpdateCaches;
|
||||
@@ -954,13 +955,13 @@ begin
|
||||
FDefaultColWidth := 12;
|
||||
FDefaultRowHeight := 1;
|
||||
|
||||
FFirstRowIndex := $FFFFFFFF;
|
||||
FFirstColIndex := $FFFFFFFF;
|
||||
FLastRowIndex := $FFFFFFFF;
|
||||
FLastColIndex := $FFFFFFFF;
|
||||
FFirstRowIndex := UNASSIGNED_ROW_COL_INDEX;
|
||||
FFirstColIndex := UNASSIGNED_ROW_COL_INDEX;
|
||||
FLastRowIndex := UNASSIGNED_ROW_COL_INDEX;
|
||||
FLastColIndex := UNASSIGNED_ROW_COL_INDEX;
|
||||
|
||||
FActiveCellRow := Cardinal(-1);
|
||||
FActiveCellCol := Cardinal(-1);
|
||||
FActiveCellRow := UNASSIGNED_ROW_COL_INDEX; // Cardinal(-1);
|
||||
FActiveCellCol := UNASSIGNED_ROW_COL_INDEX; // Cardinal(-1);
|
||||
|
||||
FOptions := [soShowGridLines, soShowHeaders];
|
||||
end;
|
||||
@@ -1916,14 +1917,22 @@ end;
|
||||
function TsWorksheet.AddCell(ARow, ACol: Cardinal): PCell;
|
||||
begin
|
||||
Result := Cells.AddCell(ARow, ACol);
|
||||
if FFirstColIndex = $FFFFFFFF then FFirstColIndex := GetFirstColIndex(true)
|
||||
else FFirstColIndex := Min(FFirstColIndex, ACol);
|
||||
if FFirstRowIndex = $FFFFFFFF then FFirstRowIndex := GetFirstRowIndex(true)
|
||||
else FFirstRowIndex := Min(FFirstRowIndex, ARow);
|
||||
if FLastColIndex = $FFFFFFFF then FLastColIndex := GetLastColIndex(true)
|
||||
else FLastColIndex := Max(FLastColIndex, ACol);
|
||||
if FLastRowIndex = $FFFFFFFF then FLastRowIndex := GetLastRowIndex(true)
|
||||
else FLastRowIndex := Max(FLastRowIndex, ARow);
|
||||
|
||||
if FFirstColIndex = UNASSIGNED_ROW_COL_INDEX then
|
||||
FFirstColIndex := GetFirstColIndex(true) else
|
||||
FFirstColIndex := Min(FFirstColIndex, ACol);
|
||||
|
||||
if FFirstRowIndex = UNASSIGNED_ROW_COL_INDEX then
|
||||
FFirstRowIndex := GetFirstRowIndex(true) else
|
||||
FFirstRowIndex := Min(FFirstRowIndex, ARow);
|
||||
|
||||
if FLastColIndex = UNASSIGNED_ROW_COL_INDEX then
|
||||
FLastColIndex := GetLastColIndex(true) else
|
||||
FLastColIndex := Max(FLastColIndex, ACol);
|
||||
|
||||
if FLastRowIndex = UNASSIGNED_ROW_COL_INDEX then
|
||||
FLastRowIndex := GetLastRowIndex(true) else
|
||||
FLastRowIndex := Max(FLastRowIndex, ARow);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
@@ -2160,7 +2169,7 @@ var
|
||||
begin
|
||||
if AForceCalculation then
|
||||
begin
|
||||
Result := Cardinal(-1);
|
||||
Result := UNASSIGNED_ROW_COL_INDEX;
|
||||
for cell in FCells do
|
||||
Result := Math.Min(Result, cell^.Col);
|
||||
// In addition, there may be column records defining the column width even
|
||||
@@ -2174,7 +2183,7 @@ begin
|
||||
else
|
||||
begin
|
||||
Result := FFirstColIndex;
|
||||
if Result = cardinal(-1) then
|
||||
if Result = UNASSIGNED_ROW_COL_INDEX then
|
||||
Result := GetFirstColIndex(true);
|
||||
end;
|
||||
end;
|
||||
@@ -2200,7 +2209,7 @@ function TsWorksheet.GetLastColIndex(AForceCalculation: Boolean = false): Cardin
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
if AForceCalculation or (FLastColIndex = $FFFFFFFF) then
|
||||
if AForceCalculation or (FLastColIndex = UNASSIGNED_ROW_COL_INDEX) then
|
||||
begin
|
||||
// Traverse the tree from lowest to highest.
|
||||
// Since tree primary sort order is on row highest col could exist anywhere.
|
||||
@@ -2265,7 +2274,7 @@ var
|
||||
begin
|
||||
if AForceCalculation then
|
||||
begin
|
||||
Result := $FFFFFFFF;
|
||||
Result := UNASSIGNED_ROW_COL_INDEX;
|
||||
cell := FCells.GetFirstCell;
|
||||
if cell <> nil then Result := cell^.Row;
|
||||
// In addition, there may be row records even for rows without cells.
|
||||
@@ -2278,7 +2287,7 @@ begin
|
||||
else
|
||||
begin
|
||||
Result := FFirstRowIndex;
|
||||
if Result = Cardinal(-1) then
|
||||
if Result = UNASSIGNED_ROW_COL_INDEX then
|
||||
Result := GetFirstRowIndex(true);
|
||||
end;
|
||||
end;
|
||||
@@ -2303,7 +2312,7 @@ function TsWorksheet.GetLastRowIndex(AForceCalculation: Boolean = false): Cardin
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
if AForceCalculation or (FLastRowIndex = $FFFFFFFF) then
|
||||
if AForceCalculation or (FLastRowIndex = UNASSIGNED_ROW_COL_INDEX) then
|
||||
begin
|
||||
// Index of highest row with at least one existing cell
|
||||
Result := GetLastOccupiedRowIndex;
|
||||
@@ -3542,7 +3551,8 @@ end;
|
||||
first cell meeting the criteria.
|
||||
-------------------------------------------------------------------------------}
|
||||
function TsWorksheet.Search(ASearchText: String; AOptions: TsSearchOptions;
|
||||
AStartRow: Cardinal = $FFFFFFFF; AStartCol: Cardinal = $FFFFFFFF): PCell;
|
||||
AStartRow: Cardinal = UNASSIGNED_ROW_COL_INDEX;
|
||||
AStartCol: Cardinal = UNASSIGNED_ROW_COL_INDEX): PCell;
|
||||
var
|
||||
regex: TRegExpr;
|
||||
cell, startCell: PCell;
|
||||
@@ -3577,12 +3587,12 @@ begin
|
||||
// Find first occupied cell to start with
|
||||
if (soBackward in AOptions) then
|
||||
begin
|
||||
if AStartRow = $FFFFFFFF then AStartRow := lastR;
|
||||
if AStartCol = $FFFFFFFF then AStartCol := lastC;
|
||||
if AStartRow = UNASSIGNED_ROW_COL_INDEX then AStartRow := lastR;
|
||||
if AStartCol = UNASSIGNED_ROW_COL_INDEX then AStartCol := lastC;
|
||||
end else
|
||||
begin
|
||||
if AStartRow = $FFFFFFFF then AStartRow := firstR;
|
||||
if AStartCol = $FFFFFFFF then AStartCol := firstC;
|
||||
if AStartRow = UNASSIGNED_ROW_COL_INDEX then AStartRow := firstR;
|
||||
if AStartCol = UNASSIGNED_ROW_COL_INDEX then AStartCol := firstC;
|
||||
end;
|
||||
startcell := FindCell(AStartRow, AStartCol);
|
||||
if startcell = nil then
|
||||
@@ -3805,11 +3815,10 @@ begin
|
||||
ACell^.ContentType := cctUTF8String;
|
||||
ACell^.UTF8StringValue := AText;
|
||||
|
||||
if Length(ARichTextParams) > 0 then begin
|
||||
SetLength(ACell^.RichTextParams, Length(ARichTextParams));
|
||||
SetLength(ACell^.RichTextParams, Length(ARichTextParams));
|
||||
if Length(ARichTextParams) > 0 then
|
||||
for i:=0 to High(ARichTextParams) do
|
||||
ACell^.RichTextParams[i] := ARichTextParams[i];
|
||||
end;
|
||||
|
||||
ChangedCell(ACell^.Row, ACell^.Col);
|
||||
end;
|
||||
@@ -5779,10 +5788,10 @@ begin
|
||||
FillChar(Result^, SizeOf(TCol), #0);
|
||||
Result^.Col := ACol;
|
||||
FCols.Add(Result);
|
||||
if FFirstColIndex = $FFFFFFFF
|
||||
if FFirstColIndex = UNASSIGNED_ROW_COL_INDEX
|
||||
then FFirstColIndex := GetFirstColIndex(true)
|
||||
else FFirstColIndex := Min(FFirstColIndex, ACol);
|
||||
if FLastColIndex = $FFFFFFFF
|
||||
if FLastColIndex = UNASSIGNED_ROW_COL_INDEX
|
||||
then FLastColIndex := GetLastColIndex(true)
|
||||
else FLastColIndex := Max(FLastColIndex, ACol);
|
||||
end;
|
||||
@@ -6386,8 +6395,8 @@ end;
|
||||
a specified text.
|
||||
-------------------------------------------------------------------------------}
|
||||
function TsWorkbook.Search(ASearchText: String; AOptions: TsSearchOptions;
|
||||
AStartSheet: TsWorksheet = nil; AStartRow: Cardinal = $FFFFFFFF;
|
||||
AStartCol: Cardinal = $FFFFFFFF): PCell;
|
||||
AStartSheet: TsWorksheet = nil; AStartRow: Cardinal = UNASSIGNED_ROW_COL_INDEX;
|
||||
AStartCol: Cardinal = UNASSIGNED_ROW_COL_INDEX): PCell;
|
||||
var
|
||||
i, idxSheet: Integer;
|
||||
sheet: TsWorksheet;
|
||||
@@ -6395,19 +6404,19 @@ begin
|
||||
// Setup missing default parameters
|
||||
if soBackward in AOptions then
|
||||
begin
|
||||
if (AStartRow = $FFFFFFFF) and (AStartCol = $FFFFFFFF) and (AStartSheet = nil)
|
||||
if (AStartRow = UNASSIGNED_ROW_COL_INDEX) and (AStartCol = UNASSIGNED_ROW_COL_INDEX) and (AStartSheet = nil)
|
||||
then AStartsheet := GetWorksheetByIndex(GetWorksheetCount-1);
|
||||
if AStartRow = $FFFFFFFF then
|
||||
if AStartRow = UNASSIGNED_ROW_COL_INDEX then
|
||||
AStartRow := AStartsheet.GetLastRowIndex;
|
||||
if AStartCol = $FFFFFFFF then
|
||||
if AStartCol = UNASSIGNED_ROW_COL_INDEX then
|
||||
AStartCol := AStartsheet.GetLastColIndex;
|
||||
end else
|
||||
begin
|
||||
if (AStartRow = $FFFFFFFF) and (AStartCol = $FFFFFFFF) and (AStartSheet = nil)
|
||||
if (AStartRow = UNASSIGNED_ROW_COL_INDEX) and (AStartCol = UNASSIGNED_ROW_COL_INDEX) and (AStartSheet = nil)
|
||||
then AStartsheet := GetWorksheetByIndex(0);
|
||||
if (AStartRow = $FFFFFFFF) then
|
||||
if (AStartRow = UNASSIGNED_ROW_COL_INDEX) then
|
||||
AStartRow := AStartsheet.GetFirstRowIndex;
|
||||
if (AStartCol = $FFFFFFFF) then
|
||||
if (AStartCol = UNASSIGNED_ROW_COL_INDEX) then
|
||||
AStartCol := AStartsheet.GetFirstColIndex;
|
||||
end;
|
||||
if AStartSheet = nil then
|
||||
|
@@ -2725,7 +2725,7 @@ begin
|
||||
begin
|
||||
s := '';
|
||||
for rtp in ACell^.RichTextParams do
|
||||
s := Format('%s; Font #%d @ %d-%d', [s, rtp.FontIndex, rtp.StartIndex, rtp.EndIndex]);
|
||||
s := Format('%s; Font #%d after pos %d', [s, rtp.FontIndex, rtp.FirstIndex]);
|
||||
Delete(s, 1, 2);
|
||||
if s = '' then s := '(none)';
|
||||
AStrings.Add('Rich-text parameters='+s);
|
||||
|
@@ -1878,7 +1878,7 @@ begin
|
||||
// Because of possible cell overflow from cells left of the visible range
|
||||
// we have to seek to the left for the first occupied text cell
|
||||
// and start painting from here.
|
||||
if FTextOverflow and (sr <> Cardinal(-1)) and Assigned(Worksheet) then
|
||||
if FTextOverflow and (sr <> UNASSIGNED_ROW_COL_INDEX) and Assigned(Worksheet) then
|
||||
while (gc > FixedCols) do
|
||||
begin
|
||||
dec(gc);
|
||||
@@ -1904,7 +1904,7 @@ begin
|
||||
// Now find the last column. Again text can overflow into the visible area
|
||||
// from cells to the right.
|
||||
gcLast := Right;
|
||||
if FTextOverflow and (sr <> Cardinal(-1)) and Assigned(Worksheet) then
|
||||
if FTextOverflow and (sr <> UNASSIGNED_ROW_COL_INDEX) and Assigned(Worksheet) then
|
||||
begin
|
||||
gcLastUsed := GetGridCol(scLastUsed);
|
||||
while (gcLast < ColCount-1) and (gcLast < gcLastUsed) do begin
|
||||
@@ -3011,7 +3011,7 @@ end;
|
||||
function TsCustomWorksheetGrid.GetWorksheetCol(AGridCol: Integer): cardinal;
|
||||
begin
|
||||
if (FHeaderCount > 0) and (AGridCol = 0) then
|
||||
Result := Cardinal(-1)
|
||||
Result := UNASSIGNED_ROW_COL_INDEX
|
||||
else
|
||||
Result := AGridCol - FHeaderCount;
|
||||
end;
|
||||
@@ -3027,7 +3027,7 @@ end;
|
||||
function TsCustomWorksheetGrid.GetWorksheetRow(AGridRow: Integer): Cardinal;
|
||||
begin
|
||||
if (FHeaderCount > 0) and (AGridRow = 0) then
|
||||
Result := Cardinal(-1)
|
||||
Result := UNASSIGNED_ROW_COL_INDEX
|
||||
else
|
||||
Result := AGridRow - FHeaderCount;
|
||||
end;
|
||||
|
@@ -51,6 +51,9 @@ const
|
||||
{@@ Maximum count of worksheet columns}
|
||||
MAX_COL_COUNT = 65535;
|
||||
|
||||
{@@ Unassigned row/col index }
|
||||
UNASSIGNED_ROW_COL_INDEX = $FFFFFFFF;
|
||||
|
||||
{@@ Name of the default font}
|
||||
DEFAULT_FONTNAME = 'Arial';
|
||||
{@@ Size of the default font}
|
||||
@@ -426,14 +429,20 @@ type
|
||||
|
||||
{@@ Parameter describing formatting of an text range in cell text }
|
||||
TsRichTextParam = record
|
||||
FirstIndex: Integer; // 1-based utf8 character index
|
||||
FontIndex: Integer;
|
||||
HyperlinkIndex: Integer;
|
||||
{
|
||||
FontIndex: Integer;
|
||||
StartIndex: Integer; // zero-based
|
||||
EndIndex: Integer; // zero-based, next character!
|
||||
}
|
||||
end;
|
||||
|
||||
{@@ Parameters describing formatting of text ranges in cell text }
|
||||
TsRichTextParams = array of TsRichTextParam;
|
||||
|
||||
(*
|
||||
{@@ Excel rich-text formatting run }
|
||||
TsRichTextFormattingRun = packed record
|
||||
FirstIndex: Integer;
|
||||
@@ -442,6 +451,7 @@ type
|
||||
|
||||
{@@ Array of Excel rich-text formatting runs }
|
||||
TsRichTextFormattingRuns = array of TsRichTextFormattingRun;
|
||||
*)
|
||||
|
||||
{@@ Indicates the border for a cell. If included in the CellBorders set the
|
||||
corresponding border is drawn in the style defined by the CellBorderStyle. }
|
||||
|
@@ -131,6 +131,7 @@ function TintedColor(AColor: TsColor; tint: Double): TsColor;
|
||||
|
||||
function AnalyzeCompareStr(AString: String; out ACompareOp: TsCompareOperation): String;
|
||||
|
||||
procedure FixLineEndings(var AText: String; var ARichTextParams: TsRichTextParams);
|
||||
function UnquoteStr(AString: String): String;
|
||||
|
||||
function InitSortParams(ASortByCols: Boolean = true; ANumSortKeys: Integer = 1;
|
||||
@@ -1665,6 +1666,34 @@ begin
|
||||
RemoveChars(0, coEqual);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Replaces CRLF line endings by LF (#10) alone because this is what xml returns.
|
||||
This is required to keep the character indexes of the rich text formatting
|
||||
runs in synch when reading xml files.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure FixLineEndings(var AText: String; var ARichTextParams: TsRichTextParams);
|
||||
var
|
||||
i, j: Integer;
|
||||
begin
|
||||
if AText = '' then
|
||||
exit;
|
||||
|
||||
i := 1;
|
||||
if AText[Length(AText)] = #13 then
|
||||
Delete(AText, Length(AText), 1);
|
||||
|
||||
while i <= Length(AText) - 1 do
|
||||
begin
|
||||
if (AText[i] = #13) and (AText[i+1] = #10) then
|
||||
begin
|
||||
Delete(AText, i, 1);
|
||||
for j := 0 to High(ARichTextParams) do
|
||||
if ARichTextParams[j].FirstIndex > i then dec(ARichTextParams[j].FirstIndex);
|
||||
end;
|
||||
inc(i);
|
||||
end;
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Removes quotation characters which enclose a string
|
||||
-------------------------------------------------------------------------------}
|
||||
|
@@ -176,129 +176,120 @@ procedure InternalDrawRichText(ACanvas: TCanvas; AWorkbook: TsWorkbook;
|
||||
const ARect: TRect; const AText: String; AFontIndex: Integer;
|
||||
ARichTextParams: TsRichTextParams; AWordwrap: Boolean;
|
||||
AHorAlignment: TsHorAlignment; AVertAlignment: TsVertAlignment;
|
||||
ARotation: TsTextRotation; AOverrideTextColor: TColor;
|
||||
var Width,Height: Integer; AMeasureOnly: Boolean);
|
||||
ARotation: TsTextRotation; AOverrideTextColor: TColor; AMeasureOnly: Boolean;
|
||||
var AWidth, AHeight: Integer);
|
||||
type
|
||||
TLineInfo = record
|
||||
pStart, pEnd: PChar;
|
||||
NumSpaces: Integer;
|
||||
FirstRtpIndex: Integer;
|
||||
NextRtpIndex: Integer;
|
||||
BeginsWithFontOfRtpIndex: Integer;
|
||||
Width: Integer;
|
||||
Height: Integer;
|
||||
end;
|
||||
TRtState = (rtEnter, rtExit);
|
||||
var
|
||||
xpos, ypos: Integer;
|
||||
p, pStartText: PChar;
|
||||
iRtp: Integer;
|
||||
rtpIndex: Integer;
|
||||
lineInfo: TLineInfo;
|
||||
lineInfos: Array of TLineInfo = nil;
|
||||
totalHeight, linelen, stackPeriod: Integer;
|
||||
charPos: Integer;
|
||||
fontpos: TsFontPosition;
|
||||
fontHeight: Integer;
|
||||
|
||||
procedure InitFont(P: PChar; out rtState: TRtState;
|
||||
PendingRtpIndex: Integer; out AHeight: Integer; out AFontPos: TsFontPosition);
|
||||
procedure InitFont(out ARtpFontIndex: Integer; out AFontHeight: Integer;
|
||||
out AFontPos: TsFontPosition);
|
||||
var
|
||||
fnt: TsFont;
|
||||
hasRtp: Boolean;
|
||||
rtp: TsRichTextParam;
|
||||
rtParam: TsRichTextParam;
|
||||
begin
|
||||
fnt := AWorkbook.GetFont(AFontIndex);
|
||||
hasRtp := PendingRtpIndex >= 0;
|
||||
if hasRTP and (PendingRtpIndex < Length(ARichTextParams)) then begin
|
||||
rtp := ARichTextParams[PendingRtpIndex];
|
||||
if p - pStartText >= rtp.StartIndex then
|
||||
begin
|
||||
fnt := AWorkbook.GetFont(rtp.FontIndex);
|
||||
rtState := rtEnter;
|
||||
end else
|
||||
rtState := rtExit;
|
||||
if (Length(ARichTextParams) > 0) and (charPos >= ARichTextParams[0].FirstIndex) then
|
||||
begin
|
||||
ARtpFontIndex := 0;
|
||||
fnt := AWorkbook.GetFont(ARichTextParams[0].FontIndex);
|
||||
end else
|
||||
begin
|
||||
ARtpFontIndex := -1;
|
||||
fnt := AWorkbook.GetFont(AFontIndex);
|
||||
end;
|
||||
Convert_sFont_to_Font(fnt, ACanvas.Font);
|
||||
AHeight := ACanvas.TextHeight('Tg');
|
||||
AFontHeight := ACanvas.TextHeight('Tg');
|
||||
if (fnt <> nil) and (fnt.Position <> fpNormal) then
|
||||
ACanvas.Font.Size := round(fnt.Size * SUBSCRIPT_SUPERSCRIPT_FACTOR);
|
||||
AFontPos := fnt.Position;
|
||||
end;
|
||||
|
||||
procedure UpdateFont(P:PChar; var rtState: TRtState;
|
||||
var PendingRtpIndex: Integer; var AHeight: Integer;
|
||||
var AFontPos: TsFontPosition);
|
||||
procedure UpdateFont(ACharPos: Integer; var ARtpFontIndex: Integer;
|
||||
var AFontHeight: Integer; var AFontPos: TsFontPosition);
|
||||
var
|
||||
hasRtp: Boolean;
|
||||
rtp: TsRichTextParam;
|
||||
rtParam: TsRichTextParam;
|
||||
fnt: TsFont;
|
||||
endPos: Integer;
|
||||
begin
|
||||
fnt := AWorkbook.GetFont(AFontIndex);
|
||||
hasRtp := PendingRtpIndex >= 0;
|
||||
if hasRtp and (PendingRtpIndex < Length(ARichTextParams)) then
|
||||
begin
|
||||
rtp := ARichTextParams[PendingRtpIndex];
|
||||
if (p - pStartText >= rtp.StartIndex) and (rtState = rtExit) then
|
||||
begin
|
||||
fnt := AWorkbook.GetFont(rtp.FontIndex);
|
||||
Convert_sFont_to_Font(fnt, ACanvas.Font);
|
||||
AHeight := ACanvas.TextHeight('Tg');
|
||||
if fnt.Position <> fpNormal then
|
||||
ACanvas.Font.Size := round(fnt.Size * SUBSCRIPT_SUPERSCRIPT_FACTOR);
|
||||
AFontPos := fnt.Position;
|
||||
rtState := rtEnter;
|
||||
end else
|
||||
if (p - pStartText >= rtp.EndIndex) and (rtState = rtEnter) then
|
||||
begin
|
||||
inc(PendingRtpIndex);
|
||||
if PendingRtpIndex = Length(ARichTextparams) then
|
||||
begin
|
||||
fnt := AWorkbook.GetFont(AFontIndex);
|
||||
rtState := rtExit;
|
||||
end else
|
||||
begin
|
||||
rtp := ARichTextParams[PendingRtpIndex];
|
||||
if (p - pStartText < rtp.StartIndex) then
|
||||
begin
|
||||
fnt := AWorkbook.GetFont(AFontIndex);
|
||||
rtState := rtExit;
|
||||
end else
|
||||
begin
|
||||
fnt := AWorkbook.GetFont(rtp.FontIndex);
|
||||
rtState := rtEnter;
|
||||
end;
|
||||
end;
|
||||
Convert_sFont_to_Font(fnt, ACanvas.Font);
|
||||
AHeight := ACanvas.TextHeight('Tg');
|
||||
if fnt.Position <> fpNormal then
|
||||
ACanvas.Font.Size := round(fnt.Size * SUBSCRIPT_SUPERSCRIPT_FACTOR);
|
||||
AFontPos := fnt.Position;
|
||||
end;
|
||||
if ARtpFontIndex = High(ARichTextParams) then
|
||||
endPos := MaxInt
|
||||
else begin
|
||||
rtParam := ARichTextParams[ARtpFontIndex + 1];
|
||||
endPos := rtParam.FirstIndex;
|
||||
end;
|
||||
|
||||
if ACharPos >= endPos then begin
|
||||
inc(ARtpFontIndex);
|
||||
rtParam := ARichTextParams[ARtpFontIndex];
|
||||
fnt := AWorkbook.GetFont(rtParam.FontIndex);
|
||||
Convert_sFont_to_Font(fnt, ACanvas.Font);
|
||||
AFontHeight := ACanvas.TextHeight('Tg');
|
||||
if fnt.Position <> fpNormal then
|
||||
ACanvas.Font.Size := round(fnt.Size * SUBSCRIPT_SUPERSCRIPT_FACTOR);
|
||||
AFontPos := fnt.Position;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Scans the line for a possible line break. The max width is determined by
|
||||
the size of the rectangle ARect passed to the outer procedure:
|
||||
rectangle width in case of horizontal painting, rectangle height in case
|
||||
of vertical painting. Line breaks can occure at spaces or cr/lf characters,
|
||||
or, if not found, at any character reaching the max width.
|
||||
|
||||
Parameters:
|
||||
|
||||
P defines where the scan starts. At the end of the routine it
|
||||
points to the first character of the next line.
|
||||
ANumSpaces is how many spaces were found between the start and end value
|
||||
of P.
|
||||
ARtpFontIndex At input, this is the index of the rich-text formatting
|
||||
parameter value used for the font at line start. At output,
|
||||
it is the index which will be valid at next line start.
|
||||
ALineWidth the pixel width of the line seen along drawing direction, i.e.
|
||||
in case of stacked text it is the character height times
|
||||
character count in the line (!)
|
||||
ALineHeight The height of the line as seen vertical to the drawing
|
||||
direction. Normally this is the height of the largest font
|
||||
found in the line; in case of stacked text it is the
|
||||
standardized width of a character. }
|
||||
procedure ScanLine(var P: PChar; var NumSpaces: Integer;
|
||||
var PendingRtpIndex: Integer; var width, height: Integer);
|
||||
var ARtpFontIndex: Integer; var ALineWidth, ALineHeight: Integer);
|
||||
var
|
||||
pEOL: PChar;
|
||||
savedSpaces: Integer;
|
||||
savedWidth: Integer;
|
||||
savedRtpIndex: Integer;
|
||||
maxWidth: Integer;
|
||||
rtState: TRtState;
|
||||
dw, h: Integer;
|
||||
fntpos: TsFontPosition;
|
||||
dw: Integer;
|
||||
spaceFound: Boolean;
|
||||
s: utf8String;
|
||||
charLen: Integer;
|
||||
charLen: Integer; // Number of bytes of current utf8 character
|
||||
begin
|
||||
NumSpaces := 0;
|
||||
|
||||
InitFont(p, rtState, PendingRtpIndex, h, fntpos);
|
||||
height := h;
|
||||
|
||||
pEOL := p;
|
||||
width := 0;
|
||||
ALineHeight := fontHeight;
|
||||
ALineWidth := 0;
|
||||
savedWidth := 0;
|
||||
savedSpaces := 0;
|
||||
savedRtpIndex := PendingRtpIndex;
|
||||
savedRtpIndex := ARtpFontIndex;
|
||||
spaceFound := false;
|
||||
pEOL := p;
|
||||
|
||||
if AWordwrap then
|
||||
begin
|
||||
if ARotation = trHorizontal then
|
||||
@@ -310,48 +301,55 @@ var
|
||||
maxWidth := MaxInt;
|
||||
|
||||
while p^ <> #0 do begin
|
||||
UpdateFont(p, rtState, PendingRtpIndex, h, fntpos);
|
||||
if h > height then height := h;
|
||||
UpdateFont(charPos, ARtpFontIndex, fontHeight, fontPos);
|
||||
ALineHeight := Max(fontHeight, ALineHeight);
|
||||
|
||||
s := UnicodeToUTF8(UTF8CharacterToUnicode(p, charLen));
|
||||
case p^ of
|
||||
' ': begin
|
||||
spaceFound := true;
|
||||
pEOL := p;
|
||||
savedWidth := width;
|
||||
savedWidth := ALineWidth;
|
||||
savedSpaces := NumSpaces;
|
||||
savedRtpIndex := PendingRtpIndex;
|
||||
dw := Math.IfThen(ARotation = rtStacked, h, ACanvas.TextWidth(s));
|
||||
if width + dw < MaxWidth then
|
||||
savedRtpIndex := ARtpFontIndex;
|
||||
dw := Math.IfThen(ARotation = rtStacked, fontHeight, ACanvas.TextWidth(s));
|
||||
if ALineWidth + dw < MaxWidth then
|
||||
begin
|
||||
inc(NumSpaces);
|
||||
width := width + dw;
|
||||
ALineWidth := ALineWidth + dw;
|
||||
end else
|
||||
break;
|
||||
end;
|
||||
#13,
|
||||
#13: begin
|
||||
inc(p);
|
||||
inc(charPos);
|
||||
if p^ = #10 then
|
||||
begin
|
||||
inc(p);
|
||||
inc(charPos);
|
||||
end;
|
||||
break;
|
||||
end;
|
||||
#10: begin
|
||||
// dec(p);
|
||||
//width := savedWidth;
|
||||
//numSpaces := savedspaces;
|
||||
//PendingRtpIndex := savedRtpIndex;
|
||||
exit;
|
||||
inc(p);
|
||||
inc(charPos);
|
||||
break;
|
||||
end;
|
||||
else begin
|
||||
dw := Math.IfThen(ARotation = rtStacked, h, ACanvas.TextWidth(s));
|
||||
width := width + dw;
|
||||
if width > maxWidth then
|
||||
dw := Math.IfThen(ARotation = rtStacked, fontHeight, ACanvas.TextWidth(s));
|
||||
ALineWidth := ALineWidth + dw;
|
||||
if ALineWidth > maxWidth then
|
||||
begin
|
||||
if spaceFound then
|
||||
begin
|
||||
p := pEOL;
|
||||
width := savedWidth;
|
||||
ALineWidth := savedWidth;
|
||||
NumSpaces := savedSpaces;
|
||||
PendingRtpIndex := savedRtpIndex;
|
||||
ARtpFontIndex := savedRtpIndex;
|
||||
end else
|
||||
begin
|
||||
width := width - dw;
|
||||
if width = 0 then
|
||||
ALineWidth := ALineWidth - dw;
|
||||
if ALineWidth = 0 then
|
||||
inc(p);
|
||||
end;
|
||||
break;
|
||||
@@ -360,53 +358,76 @@ var
|
||||
end;
|
||||
|
||||
inc(p, charLen);
|
||||
inc(charPos);
|
||||
end;
|
||||
UpdateFont(charPos, ARtpFontIndex, fontHeight, fontPos);
|
||||
end;
|
||||
|
||||
procedure DrawLine(pStart, pEnd: PChar; x,y, hLine: Integer; PendingRtpIndex: Integer);
|
||||
{ Paints the text between the pointers pStart and pEnd.
|
||||
Starting point for the text location is given by the coordinates x/y, i.e.
|
||||
text alignment is already corrected. In case of sub/superscripts, the
|
||||
characters reduced in size are shifted vertical to drawing direction by a
|
||||
fraction of the line height (ALineHeight).
|
||||
ARtpFontIndex is the index of the rich-text formatting param used to at line
|
||||
start for font selection; it will advance automatically along the line }
|
||||
procedure DrawLine(pStart, pEnd: PChar; x,y, ALineHeight: Integer;
|
||||
ARtpFontIndex: Integer);
|
||||
var
|
||||
p: PChar;
|
||||
rtState: TRtState;
|
||||
h, w: Integer;
|
||||
fntpos: TsFontPosition = fpNormal;
|
||||
w: Integer;
|
||||
s: utf8String;
|
||||
charLen: Integer;
|
||||
begin
|
||||
p := pStart;
|
||||
InitFont(p, rtState, PendingRtpIndex, h, fntpos);
|
||||
while p^ <> #0 do begin
|
||||
s := UnicodeToUTF8(UTF8CharacterToUnicode(p, charLen));
|
||||
UpdateFont(p, rtState, PendingRtpIndex, h, fntpos);
|
||||
UpdateFont(charPos, ARtpFontIndex, fontHeight, fontPos);
|
||||
if AOverrideTextColor <> clNone then
|
||||
ACanvas.Font.Color := AOverrideTextColor;
|
||||
case p^ of
|
||||
#10: begin
|
||||
inc(p);
|
||||
inc(charPos);
|
||||
break;
|
||||
end;
|
||||
#13: begin
|
||||
inc(p);
|
||||
inc(charPos);
|
||||
if p^ = #10 then begin
|
||||
inc(p);
|
||||
inc(charpos);
|
||||
end;
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
case ARotation of
|
||||
trHorizontal:
|
||||
begin
|
||||
ACanvas.Font.Orientation := 0;
|
||||
case fntpos of
|
||||
case fontpos of
|
||||
fpNormal : ACanvas.TextOut(x, y, s);
|
||||
fpSubscript : ACanvas.TextOut(x, y + hLine div 2, s);
|
||||
fpSuperscript: ACanvas.TextOut(x, y - hLine div 6, s);
|
||||
fpSubscript : ACanvas.TextOut(x, y + ALineHeight div 2, s);
|
||||
fpSuperscript: ACanvas.TextOut(x, y - ALineHeight div 6, s);
|
||||
end;
|
||||
inc(x, ACanvas.TextWidth(s));
|
||||
end;
|
||||
rt90DegreeClockwiseRotation:
|
||||
begin
|
||||
ACanvas.Font.Orientation := -900;
|
||||
case fntpos of
|
||||
case fontpos of
|
||||
fpNormal : ACanvas.TextOut(x, y, s);
|
||||
fpSubscript : ACanvas.TextOut(x - hLine div 2, y, s);
|
||||
fpSuperscript: ACanvas.TextOut(x + hLine div 6, y, s);
|
||||
fpSubscript : ACanvas.TextOut(x - ALineHeight div 2, y, s);
|
||||
fpSuperscript: ACanvas.TextOut(x + ALineHeight div 6, y, s);
|
||||
end;
|
||||
inc(y, ACanvas.TextWidth(s));
|
||||
end;
|
||||
rt90DegreeCounterClockwiseRotation:
|
||||
begin
|
||||
ACanvas.Font.Orientation := +900;
|
||||
case fntpos of
|
||||
case fontpos of
|
||||
fpNormal : ACanvas.TextOut(x, y, s);
|
||||
fpSubscript : ACanvas.TextOut(x + hLine div 2, y, s);
|
||||
fpSuperscript: ACanvas.TextOut(x - hLine div 6, y, s);
|
||||
fpSubscript : ACanvas.TextOut(x + ALineHeight div 2, y, s);
|
||||
fpSuperscript: ACanvas.TextOut(x - ALineHeight div 6, y, s);
|
||||
end;
|
||||
dec(y, ACanvas.TextWidth(s));
|
||||
end;
|
||||
@@ -415,18 +436,20 @@ var
|
||||
ACanvas.Font.Orientation := 0;
|
||||
w := ACanvas.TextWidth(s);
|
||||
// chars centered around x
|
||||
case fntpos of
|
||||
case fontpos of
|
||||
fpNormal : ACanvas.TextOut(x - w div 2, y, s);
|
||||
fpSubscript : ACanvas.TextOut(x - w div 2, y + hLine div 2, s);
|
||||
fpSuperscript: ACanvas.TextOut(x - w div 2, y - hLine div 6, s);
|
||||
fpSubscript : ACanvas.TextOut(x - w div 2, y + ALineHeight div 2, s);
|
||||
fpSuperscript: ACanvas.TextOut(x - w div 2, y - ALineHeight div 6, s);
|
||||
end;
|
||||
inc(y, h);
|
||||
inc(y, fontHeight);
|
||||
end;
|
||||
end;
|
||||
|
||||
inc(P, charLen);
|
||||
inc(charPos);
|
||||
if P >= PEnd then break;
|
||||
end;
|
||||
UpdateFont(charPos, ARtpFontIndex, fontHeight, fontPos);
|
||||
end;
|
||||
|
||||
begin
|
||||
@@ -435,56 +458,50 @@ begin
|
||||
|
||||
p := PChar(AText);
|
||||
pStartText := p; // first char of text
|
||||
|
||||
if (Length(ARichTextParams) > 0) then
|
||||
iRTP := 0
|
||||
else
|
||||
iRtp := -1;
|
||||
charPos := 1; // Counter for utf8 character position
|
||||
totalHeight := 0;
|
||||
linelen := 0;
|
||||
|
||||
Convert_sFont_to_Font(AWorkbook.GetFont(AFontIndex), ACanvas.Font);
|
||||
|
||||
if ARotation = rtStacked then
|
||||
stackPeriod := ACanvas.TextWidth('M') * 2;
|
||||
|
||||
// Get layout of lines:
|
||||
// "lineinfos" collect data on where lines start and end, their width and
|
||||
// (1) Get layout of lines
|
||||
// ======================
|
||||
// "lineinfos" collect data for where lines start and end, their width and
|
||||
// height, the rich-text parameter index range, and the number of spaces
|
||||
// (for text justification)
|
||||
InitFont(rtpIndex, fontheight, fontpos);
|
||||
repeat
|
||||
SetLength(lineInfos, Length(lineInfos)+1);
|
||||
with lineInfos[High(lineInfos)] do begin
|
||||
pStart := p;
|
||||
pEnd := p;
|
||||
FirstRtpIndex := iRtp;
|
||||
NextRtpIndex := iRtp;
|
||||
ScanLine(pEnd, NumSpaces, NextRtpIndex, Width, Height);
|
||||
BeginsWithFontOfRtpIndex := rtpIndex;
|
||||
ScanLine(pEnd, NumSpaces, rtpIndex, Width, Height);
|
||||
totalHeight := totalHeight + Height;
|
||||
linelen := Max(linelen, Width);
|
||||
iRtp := NextRtpIndex;
|
||||
p := pEnd;
|
||||
case p^ of
|
||||
' ': while (p^ <> #0) and (p^ = ' ') do inc(p);
|
||||
#13: begin
|
||||
inc(p);
|
||||
if p^ = #10 then inc(p);
|
||||
end;
|
||||
#10: inc(p);
|
||||
end;
|
||||
if p^ = ' ' then
|
||||
while (p^ <> #0) and (p^ = ' ') do begin
|
||||
inc(p);
|
||||
inc(charPos);
|
||||
end;
|
||||
end;
|
||||
until p^ = #0;
|
||||
|
||||
Width := linelen;
|
||||
AWidth := linelen;
|
||||
if ARotation = rtStacked then
|
||||
Height := Length(lineinfos) * stackperiod
|
||||
AHeight := Length(lineinfos) * stackperiod // to be understood horizontally
|
||||
else
|
||||
Height := totalHeight;
|
||||
AHeight := totalHeight;
|
||||
if AMeasureOnly then
|
||||
exit;
|
||||
|
||||
// Draw lines
|
||||
// 1/ get starting point of line
|
||||
// (2) Draw lines
|
||||
// ==============
|
||||
// 2a) get starting point of line
|
||||
// ------------------------------
|
||||
case ARotation of
|
||||
trHorizontal:
|
||||
case AVertAlignment of
|
||||
@@ -515,7 +532,10 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
// 2/ Draw line by line and respect text rotation
|
||||
// (2b) Draw line by line and respect text rotation
|
||||
// ------------------------------------------------
|
||||
charPos := 1; // Counter for utf8 character position
|
||||
InitFont(rtpIndex, fontheight, fontpos);
|
||||
for lineInfo in lineInfos do begin
|
||||
with lineInfo do
|
||||
begin
|
||||
@@ -528,7 +548,7 @@ begin
|
||||
haRight : xpos := ARect.Right - Width;
|
||||
haCenter : xpos := (ARect.Left + ARect.Right - Width) div 2;
|
||||
end;
|
||||
DrawLine(pStart, pEnd, xpos, ypos, Height, FirstRtpIndex);
|
||||
DrawLine(pStart, pEnd, xpos, ypos, Height, BeginsWithFontOfRtpIndex);
|
||||
inc(ypos, Height);
|
||||
end;
|
||||
rt90DegreeClockwiseRotation:
|
||||
@@ -538,7 +558,7 @@ begin
|
||||
vaBottom : ypos := ARect.Bottom - Width;
|
||||
vaCenter : ypos := (ARect.Top + ARect.Bottom - Width) div 2;
|
||||
end;
|
||||
DrawLine(pStart, pEnd, xpos, ypos, Height, FirstRtpIndex);
|
||||
DrawLine(pStart, pEnd, xpos, ypos, Height, BeginsWithFontOfRtpIndex);
|
||||
dec(xpos, Height);
|
||||
end;
|
||||
rt90DegreeCounterClockwiseRotation:
|
||||
@@ -548,7 +568,7 @@ begin
|
||||
vaBottom : ypos := ARect.Bottom;
|
||||
vaCenter : ypos := (ARect.Top + ARect.Bottom + Width) div 2;
|
||||
end;
|
||||
DrawLine(pStart, pEnd, xpos, ypos, Height, FirstRtpIndex);
|
||||
DrawLine(pStart, pEnd, xpos, ypos, Height, BeginsWithFontOfRtpIndex);
|
||||
inc(xpos, Height);
|
||||
end;
|
||||
rtStacked:
|
||||
@@ -558,7 +578,7 @@ begin
|
||||
vaBottom : ypos := ARect.Bottom - Width;
|
||||
vaCenter : ypos := (ARect.Top + ARect.Bottom - Width) div 2;
|
||||
end;
|
||||
DrawLine(pStart, pEnd, xpos, ypos, Height, FirstRtpIndex);
|
||||
DrawLine(pStart, pEnd, xpos, ypos, Height, BeginsWithFontOfRtpIndex);
|
||||
inc(xpos, stackPeriod);
|
||||
end;
|
||||
end;
|
||||
@@ -575,7 +595,7 @@ var
|
||||
begin
|
||||
InternalDrawRichText(ACanvas, AWorkbook, ARect, AText, AFontIndex,
|
||||
ARichTextParams, AWordWrap, AHorAlignment, AVertAlignment, ARotation,
|
||||
AOverrideTextColor, w, h, false);
|
||||
AOverrideTextColor, false, w, h);
|
||||
end;
|
||||
|
||||
function RichTextWidth(ACanvas: TCanvas; AWorkbook: TsWorkbook; AMaxRect: TRect;
|
||||
@@ -585,8 +605,8 @@ var
|
||||
h, w: Integer;
|
||||
begin
|
||||
InternalDrawRichText(ACanvas, AWorkbook, AMaxRect, AText, AFontIndex,
|
||||
ARichTextParams, AWordWrap, haLeft, vaTop, ATextRotation, clNone,
|
||||
w, h, true);
|
||||
ARichTextParams, AWordWrap, haLeft, vaTop, ATextRotation, clNone, true,
|
||||
w, h);
|
||||
case ATextRotation of
|
||||
trHorizontal, rtStacked:
|
||||
Result := w;
|
||||
@@ -602,8 +622,8 @@ var
|
||||
h, w: Integer;
|
||||
begin
|
||||
InternalDrawRichText(ACanvas, AWorkbook, AMaxRect, AText, AFontIndex,
|
||||
ARichTextParams, AWordWrap, haLeft, vaTop, ATextRotation, clNone,
|
||||
w, h, true);
|
||||
ARichTextParams, AWordWrap, haLeft, vaTop, ATextRotation, clNone, true,
|
||||
w, h);
|
||||
case ATextRotation of
|
||||
trHorizontal:
|
||||
Result := h;
|
||||
|
@@ -12,6 +12,11 @@ type
|
||||
|
||||
TBIFFDetailsEvent = procedure(Sender: TObject; ADetails: TStrings) of object;
|
||||
|
||||
TRichTextFormattingRun = packed record
|
||||
FirstIndex, fontIndex: Word;
|
||||
end;
|
||||
TRichTextFormattingRuns = array of TRichTextFormattingRun;
|
||||
|
||||
TBIFFGrid = class(TStringGrid)
|
||||
private
|
||||
FRecType: Word;
|
||||
@@ -133,7 +138,7 @@ type
|
||||
ACharCount: Integer; out AString: String; out ANumbytes: Integer); overload;
|
||||
procedure ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean;
|
||||
out AString: String; out ANumBytes: Integer;
|
||||
out ARichTextRuns: TsRichTextFormattingRuns;
|
||||
out ARichTextRuns: TRichTextFormattingRuns;
|
||||
out ABufIndexOfFirstRichTextRun: LongWord;
|
||||
IgnoreCompressedFlag: Boolean = false); overload;
|
||||
procedure ExtractString(ABufIndex: Integer; ALenbytes: Byte; AUnicode: Boolean;
|
||||
@@ -274,7 +279,7 @@ procedure TBIFFGrid.ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode:
|
||||
out AString: String; out ANumBytes: Integer;
|
||||
IgnoreCompressedFlag: Boolean = false);
|
||||
var
|
||||
rtfRuns: TsRichTextFormattingRuns;
|
||||
rtfRuns: TRichTextFormattingRuns;
|
||||
rtfIndex: LongWord;
|
||||
begin
|
||||
ExtractString(ABufIndex, ALenbytes, AUnicode, AString, ANumBytes,
|
||||
@@ -283,7 +288,7 @@ end;
|
||||
|
||||
procedure TBIFFGrid.ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean;
|
||||
out AString: String; out ANumBytes: Integer;
|
||||
out ARichTextRuns: TsRichTextFormattingRuns;
|
||||
out ARichTextRuns: TRichTextFormattingRuns;
|
||||
out ABufIndexOfFirstRichTextRun: LongWord;
|
||||
IgnoreCompressedFlag: Boolean = false);
|
||||
var
|
||||
@@ -1286,7 +1291,7 @@ var
|
||||
run: Integer;
|
||||
total2: Integer;
|
||||
optn: Byte;
|
||||
rtfRuns: TsRichTextFormattingRuns;
|
||||
rtfRuns: TRichTextFormattingRuns;
|
||||
rtfBufferIndex: LongWord;
|
||||
begin
|
||||
case FInfo of
|
||||
@@ -1896,7 +1901,7 @@ var
|
||||
ansiStr: AnsiString;
|
||||
s: String;
|
||||
i, n: Integer;
|
||||
rtfRuns: TsRichTextFormattingRuns;
|
||||
rtfRuns: TRichTextFormattingRuns;
|
||||
begin
|
||||
BeginUpdate;
|
||||
RowCount := FixedRows + 1000;
|
||||
@@ -5322,7 +5327,7 @@ var
|
||||
s: String;
|
||||
total1, total2: DWord;
|
||||
i, j, n: Integer;
|
||||
rtfRuns: TsRichTextFormattingRuns;
|
||||
rtfRuns: TRichTextFormattingRuns;
|
||||
rtfIndex: LongWord;
|
||||
w: Word;
|
||||
begin
|
||||
|
@@ -175,22 +175,22 @@ object MainForm: TMainForm
|
||||
OnSelection = HexGridSelection
|
||||
ColWidths = (
|
||||
28
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
21
|
||||
26
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
20
|
||||
24
|
||||
)
|
||||
Cells = (
|
||||
16
|
||||
@@ -261,22 +261,22 @@ object MainForm: TMainForm
|
||||
OnClick = GridClick
|
||||
OnSelection = AlphaGridSelection
|
||||
ColWidths = (
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
17
|
||||
18
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
)
|
||||
Cells = (
|
||||
16
|
||||
@@ -415,7 +415,6 @@ object MainForm: TMainForm
|
||||
Width = 419
|
||||
Align = alClient
|
||||
ButtonStyle = bsTriangle
|
||||
Colors.UnfocusedColor = clMedGray
|
||||
DefaultText = 'Node'
|
||||
Header.AutoSizeIndex = 4
|
||||
Header.Columns = <
|
||||
|
@@ -106,7 +106,7 @@ type
|
||||
procedure WriteFORMAT(AStream: TStream; ANumFormatStr: String;
|
||||
ANumFormatIndex: Integer); override;
|
||||
procedure WriteIndex(AStream: TStream);
|
||||
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
||||
procedure WriteLABEL(AStream: TStream; const ARow, ACol: Cardinal;
|
||||
const AValue: string; ACell: PCell); override;
|
||||
procedure WriteStringRecord(AStream: TStream; AString: String); override;
|
||||
procedure WriteStyle(AStream: TStream);
|
||||
@@ -519,22 +519,19 @@ end;
|
||||
|
||||
procedure TsSpreadBIFF5Reader.ReadRSTRING(AStream: TStream);
|
||||
var
|
||||
L: Word;
|
||||
L, i: Word;
|
||||
B: Byte;
|
||||
ARow, ACol: Cardinal;
|
||||
XF: Word;
|
||||
ansistr: ansistring;
|
||||
valueStr: UTF8String;
|
||||
cell: PCell;
|
||||
rtfRuns: TsRichTextFormattingRuns;
|
||||
rtfRuns: TBiff5_RichTextFormattingRuns;
|
||||
fntIndex: Integer;
|
||||
fnt: TsFont;
|
||||
begin
|
||||
ReadRowColXF(AStream, ARow, ACol, XF);
|
||||
|
||||
{ Byte String with 16-bit size }
|
||||
L := WordLEtoN(AStream.ReadWord);
|
||||
SetLength(ansistr, L);
|
||||
AStream.ReadBuffer(ansistr[1], L);
|
||||
|
||||
{ Create cell }
|
||||
if FIsVirtualMode then begin
|
||||
InitCell(ARow, ACol, FVirtualCell);
|
||||
@@ -542,21 +539,37 @@ begin
|
||||
end else
|
||||
cell := FWorksheet.AddCell(ARow, ACol);
|
||||
|
||||
{ Save the data }
|
||||
{ Read data string (Byte string with 16-bit length) }
|
||||
L := WordLEtoN(AStream.ReadWord);
|
||||
SetLength(ansistr, L);
|
||||
AStream.ReadBuffer(ansistr[1], L);
|
||||
|
||||
{ Save the data string to cell }
|
||||
valueStr := ConvertEncoding(ansistr, FCodePage, encodingUTF8);
|
||||
FWorksheet.WriteUTF8Text(cell, valuestr);
|
||||
|
||||
// Read rich-text formatting runs
|
||||
{ Read rich-text formatting runs }
|
||||
B := AStream.ReadByte;
|
||||
SetLength(cell^.RichTextParams, B);
|
||||
SetLength(rtfRuns, B);
|
||||
for L := 0 to B-1 do begin
|
||||
rtfRuns[L].FirstIndex := AStream.ReadByte; // Index of first formatted character
|
||||
rtfRuns[L].FontIndex := AStream.ReadByte; // Index of font used
|
||||
AStream.ReadBuffer(rtfRuns[0], B * SizeOf(TBiff5_RichTextFormattingRun));
|
||||
for i := 0 to B-1 do begin
|
||||
// Index of first formatted character; it is 0-based in file, but 1-based in fps
|
||||
cell^.RichTextParams[i].FirstIndex := rtfRuns[i].FirstIndex + 1;
|
||||
// Index of font used after this character. But be aware that the font index
|
||||
// in the file is different from the font index stored by the workbook.
|
||||
fntIndex := rtfRuns[i].FontIndex;
|
||||
fnt := TsFont(FFontList[fntIndex]);
|
||||
fntIndex := FWorkbook.FindFont(fnt.FontName, fnt.Size, fnt.Style,fnt.Color, fnt.Position);
|
||||
if fntIndex = -1 then
|
||||
fntIndex := FWorkbook.AddFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color, fnt.Position);
|
||||
cell^.RichTextParams[i].FontIndex := fntIndex;
|
||||
// Hyperlink index (not used here)
|
||||
cell^.RichTextParams[i].HyperlinkIndex := -1;
|
||||
end;
|
||||
|
||||
{ Add attributes to cell }
|
||||
ApplyCellFormatting(cell, XF);
|
||||
ApplyRichTextFormattingRuns(cell, rtfRuns);
|
||||
|
||||
if FIsVirtualMode then
|
||||
Workbook.OnReadCellData(Workbook, ARow, ACol, cell);
|
||||
@@ -1448,8 +1461,11 @@ end;
|
||||
|
||||
If the string length exceeds 255 bytes, the string will be truncated and
|
||||
an error message will be logged as a warning.
|
||||
|
||||
NOTE: This method is called for "normal" LABEL cells as well as for
|
||||
rich-text-formatted RSTRING cells.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsSpreadBIFF5Writer.WriteLabel(AStream: TStream; const ARow,
|
||||
procedure TsSpreadBIFF5Writer.WriteLABEL(AStream: TStream; const ARow,
|
||||
ACol: Cardinal; const AValue: string; ACell: PCell);
|
||||
const
|
||||
MAXBYTES = 255; // Limit for this BIFF5
|
||||
@@ -1458,11 +1474,9 @@ var
|
||||
AnsiValue: ansistring;
|
||||
rec: TBIFF5_LabelRecord;
|
||||
buf: array of byte;
|
||||
useRTF: Boolean;
|
||||
fmt: PsCellFormat;
|
||||
i, nRuns: Integer;
|
||||
rtParam: TsRichTextParam;
|
||||
rtfRuns: TBiff5_RichTextformattingRuns;
|
||||
rtfRuns: TBIFF5_RichTextFormattingRuns;
|
||||
fntIndex, cellFntIndex: Integer;
|
||||
begin
|
||||
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
|
||||
@@ -1489,83 +1503,15 @@ begin
|
||||
]);
|
||||
end;
|
||||
L := Length(AnsiValue);
|
||||
|
||||
useRTF := (Length(ACell^.RichTextParams) > 0);
|
||||
nRuns := Length(ACell^.RichTextParams);
|
||||
|
||||
{ BIFF record header }
|
||||
rec.RecordID := WordToLE(IfThen(useRTF, INT_EXCEL_ID_RSTRING, INT_EXCEL_ID_LABEL));
|
||||
rec.RecordSize := WordToLE(SizeOf(rec) - SizeOf(TsBIFFHeader) + L);
|
||||
rec.RecordID := WordToLE(IfThen(nRuns > 0, INT_EXCEL_ID_RSTRING, INT_EXCEL_ID_LABEL));
|
||||
rec.RecordSize := SizeOf(rec) - SizeOf(TsBIFFHeader) + L;
|
||||
if (nRuns > 0) then
|
||||
inc(rec.RecordSize, 1 + nRuns * SizeOf(TBiff5_RichTextFormattingRun));
|
||||
rec.RecordSize := WordToLE(rec.RecordSize);
|
||||
|
||||
{ Prepare rich-text formatting runs }
|
||||
if useRTF then
|
||||
begin
|
||||
fmt := FWorkbook.GetPointerToCellFormat(ACell^.FormatIndex);
|
||||
cellFntIndex := fmt^.FontIndex;
|
||||
if cellFntIndex >= 4 then inc(cellFntIndex);
|
||||
nRuns := 0;
|
||||
for i := 0 to High(ACell^.RichTextParams) do
|
||||
begin
|
||||
// formatted part according to RichTextParams
|
||||
rtParam := ACell^.RichTextParams[i];
|
||||
SetLength(rtfRuns, nRuns + 1);
|
||||
fntIndex := rtParam.FontIndex;
|
||||
if fntIndex >= 4 then
|
||||
inc(fntIndex); // Font #4 does not exist in BIFF
|
||||
rtfRuns[nRuns].FontIndex := WordLEToN(fntIndex);
|
||||
rtfRuns[nRuns].FirstIndex := WordLEToN(rtParam.StartIndex);
|
||||
inc(nRuns);
|
||||
// Unformatted part at end?
|
||||
if (rtParam.EndIndex < L) and (i = High(ACell^.RichTextParams)) then
|
||||
begin
|
||||
SetLength(rtfRuns, nRuns + 1);
|
||||
rtfRuns[nRuns].FontIndex := WordLEToN(cellFntIndex);
|
||||
rtfRuns[nRuns].FirstIndex := WordLEToN(rtParam.EndIndex);
|
||||
inc(nRuns);
|
||||
end else
|
||||
// Unformatted part between two formatted parts?
|
||||
if (i < High(ACell^.RichTextParams)) and (rtParam.EndIndex < ACell^.RichTextParams[i+1].StartIndex) then
|
||||
begin
|
||||
SetLengtH(rtfRuns, nRuns + 1);
|
||||
rtfRuns[nRuns].FontIndex := WordLEToN(cellFntIndex);
|
||||
rtfRuns[nRuns].FirstIndex := WordLEToN(rtParam.EndIndex);
|
||||
inc(nRuns);
|
||||
end;
|
||||
end;
|
||||
|
||||
// Adjust BIFF record size for appended formatting runs
|
||||
inc(rec.RecordSize, SizeOf(word) + nRuns * SizeOf(TBiff5_RichTextFormattingRun));
|
||||
end;
|
||||
|
||||
(*
|
||||
{ Prepare rich-text formatting runs }
|
||||
if useRTF then
|
||||
begin
|
||||
fmt := FWorkbook.GetPointerToCellFormat(ACell^.FormatIndex);
|
||||
run := 0;
|
||||
for j:=0 to High(ACell^.RichTextParams) do
|
||||
begin
|
||||
SetLength(rtfRuns, run + 1);
|
||||
rtfRuns[run].FirstIndex := ACell^.RichTextParams[j].StartIndex;
|
||||
rtfRuns[run].FontIndex := ACell^.RichTextParams[j].FontIndex;
|
||||
if rtfRuns[run].FontIndex >= 4 then
|
||||
inc(rtfRuns[run].FontIndex); // Font #4 does not exist in BIFF
|
||||
inc(run);
|
||||
if (ACell^.RichTextParams[j].EndIndex < L) and
|
||||
(ACell^.RichTextParams[j].EndIndex <> ACell^.RichTextParams[j+1].StartIndex) // wp: j+1 needs to be checked!
|
||||
then begin
|
||||
SetLength(rtfRuns, run+1);
|
||||
rtfRuns[run].FirstIndex := ACell^.RichTextParams[j].EndIndex;
|
||||
rtfRuns[run].FontIndex := fmt^.FontIndex;
|
||||
if rtfRuns[run].FontIndex >= 4 then
|
||||
inc(rtfRuns[run].FontIndex);
|
||||
inc(run);
|
||||
end;
|
||||
end;
|
||||
|
||||
// Adjust BIFF record size for appended formatting runs
|
||||
inc(rec.RecordSize, SizeOf(byte) + run * SizeOf(TBiff5_RichTextFormattingRun));
|
||||
end;
|
||||
*)
|
||||
{ BIFF record data }
|
||||
rec.Row := WordToLE(ARow);
|
||||
rec.Col := WordToLE(ACol);
|
||||
@@ -1585,17 +1531,26 @@ begin
|
||||
AStream.WriteBuffer(buf[0], SizeOf(Rec) + L);
|
||||
|
||||
{ Write rich-text information in case of RSTRING record }
|
||||
if useRTF then
|
||||
if nRuns > 0 then
|
||||
begin
|
||||
{ Write number of rich-text formatting runs }
|
||||
AStream.WriteByte(nRuns);
|
||||
{ Write rich-text formatting runs }
|
||||
AStream.WriteBuffer(rtfRuns[0], nRuns * SizeOf(TBiff5_RichTextFormattingRun));
|
||||
SetLength(rtfRuns, nRuns);
|
||||
for i:=0 to nRuns-1 do
|
||||
begin
|
||||
// Char index where new font starts: 0-based in file, 1-based in fps
|
||||
rtfRuns[i].FirstIndex := ACell^.RichTextParams[i].FirstIndex - 1;
|
||||
// Index of new font. Be aware of font #4 missing in BIFF!
|
||||
if ACell^.RichTextParams[i].FontIndex >= 4 then
|
||||
rtfRuns[i].FontIndex := ACell^.RichTextParams[i].FontIndex + 1 else
|
||||
rtfRuns[i].FontIndex := ACell^.RichTextParams[i].FontIndex;
|
||||
end;
|
||||
AStream.WriteBuffer(rtfRuns[0], nRuns*SizeOf(TBiff5_RichTextFormattingRun));
|
||||
end;
|
||||
|
||||
{ Clean up }
|
||||
SetLength(buf, 0);
|
||||
SetLength(rtfRuns, 0);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
|
@@ -77,11 +77,11 @@ type
|
||||
FCommentLen: Integer;
|
||||
procedure ReadBoundsheet(AStream: TStream);
|
||||
function ReadString(const AStream: TStream; const ALength: Word;
|
||||
out ARichTextRuns: TsRichTextFormattingRuns): String;
|
||||
out ARichTextParams: TsRichTextParams): String;
|
||||
function ReadUnformattedWideString(const AStream: TStream;
|
||||
const ALength: Word): WideString;
|
||||
function ReadWideString(const AStream: TStream; const ALength: Word;
|
||||
out ARichTextRuns: TsRichTextFormattingRuns): WideString; overload;
|
||||
out ARichTextParams: TsRichTextParams): WideString; overload;
|
||||
function ReadWideString(const AStream: TStream; const AUse8BitLength: Boolean): WideString; overload;
|
||||
protected
|
||||
procedure PopulatePalette; override;
|
||||
@@ -471,10 +471,10 @@ procedure TsSpreadBIFF8Reader.ReadCONTINUE(const AStream: TStream);
|
||||
var
|
||||
commentStr: String;
|
||||
comment: TBIFF8Comment;
|
||||
rtRuns: TsRichTextFormattingRuns;
|
||||
rtParams: TsRichTextParams;
|
||||
begin
|
||||
if FCommentPending then begin
|
||||
commentStr := ReadWideString(AStream, FCommentLen, rtRuns);
|
||||
commentStr := ReadWideString(AStream, FCommentLen, rtParams);
|
||||
if commentStr <> '' then
|
||||
begin
|
||||
comment := TBIFF8Comment.Create;
|
||||
@@ -605,7 +605,7 @@ begin
|
||||
end;
|
||||
|
||||
function TsSpreadBIFF8Reader.ReadWideString(const AStream: TStream;
|
||||
const ALength: WORD; out ARichTextRuns: TsRichTextFormattingRuns): WideString;
|
||||
const ALength: WORD; out ARichTextParams: TsRichTextParams): WideString;
|
||||
var
|
||||
StringFlags: BYTE;
|
||||
DecomprStrValue: WideString;
|
||||
@@ -662,7 +662,7 @@ begin
|
||||
Raise Exception.Create('[TsSpreadBIFF8Reader.ReadWideString] CONTINUE record expected, but not found.');
|
||||
end else begin
|
||||
PendingRecordSize := RecordSize;
|
||||
DecomprStrValue := copy(DecomprStrValue,1,i) + ReadWideString(AStream, ALength-i, ARichTextRuns);
|
||||
DecomprStrValue := copy(DecomprStrValue,1,i) + ReadWideString(AStream, ALength-i, ARichTextParams);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
@@ -671,7 +671,7 @@ begin
|
||||
end;
|
||||
if StringFlags and 8 = 8 then begin
|
||||
// Rich string (This only occurs in BIFF8)
|
||||
SetLength(ARichTextRuns, RunsCounter);
|
||||
SetLength(ARichTextParams, RunsCounter);
|
||||
for j := 0 to SmallInt(RunsCounter) - 1 do begin
|
||||
if (PendingRecordSize <= 0) then begin
|
||||
// A CONTINUE may happened here
|
||||
@@ -683,8 +683,10 @@ begin
|
||||
PendingRecordSize := RecordSize;
|
||||
end;
|
||||
end;
|
||||
ARichTextRuns[j].FirstIndex := WordLEToN(AStream.ReadWord);
|
||||
ARichTextRuns[j].FontIndex := WordLEToN(AStream.ReadWord);
|
||||
// character start index: 0-based in file, 1-based in fps
|
||||
ARichTextParams[j].FirstIndex := WordLEToN(AStream.ReadWord) + 1;
|
||||
ARichTextParams[j].FontIndex := WordLEToN(AStream.ReadWord);
|
||||
ARichTextParams[j].HyperlinkIndex := -1;
|
||||
dec(PendingRecordSize, 2*2);
|
||||
end;
|
||||
end;
|
||||
@@ -701,14 +703,14 @@ function TsSpreadBIFF8Reader.ReadWideString(const AStream: TStream;
|
||||
const AUse8BitLength: Boolean): WideString;
|
||||
var
|
||||
Len: Word;
|
||||
rtRuns: TsRichTextFormattingRuns;
|
||||
rtParams: TsRichTextParams;
|
||||
begin
|
||||
if AUse8BitLength then
|
||||
Len := AStream.ReadByte()
|
||||
else
|
||||
Len := WordLEtoN(AStream.ReadWord());
|
||||
|
||||
Result := ReadWideString(AStream, Len, rtRuns);
|
||||
Result := ReadWideString(AStream, Len, rtParams);
|
||||
end;
|
||||
|
||||
procedure TsSpreadBIFF8Reader.ReadWorkbookGlobals(AStream: TStream);
|
||||
@@ -846,7 +848,7 @@ procedure TsSpreadBIFF8Reader.ReadBoundsheet(AStream: TStream);
|
||||
var
|
||||
Len: Byte;
|
||||
WideName: WideString;
|
||||
rtRuns: TsRichTextFormattingRuns;
|
||||
rtParams: TsRichTextParams;
|
||||
begin
|
||||
{ Absolute stream position of the BOF record of the sheet represented
|
||||
by this record }
|
||||
@@ -863,15 +865,15 @@ begin
|
||||
Len := AStream.ReadByte();
|
||||
|
||||
{ Read string with flags }
|
||||
WideName:=ReadWideString(AStream, Len, rtRuns);
|
||||
WideName:=ReadWideString(AStream, Len, rtParams);
|
||||
|
||||
FWorksheetNames.Add(UTF8Encode(WideName));
|
||||
end;
|
||||
|
||||
function TsSpreadBIFF8Reader.ReadString(const AStream: TStream;
|
||||
const ALength: WORD; out ARichTextRuns: TsRichTextFormattingRuns): String;
|
||||
const ALength: WORD; out ARichTextParams: TsRichTextParams): String;
|
||||
begin
|
||||
Result := UTF16ToUTF8(ReadWideString(AStream, ALength, ARichTextRuns));
|
||||
Result := UTF16ToUTF8(ReadWideString(AStream, ALength, ARichTextParams));
|
||||
end;
|
||||
(*
|
||||
procedure TsSpreadBIFF8Reader.ReadFromFile(AFileName: String);
|
||||
@@ -985,12 +987,14 @@ end;
|
||||
|
||||
procedure TsSpreadBIFF8Reader.ReadLABEL(AStream: TStream);
|
||||
var
|
||||
L: Word;
|
||||
L, i: Word;
|
||||
ARow, ACol: Cardinal;
|
||||
XF: Word;
|
||||
wideStrValue: WideString;
|
||||
cell: PCell;
|
||||
rtfRuns: TsRichTextFormattingRuns;
|
||||
rtParams: TsRichTextParams;
|
||||
fntIndex: Integer;
|
||||
fnt: TsFont;
|
||||
begin
|
||||
{ BIFF Record data: Row, Column, XF Index }
|
||||
ReadRowColXF(AStream, ARow, ACol, XF);
|
||||
@@ -999,7 +1003,7 @@ begin
|
||||
L := WordLEtoN(AStream.ReadWord());
|
||||
|
||||
{ Read wide string with flags }
|
||||
wideStrValue := ReadWideString(AStream, L, rtfRuns);
|
||||
wideStrValue := ReadWideString(AStream, L, rtParams);
|
||||
|
||||
{ Save the data }
|
||||
if FIsVirtualMode then begin
|
||||
@@ -1010,9 +1014,27 @@ begin
|
||||
|
||||
FWorksheet.WriteUTF8Text(cell, UTF16ToUTF8(wideStrValue));
|
||||
|
||||
{Add attributes}
|
||||
{ Add attributes }
|
||||
ApplyCellFormatting(cell, XF);
|
||||
ApplyRichTextFormattingRuns(cell, rtfRuns);
|
||||
|
||||
{ Apply rich-text formatting }
|
||||
if Length(rtParams) > 0 then begin
|
||||
SetLength(cell^.RichTextParams, Length(rtParams));
|
||||
for i := 0 to High(rtParams) do
|
||||
begin
|
||||
// Character index where format starts: 0-based in file, 1-based in fps
|
||||
cell^.RichTextParams[i].FirstIndex := rtParams[i].FirstIndex + 1;
|
||||
// Font index of new format - need to adjust index!
|
||||
fntIndex := rtParams[i].FontIndex;
|
||||
fnt := TsFont(FFontList[fntIndex]);
|
||||
fntIndex := FWorkbook.FindFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color, fnt.Position);
|
||||
if fntIndex = -1 then
|
||||
fntIndex := FWorkbook.AddFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color, fnt.Position);
|
||||
cell^.RichTextParams[i].FontIndex := fntIndex;
|
||||
// Hyperlink index, not used here
|
||||
cell^.RichTextParams[i].HyperlinkIndex := -1;
|
||||
end;
|
||||
end;
|
||||
|
||||
if FIsVirtualMode then
|
||||
Workbook.OnReadCellData(Workbook, ARow, ACol, cell);
|
||||
@@ -1187,44 +1209,57 @@ end;
|
||||
|
||||
procedure TsSpreadBIFF8Reader.ReadRSTRING(AStream: TStream);
|
||||
var
|
||||
j: Integer;
|
||||
L: Word;
|
||||
j, L: Word;
|
||||
ARow, ACol: Cardinal;
|
||||
XF: Word;
|
||||
wideStrValue: WideString;
|
||||
cell: PCell;
|
||||
rtfRuns: TsRichTextFormattingRuns;
|
||||
rtfRuns: TBiff8_RichTextFormattingRuns;
|
||||
fntIndex: Integer;
|
||||
fnt: TsFont;
|
||||
begin
|
||||
{ BIFF Record data: Row, Column, XF Index }
|
||||
ReadRowColXF(AStream, ARow, ACol, XF);
|
||||
|
||||
{ Byte String with 16-bit size }
|
||||
{ Data string: 16-bit length }
|
||||
L := WordLEtoN(AStream.ReadWord());
|
||||
|
||||
{ Read wide string without flags }
|
||||
{ Read wide string plus flag, but without processing it }
|
||||
wideStrValue := ReadUnformattedWideString(AStream, L);
|
||||
|
||||
{ Rich-tech formatting runs }
|
||||
L := WordLEToN(AStream.ReadWord);
|
||||
SetLength(rtfRuns, L);
|
||||
for j := 0 to L-1 do
|
||||
begin
|
||||
rtfRuns[j].FirstIndex := WordLEToN(AStream.ReadWord);
|
||||
rtfRuns[j].FontIndex := WordLEToN(AStream.ReadWord);
|
||||
end;
|
||||
|
||||
{ Save the data }
|
||||
{ Create cell }
|
||||
if FIsVirtualMode then begin
|
||||
InitCell(ARow, ACol, FVirtualCell); // "virtual" cell
|
||||
cell := @FVirtualCell;
|
||||
end else
|
||||
cell := FWorksheet.AddCell(ARow, ACol); // "real" cell
|
||||
|
||||
{ Save the data string}
|
||||
FWorksheet.WriteUTF8Text(cell, UTF16ToUTF8(wideStrValue));
|
||||
|
||||
{ Read rich-text formatting runs }
|
||||
L := WordLEToN(AStream.ReadWord);
|
||||
SetLength(cell^.RichTextParams, L);
|
||||
SetLength(rtfRuns, L);
|
||||
AStream.ReadBuffer(rtfRuns[0], L * SizeOf(TBiff8_RichTextFormattingRun));
|
||||
for j := 0 to L-1 do
|
||||
begin
|
||||
// Index of the font. Be aware that the index in the file is not
|
||||
// necessarily the same as the index used by the workbook!
|
||||
fntIndex := WordLEToN(rtfRuns[j].FontIndex);
|
||||
fnt := TsFont(FFontList[fntIndex]);
|
||||
fntIndex := FWorkbook.FindFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color, fnt.Position);
|
||||
if fntIndex = -1 then
|
||||
fntIndex := FWorkbook.AddFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color, fnt.Position);
|
||||
cell^.RichTextParams[j].FontIndex := fntIndex;
|
||||
// Index of the first character using this font: 0-based in file, 1-based in fps
|
||||
cell^.RichTextParams[j].FirstIndex := WordLEToN(rtfRuns[j].FirstIndex) + 1;
|
||||
// Hyperlink index - not used by biff
|
||||
cell^.RichTextParams[j].HyperlinkIndex := -1;
|
||||
end;
|
||||
|
||||
{Add attributes}
|
||||
ApplyCellFormatting(cell, XF);
|
||||
ApplyRichTextFormattingRuns(cell, rtfRuns);
|
||||
|
||||
if FIsVirtualMode then
|
||||
Workbook.OnReadCellData(Workbook, ARow, ACol, cell);
|
||||
@@ -1236,7 +1271,7 @@ var
|
||||
StringLength, CurStrLen: WORD;
|
||||
LString: String;
|
||||
ContinueIndicator: WORD;
|
||||
rtfRuns: TsRichTextFormattingRuns;
|
||||
rtParams: TsRichTextParams;
|
||||
ms: TMemoryStream;
|
||||
begin
|
||||
//Reads the shared string table, only compatible with BIFF8
|
||||
@@ -1265,10 +1300,8 @@ begin
|
||||
while PendingRecordSize > 0 do
|
||||
begin
|
||||
if StringLength > 0 then
|
||||
begin
|
||||
//Read a stream of zero length reads all the stream.
|
||||
LString := LString + ReadString(AStream, StringLength, rtfRuns);
|
||||
end
|
||||
LString := LString + ReadString(AStream, StringLength, rtParams)
|
||||
else
|
||||
begin
|
||||
//String of 0 chars in length, so just read it empty, reading only the mandatory flags
|
||||
@@ -1282,9 +1315,9 @@ begin
|
||||
begin
|
||||
//A Continue will happend, read the
|
||||
//tag and continue linking...
|
||||
ContinueIndicator:=WordLEtoN(AStream.ReadWord);
|
||||
if ContinueIndicator<>INT_EXCEL_ID_CONTINUE then begin
|
||||
Raise Exception.Create('[TsSpreadBIFF8Reader.ReadSST] Expected CONTINUE record not found.');
|
||||
ContinueIndicator := WordLEtoN(AStream.ReadWord);
|
||||
if ContinueIndicator <> INT_EXCEL_ID_CONTINUE then begin
|
||||
raise Exception.Create('[TsSpreadBIFF8Reader.ReadSST] Expected CONTINUE record not found.');
|
||||
end;
|
||||
PendingRecordSize := WordLEtoN(AStream.ReadWord);
|
||||
CurStrLen := Length(UTF8ToUTF16(LString));
|
||||
@@ -1297,13 +1330,13 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
if Length(rtfRuns) = 0 then
|
||||
if Length(rtParams) = 0 then
|
||||
FSharedStringTable.Add(LString)
|
||||
else
|
||||
begin
|
||||
ms := TMemoryStream.Create;
|
||||
ms.WriteWord(Length(rtfRuns));
|
||||
ms.WriteBuffer(rtfRuns[0], SizeOf(TsRichTextFormattingRun)*Length(rtfRuns));
|
||||
ms.WriteWord(Length(rtParams));
|
||||
ms.WriteBuffer(rtParams[0], SizeOf(TsRichTextParam)*Length(rtParams));
|
||||
ms.Position := 0;
|
||||
FSharedStringTable.AddObject(LString, ms);
|
||||
end;
|
||||
@@ -1324,13 +1357,15 @@ var
|
||||
rec: TBIFF8_LabelSSTRecord;
|
||||
cell: PCell;
|
||||
ms: TMemoryStream;
|
||||
rtfRuns: TsRichTextFormattingRuns;
|
||||
n: Integer;
|
||||
i, n: Integer;
|
||||
rtParams: TsRichTextParams;
|
||||
fnt: TsFont;
|
||||
fntIndex: Integer;
|
||||
begin
|
||||
rec.Row := 0; // to silence the compiler...
|
||||
|
||||
{ Read entire record, starting at Row }
|
||||
AStream.ReadBuffer(rec.Row, SizeOf(TBIFF8_LabelSSTRecord) - 2*SizeOf(Word));
|
||||
AStream.ReadBuffer(rec.Row, SizeOf(TBIFF8_LabelSSTRecord) - SizeOf(TsBiffHeader));
|
||||
ARow := WordLEToN(rec.Row);
|
||||
ACol := WordLEToN(rec.Col);
|
||||
XF := WordLEToN(rec.XFIndex);
|
||||
@@ -1357,10 +1392,21 @@ begin
|
||||
{ Add rich text formatting }
|
||||
ms := TMemoryStream(FSharedStringTable.Objects[SSTIndex]);
|
||||
if ms <> nil then begin
|
||||
n := ms.ReadWord;
|
||||
SetLength(rtfRuns, n);
|
||||
ms.ReadBuffer(rtfRuns[0], n*SizeOf(TsRichTextFormattingRun));
|
||||
ApplyRichTextFormattingRuns(cell, rtfRuns);
|
||||
n := WordLEToN(ms.ReadWord);
|
||||
SetLength(rtParams, n);
|
||||
ms.ReadBuffer(rtParams[0], n*SizeOf(TsRichTextParam));
|
||||
SetLength(cell^.RichTextParams, n);
|
||||
for i:=0 to n-1 do
|
||||
begin
|
||||
cell^.RichTextParams[i].FirstIndex := rtParams[i].FirstIndex;
|
||||
fntIndex := rtParams[i].FontIndex;
|
||||
fnt := TsFont(FFontList[fntIndex]);
|
||||
fntIndex := FWorkbook.FindFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color, fnt.Position);
|
||||
if fntIndex = -1 then
|
||||
fntIndex := FWorkbook.AddFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color, fnt.Position);
|
||||
cell^.RichTextParams[i].FontIndex := fntIndex;
|
||||
cell^.RichTextParams[i].HyperlinkIndex := -1;
|
||||
end;
|
||||
end;
|
||||
|
||||
if FIsVirtualMode then
|
||||
@@ -1584,7 +1630,7 @@ var
|
||||
lWeight: Word;
|
||||
Len: Byte;
|
||||
font: TsFont;
|
||||
rtfRuns: TsRichTextFormattingRuns;
|
||||
rtParams: TsRichTextParams;
|
||||
begin
|
||||
font := TsFont.Create;
|
||||
|
||||
@@ -1640,7 +1686,7 @@ begin
|
||||
|
||||
{ Font name: Unicodestring, char count in 1 byte }
|
||||
Len := AStream.ReadByte();
|
||||
font.FontName := ReadString(AStream, Len, rtfRuns); // rtfRuns is not used here.
|
||||
font.FontName := ReadString(AStream, Len, rtParams); // rtParams is not used here.
|
||||
|
||||
{ Add font to internal font list; will be transferred to workbook later because
|
||||
the font index in the internal list (= index in file) is not the same as the
|
||||
@@ -1688,13 +1734,13 @@ procedure TsSpreadBIFF8Reader.ReadHeaderFooter(AStream: TStream;
|
||||
var
|
||||
s: widestring;
|
||||
len: word;
|
||||
rtfRuns: TsRichTextFormattingRuns;
|
||||
rtParams: TsRichTextParams;
|
||||
begin
|
||||
if RecordSize = 0 then
|
||||
exit;
|
||||
|
||||
len := WordLEToN(AStream.ReadWord);
|
||||
s := ReadWideString(AStream, len, rtfRuns);
|
||||
s := ReadWideString(AStream, len, rtParams);
|
||||
if AIsHeader then
|
||||
FWorksheet.PageLayout.Headers[1] := UTF8Encode(s)
|
||||
else
|
||||
@@ -2596,85 +2642,42 @@ const
|
||||
MAXBYTES = 32758;
|
||||
var
|
||||
L: Word;
|
||||
WideValue: WideString;
|
||||
WideStr: WideString;
|
||||
rec: TBIFF8_LabelRecord;
|
||||
rtfRuns: TBiff8_RichTextFormattingRuns;
|
||||
rtParam: TsRichTextParam;
|
||||
buf: array of byte;
|
||||
i, nRuns: Integer;
|
||||
fmt: PsCellFormat;
|
||||
useRTF: Boolean;
|
||||
fntIndex: Word;
|
||||
cellfntIndex: Word;
|
||||
rtfRuns: TBiff8_RichTextFormattingRuns;
|
||||
begin
|
||||
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
|
||||
exit;
|
||||
|
||||
WideValue := UTF8Decode(FixLineEnding(AValue)); //to UTF16
|
||||
if WideValue = '' then begin
|
||||
WideStr := UTF8Decode(FixLineEnding(AValue)); //to UTF16
|
||||
if WideStr = '' then begin
|
||||
// Badly formatted UTF8String (maybe ANSI?)
|
||||
if Length(AValue)<>0 then begin
|
||||
//Quite sure it was an ANSI string written as UTF8, so raise exception.
|
||||
raise Exception.CreateFmt(rsUTF8TextExpectedButANSIFoundInCell, [GetCellString(ARow,ACol)]);
|
||||
raise Exception.CreateFmt(rsUTF8TextExpectedButANSIFoundInCell, [GetCellString(ARow, ACol)]);
|
||||
end;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if Length(WideValue) > MAXBYTES then begin
|
||||
if Length(WideStr) > MAXBYTES then begin // <-------- wp: Factor 2 missing? ---------
|
||||
// Rather than lose data when reading it, let the application programmer deal
|
||||
// with the problem or purposefully ignore it.
|
||||
SetLength(WideValue, MAXBYTES); //may corrupt the string (e.g. in surrogate pairs), but... too bad.
|
||||
SetLength(WideStr, MAXBYTES); //may corrupt the string (e.g. in surrogate pairs), but... too bad.
|
||||
Workbook.AddErrorMsg(rsTruncateTooLongCellText, [
|
||||
MAXBYTES, GetCellString(ARow, ACol)
|
||||
]);
|
||||
end;
|
||||
L := Length(WideValue);
|
||||
|
||||
useRTF := (Length(ACell^.RichTextParams) > 0);
|
||||
L := Length(WideStr);
|
||||
nRuns := Length(ACell^.RichTextParams);
|
||||
|
||||
{ BIFF record header }
|
||||
rec.RecordID := WordToLE(IfThen(useRTF, INT_EXCEL_ID_RSTRING, INT_EXCEL_ID_LABEL));
|
||||
rec.RecordSize := WordToLE(SizeOf(rec) - SizeOf(TsBIFFHeader) + L * SizeOf(WideChar));
|
||||
|
||||
{ Prepare rich-text formatting runs }
|
||||
if useRTF then
|
||||
begin
|
||||
fmt := FWorkbook.GetPointerToCellFormat(ACell^.FormatIndex);
|
||||
cellFntIndex := fmt^.FontIndex;
|
||||
if cellFntIndex >= 4 then inc(cellFntIndex);
|
||||
nRuns := 0;
|
||||
for i := 0 to High(ACell^.RichTextParams) do
|
||||
begin
|
||||
// formatted part according to RichTextParams
|
||||
rtParam := ACell^.RichTextParams[i];
|
||||
SetLength(rtfRuns, nRuns + 1);
|
||||
fntIndex := rtParam.FontIndex;
|
||||
if fntIndex >= 4 then
|
||||
inc(fntIndex); // Font #4 does not exist in BIFF
|
||||
rtfRuns[nRuns].FontIndex := WordLEToN(fntIndex);
|
||||
rtfRuns[nRuns].FirstIndex := WordLEToN(rtParam.StartIndex);
|
||||
inc(nRuns);
|
||||
// Unformatted part at end?
|
||||
if (rtParam.EndIndex < L) and (i = High(ACell^.RichTextParams)) then
|
||||
begin
|
||||
SetLength(rtfRuns, nRuns + 1);
|
||||
rtfRuns[nRuns].FontIndex := WordLEToN(cellFntIndex);
|
||||
rtfRuns[nRuns].FirstIndex := WordLEToN(rtParam.EndIndex);
|
||||
inc(nRuns);
|
||||
end else
|
||||
// Unformatted part between two formatted parts?
|
||||
if (i < High(ACell^.RichTextParams)) and (rtParam.EndIndex < ACell^.RichTextParams[i+1].StartIndex) then
|
||||
begin
|
||||
SetLengtH(rtfRuns, nRuns + 1);
|
||||
rtfRuns[nRuns].FontIndex := WordLEToN(cellFntIndex);
|
||||
rtfRuns[nRuns].FirstIndex := WordLEToN(rtParam.EndIndex);
|
||||
inc(nRuns);
|
||||
end;
|
||||
end;
|
||||
|
||||
// Adjust BIFF record size for appended formatting runs
|
||||
inc(rec.RecordSize, SizeOf(word) + nRuns * SizeOf(TBiff8_RichTextFormattingRun));
|
||||
end;
|
||||
rec.RecordID := WordToLE(IfThen(nRuns > 0, INT_EXCEL_ID_RSTRING, INT_EXCEL_ID_LABEL));
|
||||
rec.RecordSize := SizeOf(TBiff8_LabelRecord) - SizeOf(TsBiffHeader) + L *SizeOf(WideChar);
|
||||
if nRuns > 0 then
|
||||
inc(rec.RecordSize, SizeOf(Word) + nRuns * SizeOf(TBiff8_RichTextFormattingRun));
|
||||
rec.RecordSize := WordToLE(rec.RecordSize);
|
||||
|
||||
{ BIFF record data }
|
||||
rec.Row := WordToLE(ARow);
|
||||
@@ -2688,28 +2691,40 @@ begin
|
||||
|
||||
{ Byte flags }
|
||||
rec.TextFlags := 1; // means regular unicode LE encoding
|
||||
// Excel does not write the Rich-Text flag probably because rich-text info
|
||||
// is located differently in the RSTRING record.
|
||||
|
||||
{ Copy the text characters into a buffer immediately after rec }
|
||||
SetLength(buf, SizeOf(rec) + L*SizeOf(WideChar));
|
||||
Move(rec, buf[0], SizeOf(Rec));
|
||||
Move(WideStringToLE(WideValue)[1], buf[SizeOf(Rec)], L*SizeOf(WideChar));
|
||||
Move(WideStringToLE(WideStr)[1], buf[SizeOf(Rec)], L*SizeOf(WideChar));
|
||||
|
||||
{ Write out buffer }
|
||||
AStream.WriteBuffer(buf[0], SizeOf(rec) + L*SizeOf(WideChar));
|
||||
|
||||
{ Write rich-text information in case of RSTRING record }
|
||||
if useRTF then
|
||||
if (nRuns > 0) then
|
||||
begin
|
||||
{ Write number of rich-text formatting runs }
|
||||
AStream.WriteWord(WordToLE(nRuns));
|
||||
|
||||
{ Write array of rich-text formatting runs }
|
||||
SetLength(rtfRuns, nRuns);
|
||||
for i:=0 to nRuns-1 do
|
||||
begin
|
||||
// index of first character of formatted part, 0-based in file, 1-based in fps
|
||||
rtfRuns[i].FirstIndex := WordToLE(ACell^.RichTextParams[i].FirstIndex - 1);
|
||||
// Index of new font. Be aware of font #4 missing in BIFF!
|
||||
if ACell^.RichTextParams[i].FontIndex >= 4 then
|
||||
rtfRuns[i].FontIndex := WordToLE(ACell^.RichTextParams[i].FontIndex + 1) else
|
||||
rtfRuns[i].FontIndex := WordToLE(ACell^.RichTextParams[i].FontIndex);
|
||||
end;
|
||||
AStream.WriteBuffer(rtfRuns[0], nRuns * SizeOf(TBiff8_RichTextFormattingRun));
|
||||
end;
|
||||
|
||||
{ Clean up }
|
||||
SetLength(buf, 0);
|
||||
SetLength(rtfRuns, 0);
|
||||
SetLength(buf, 0);
|
||||
end;
|
||||
|
||||
procedure TsSpreadBIFF8Writer.WriteMergedCells(AStream: TStream;
|
||||
|
@@ -360,8 +360,10 @@ type
|
||||
|
||||
procedure AddBuiltinNumFormats; override;
|
||||
procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); virtual;
|
||||
(*
|
||||
procedure ApplyRichTextFormattingRuns(ACell: PCell;
|
||||
ARuns: TsRichTextFormattingRuns);
|
||||
*)
|
||||
// Extracts a number out of an RK value
|
||||
function DecodeRKValue(const ARK: DWORD): Double;
|
||||
// Returns the numberformat for a given XF record
|
||||
@@ -863,7 +865,7 @@ begin
|
||||
ACell^.FormatIndex := 0;
|
||||
end;
|
||||
end;
|
||||
|
||||
(*
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Converts the rich-text formatting run data as read from the file to the
|
||||
internal format used by the cell.
|
||||
@@ -903,7 +905,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
*)
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Extracts a number out of an RK value.
|
||||
Valid since BIFF3.
|
||||
|
@@ -566,8 +566,6 @@ var
|
||||
numFmt: TsNumFormatParams = nil;
|
||||
ms: TMemoryStream;
|
||||
n: Integer;
|
||||
rtp: TsRichTextParam;
|
||||
richTextParams: TsRichTextParams;
|
||||
begin
|
||||
if ANode = nil then
|
||||
exit;
|
||||
@@ -672,29 +670,15 @@ begin
|
||||
if s = 's' then begin
|
||||
// String from shared strings table
|
||||
sstIndex := StrToInt(dataStr);
|
||||
// Standard cell, no rich-text parameters
|
||||
if FSharedStrings.Objects[sstIndex] = nil then
|
||||
AWorksheet.WriteUTF8Text(cell, FSharedStrings[sstIndex])
|
||||
else
|
||||
AWorksheet.WriteUTF8Text(cell, FSharedStrings[sstIndex]);
|
||||
// Read rich-text parameters from the stream stored in the Objects of the stringlist
|
||||
if FSharedStrings.Objects[sstIndex] <> nil then
|
||||
begin
|
||||
// Read rich-text parameters from the stream stored in the Objects of the stringlist
|
||||
ms := TMemoryStream(FSharedStrings.Objects[sstIndex]);
|
||||
ms.Position := 0;
|
||||
n := ms.ReadWord; // Count of array elements
|
||||
SetLength(richTextParams, 0);
|
||||
while (n > 0) do begin
|
||||
ms.ReadBuffer(rtp, SizeOf(TsRichTextParam));
|
||||
// Consider only those richtext parameters with font different from cell font
|
||||
if rtp.FontIndex <> fmt.FontIndex then begin
|
||||
SetLength(richTextParams, Length(richTextParams)+1);
|
||||
richTextParams[High(richTextParams)] := rtp;
|
||||
end;
|
||||
dec(n);
|
||||
end;
|
||||
AWorksheet.WriteUTF8Text(cell,
|
||||
FSharedStrings[sstIndex],
|
||||
richTextParams
|
||||
);
|
||||
SetLength(cell^.RichTextParams, n);
|
||||
ms.ReadBuffer(cell^.RichTextParams[0], n*SizeOf(TsRichTextParam));
|
||||
end;
|
||||
end else
|
||||
if (s = 'str') or (s = 'inlineStr') then
|
||||
@@ -1646,43 +1630,36 @@ procedure TsSpreadOOXMLReader.ReadSharedStrings(ANode: TDOMNode);
|
||||
var
|
||||
valuenode: TDOMNode;
|
||||
childnode: TDOMNode;
|
||||
innernode: TDOMNode;
|
||||
nodename: String;
|
||||
s, sval: String;
|
||||
fntIndex, startIndex, count: Integer;
|
||||
richTextParams: TsRichTextParams;
|
||||
totaltxt, sval: String;
|
||||
fntIndex: Integer;
|
||||
rtParams: TsRichTextParams;
|
||||
ms: TMemoryStream;
|
||||
fnt: TsFont;
|
||||
begin
|
||||
while Assigned(ANode) do begin
|
||||
if ANode.NodeName = 'si' then begin
|
||||
s := '';
|
||||
richTextParams := nil;
|
||||
totaltxt := '';
|
||||
// rtParams := nil;
|
||||
SetLength(rtParams, 0);
|
||||
valuenode := ANode.FirstChild;
|
||||
while valuenode <> nil do begin
|
||||
nodename := valuenode.NodeName;
|
||||
if nodename = 't' then
|
||||
s := GetNodeValue(valuenode)
|
||||
// this is unformatted text
|
||||
totaltxt := GetNodeValue(valuenode)
|
||||
else
|
||||
if nodename = 'r' then begin
|
||||
// all rich-text formatted texts are defined by r nodes
|
||||
fntIndex := -1;
|
||||
startIndex := -1;
|
||||
count := -1;
|
||||
childnode := valuenode.FirstChild;
|
||||
while childnode <> nil do begin
|
||||
nodename := childnode.NodeName;
|
||||
if nodename = 't' then
|
||||
begin
|
||||
startIndex := Length(s);
|
||||
sval := GetNodevalue(childNode);
|
||||
s := s + sval;
|
||||
count := Length(sval);
|
||||
if fntIndex <> -1 then
|
||||
begin
|
||||
SetLength(richTextParams, Length(richTextParams)+1);
|
||||
richTextParams[Length(richTextParams)-1].StartIndex := startIndex;
|
||||
richTextParams[Length(richTextParams)-1].EndIndex := startIndex + count;
|
||||
richTextParams[Length(richTextParams)-1].FontIndex := fntIndex;
|
||||
end;
|
||||
sval := GetNodeValue(childNode);
|
||||
totaltxt := totaltxt + sval;
|
||||
end
|
||||
else if nodename = 'rPr' then begin
|
||||
fntIndex := ReadFont(childnode);
|
||||
@@ -1693,26 +1670,24 @@ begin
|
||||
fntIndex := Workbook.FindFont(fnt.FontName, fnt.Size, fnt.style, fnt.Color, fnt.Position);
|
||||
if fntIndex = -1 then
|
||||
fntIndex := Workbook.AddFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color, fnt.Position);
|
||||
if startIndex <> -1 then begin
|
||||
SetLength(richTextParams, Length(richTextParams)+1);
|
||||
richTextParams[Length(richTextParams)-1].StartIndex := startIndex;
|
||||
richTextParams[Length(richTextParams)-1].EndIndex := startIndex + count;
|
||||
richTextParams[Length(richTextParams)-1].FontIndex := fntIndex;
|
||||
end;
|
||||
SetLength(rtParams, Length(rtParams)+1);
|
||||
rtParams[High(rtParams)].FirstIndex := UTF8Length(totaltxt) + 1;
|
||||
rtParams[High(rtParams)].FontIndex := fntIndex;
|
||||
rtParams[High(rtParams)].HyperlinkIndex := -1;
|
||||
end;
|
||||
childnode := childnode.NextSibling;
|
||||
end;
|
||||
end;
|
||||
valuenode := valuenode.NextSibling;
|
||||
end;
|
||||
if Length(richTextParams) = 0 then
|
||||
FSharedStrings.Add(s)
|
||||
if Length(rtParams) = 0 then
|
||||
FSharedStrings.Add(totaltxt)
|
||||
else
|
||||
begin
|
||||
ms := TMemoryStream.Create;
|
||||
ms.WriteWord(Length(richTextParams));
|
||||
ms.WriteBuffer(richTextParams[0], SizeOf(TsRichTextParam)*Length(richTextParams));
|
||||
FSharedStrings.AddObject(s, ms);
|
||||
ms.WriteWord(Length(rtParams));
|
||||
ms.WriteBuffer(rtParams[0], SizeOf(TsRichTextParam)*Length(rtParams));
|
||||
FSharedStrings.AddObject(totaltxt, ms);
|
||||
end;
|
||||
end;
|
||||
ANode := ANode.NextSibling;
|
||||
@@ -2319,19 +2294,6 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{
|
||||
// Index 1 is also pre-defined (gray 25%)
|
||||
for i:=2 to High(FFillList) do begin
|
||||
fmt := FFillList[i];
|
||||
if (fmt <> nil) and (uffBackgroundColor in fmt^.UsedFormattingFields) then
|
||||
if (AFormat^.BackgroundColor = fmt^.BackgroundColor) then
|
||||
begin
|
||||
Result := i;
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
}
|
||||
|
||||
// Not found --> return -1
|
||||
Result := -1;
|
||||
end;
|
||||
@@ -3852,6 +3814,7 @@ var
|
||||
CellValueText: String;
|
||||
lStyleIndex: Integer;
|
||||
begin
|
||||
Unused(AValue);
|
||||
CellPosText := TsWorksheet.CellPosToText(ARow, ACol);
|
||||
lStyleIndex := GetStyleIndex(ACell);
|
||||
CellValueText := GetErrorValueStr(ACell^.ErrorValue);
|
||||
@@ -3934,9 +3897,7 @@ var
|
||||
lStyleIndex: Cardinal;
|
||||
ResultingValue: string;
|
||||
fnt: TsFont;
|
||||
n: Integer;
|
||||
i: Integer;
|
||||
L: Integer;
|
||||
i, n, L: Integer;
|
||||
rtParam: TsRichTextParam;
|
||||
txt: String;
|
||||
begin
|
||||
@@ -3951,6 +3912,7 @@ begin
|
||||
else
|
||||
ResultingValue := AValue;
|
||||
|
||||
{ Check for invalid characters }
|
||||
txt := ResultingValue;
|
||||
if not ValidXMLText(txt) then
|
||||
Workbook.AddErrorMsg(
|
||||
@@ -3959,7 +3921,6 @@ begin
|
||||
]);
|
||||
|
||||
{ Write string to SharedString table }
|
||||
|
||||
if Length(ACell^.RichTextParams) = 0 then
|
||||
// unformatted string
|
||||
AppendToStream(FSSharedStrings,
|
||||
@@ -3969,13 +3930,15 @@ begin
|
||||
else
|
||||
begin
|
||||
// rich-text formatted string
|
||||
FixLineEndings(ResultingValue, ACell^.RichTextParams);
|
||||
L := UTF8Length(Resultingvalue);
|
||||
AppendToStream(FSSharedStrings,
|
||||
'<si>');
|
||||
rtParam := ACell^.RichTextParams[0];
|
||||
if rtParam.StartIndex > 0 then
|
||||
if rtParam.FirstIndex > 1 then
|
||||
begin
|
||||
txt := UTF8Copy(ResultingValue, 1, rtParam.StartIndex);
|
||||
// Unformatted part before first format
|
||||
txt := UTF8Copy(ResultingValue, 1, rtParam.FirstIndex - 1);
|
||||
ValidXMLText(txt);
|
||||
AppendToStream(FSSharedStrings,
|
||||
'<r>' +
|
||||
@@ -3987,8 +3950,12 @@ begin
|
||||
begin
|
||||
rtParam := ACell^.RichTextParams[i];
|
||||
fnt := FWorkbook.GetFont(rtParam.FontIndex);
|
||||
n := rtParam.EndIndex - rtParam.StartIndex;
|
||||
txt := UTF8Copy(Resultingvalue, rtParam.StartIndex+1, n);
|
||||
// Calculate count of characters in this format section
|
||||
if i = High(ACell^.RichTextParams) then
|
||||
n := L - rtParam.FirstIndex + 1 else
|
||||
n := ACell^.RichTextParams[i+1].FirstIndex - rtParam.FirstIndex;
|
||||
// Partial string having this format
|
||||
txt := UTF8Copy(Resultingvalue, rtParam.FirstIndex, n);
|
||||
ValidXMLText(txt);
|
||||
AppendToStream(FSSharedStrings,
|
||||
'<r>');
|
||||
@@ -3997,34 +3964,12 @@ begin
|
||||
'<t xml:space="preserve">' + txt + '</t>' +
|
||||
'</r>'
|
||||
);
|
||||
if (rtParam.EndIndex < L) and (i = High(ACell^.RichTextParams)) then
|
||||
begin
|
||||
txt := UTF8Copy(ResultingValue, rtParam.EndIndex+1, MaxInt);
|
||||
ValidXMLText(txt);
|
||||
AppendToStream(FSSharedStrings,
|
||||
'<r>' +
|
||||
'<t xml:space="preserve">' + txt + '</t>' +
|
||||
'</r>'
|
||||
)
|
||||
end else
|
||||
if (i < High(ACell^.RichTextParams)) and (rtParam.EndIndex < ACell^.RichTextParams[i+1].StartIndex)
|
||||
then begin
|
||||
n := ACell^.RichTextParams[i+1].StartIndex - rtParam.EndIndex;
|
||||
txt := UTF8Copy(Resultingvalue, rtParam.EndIndex+1, n);
|
||||
ValidXMLText(txt);
|
||||
AppendToStream(FSSharedStrings,
|
||||
'<r>' +
|
||||
'<t xml:space="preserve">' + txt + '</t>' +
|
||||
'</r>'
|
||||
);
|
||||
end;
|
||||
end;
|
||||
AppendToStream(FSSharedStrings,
|
||||
'</si>');
|
||||
end;
|
||||
|
||||
{ Write shared string index to cell record }
|
||||
|
||||
CellPosText := TsWorksheet.CellPosToText(ARow, ACol);
|
||||
lStyleIndex := GetStyleIndex(ACell);
|
||||
AppendToStream(AStream, Format(
|
||||
|
Reference in New Issue
Block a user