You've already forked lazarus-ccr
fpspreadsheet: Fix rich-text ignoring non-ansi characters. Beginning to handle rich-text formatting runs when reading biff5 files.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4204 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -431,6 +431,15 @@ type
|
|||||||
{@@ Parameters describing formatting of text ranges in cell text }
|
{@@ Parameters describing formatting of text ranges in cell text }
|
||||||
TsRichTextParams = array of TsRichTextParam;
|
TsRichTextParams = array of TsRichTextParam;
|
||||||
|
|
||||||
|
{@@ Excel rich-text formatting run }
|
||||||
|
TsRichTextFormattingRun = record
|
||||||
|
FirstIndex: Integer;
|
||||||
|
FontIndex: Integer;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@ Array of Excel rich-text formatting runs }
|
||||||
|
TsRichTextFormattingRuns = array of TsRichTextFormattingRun;
|
||||||
|
|
||||||
{@@ Indicates the border for a cell. If included in the CellBorders set the
|
{@@ Indicates the border for a cell. If included in the CellBorders set the
|
||||||
corresponding border is drawn in the style defined by the CellBorderStyle. }
|
corresponding border is drawn in the style defined by the CellBorderStyle. }
|
||||||
TsCellBorder = (cbNorth, cbWest, cbEast, cbSouth, cbDiagUp, cbDiagDown);
|
TsCellBorder = (cbNorth, cbWest, cbEast, cbSouth, cbDiagUp, cbDiagDown);
|
||||||
|
@ -263,7 +263,6 @@ var
|
|||||||
procedure ScanLine(var P: PChar; var NumSpaces: Integer;
|
procedure ScanLine(var P: PChar; var NumSpaces: Integer;
|
||||||
var PendingRtpIndex: Integer; var width, height: Integer);
|
var PendingRtpIndex: Integer; var width, height: Integer);
|
||||||
var
|
var
|
||||||
ch: Char;
|
|
||||||
pEOL: PChar;
|
pEOL: PChar;
|
||||||
savedSpaces: Integer;
|
savedSpaces: Integer;
|
||||||
savedWidth: Integer;
|
savedWidth: Integer;
|
||||||
@ -273,6 +272,8 @@ var
|
|||||||
dw, h: Integer;
|
dw, h: Integer;
|
||||||
fntpos: TsFontPosition;
|
fntpos: TsFontPosition;
|
||||||
spaceFound: Boolean;
|
spaceFound: Boolean;
|
||||||
|
s: utf8String;
|
||||||
|
charLen: Integer;
|
||||||
begin
|
begin
|
||||||
NumSpaces := 0;
|
NumSpaces := 0;
|
||||||
|
|
||||||
@ -299,15 +300,15 @@ var
|
|||||||
UpdateFont(p, rtState, PendingRtpIndex, h, fntpos);
|
UpdateFont(p, rtState, PendingRtpIndex, h, fntpos);
|
||||||
if h > height then height := h;
|
if h > height then height := h;
|
||||||
|
|
||||||
ch := p^;
|
s := UnicodeToUTF8(UTF8CharacterToUnicode(p, charLen));
|
||||||
case ch of
|
case p^ of
|
||||||
' ': begin
|
' ': begin
|
||||||
spaceFound := true;
|
spaceFound := true;
|
||||||
pEOL := p;
|
pEOL := p;
|
||||||
savedWidth := width;
|
savedWidth := width;
|
||||||
savedSpaces := NumSpaces;
|
savedSpaces := NumSpaces;
|
||||||
savedRtpIndex := PendingRtpIndex;
|
savedRtpIndex := PendingRtpIndex;
|
||||||
dw := Math.IfThen(ARotation = rtStacked, h, ACanvas.TextWidth(ch));
|
dw := Math.IfThen(ARotation = rtStacked, h, ACanvas.TextWidth(s));
|
||||||
if width + dw < MaxWidth then
|
if width + dw < MaxWidth then
|
||||||
begin
|
begin
|
||||||
inc(NumSpaces);
|
inc(NumSpaces);
|
||||||
@ -324,7 +325,7 @@ var
|
|||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
else begin
|
else begin
|
||||||
dw := Math.IfThen(ARotation = rtStacked, h, ACanvas.TextWidth(ch));
|
dw := Math.IfThen(ARotation = rtStacked, h, ACanvas.TextWidth(s));
|
||||||
width := width + dw;
|
width := width + dw;
|
||||||
if width > maxWidth then
|
if width > maxWidth then
|
||||||
begin
|
begin
|
||||||
@ -345,69 +346,70 @@ var
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
inc(P, UTF8CharacterLength(p));
|
inc(p, charLen);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure DrawLine(pStart, pEnd: PChar; x,y, hLine: Integer; PendingRtpIndex: Integer);
|
procedure DrawLine(pStart, pEnd: PChar; x,y, hLine: Integer; PendingRtpIndex: Integer);
|
||||||
var
|
var
|
||||||
ch: Char;
|
|
||||||
p: PChar;
|
p: PChar;
|
||||||
rtState: TRtState;
|
rtState: TRtState;
|
||||||
h, w: Integer;
|
h, w: Integer;
|
||||||
fntpos: TsFontPosition;
|
fntpos: TsFontPosition;
|
||||||
|
s: utf8String;
|
||||||
|
charLen: Integer;
|
||||||
begin
|
begin
|
||||||
p := pStart;
|
p := pStart;
|
||||||
InitFont(p, rtState, PendingRtpIndex, h);
|
InitFont(p, rtState, PendingRtpIndex, h);
|
||||||
while p^ <> #0 do begin
|
while p^ <> #0 do begin
|
||||||
|
s := UnicodeToUTF8(UTF8CharacterToUnicode(p, charLen));
|
||||||
UpdateFont(p, rtState, PendingRtpIndex, h, fntpos);
|
UpdateFont(p, rtState, PendingRtpIndex, h, fntpos);
|
||||||
ch := p^;
|
|
||||||
case ARotation of
|
case ARotation of
|
||||||
trHorizontal:
|
trHorizontal:
|
||||||
begin
|
begin
|
||||||
ACanvas.Font.Orientation := 0;
|
ACanvas.Font.Orientation := 0;
|
||||||
case fntpos of
|
case fntpos of
|
||||||
fpNormal : ACanvas.TextOut(x, y, ch);
|
fpNormal : ACanvas.TextOut(x, y, s);
|
||||||
fpSubscript : ACanvas.TextOut(x, y + hLine div 2, ch);
|
fpSubscript : ACanvas.TextOut(x, y + hLine div 2, s);
|
||||||
fpSuperscript: ACanvas.TextOut(x, y - hLine div 6, ch);
|
fpSuperscript: ACanvas.TextOut(x, y - hLine div 6, s);
|
||||||
end;
|
end;
|
||||||
inc(x, ACanvas.TextWidth(ch));
|
inc(x, ACanvas.TextWidth(s));
|
||||||
end;
|
end;
|
||||||
rt90DegreeClockwiseRotation:
|
rt90DegreeClockwiseRotation:
|
||||||
begin
|
begin
|
||||||
ACanvas.Font.Orientation := -900;
|
ACanvas.Font.Orientation := -900;
|
||||||
case fntpos of
|
case fntpos of
|
||||||
fpNormal : ACanvas.TextOut(x, y, ch);
|
fpNormal : ACanvas.TextOut(x, y, s);
|
||||||
fpSubscript : ACanvas.TextOut(x - hLine div 2, y, ch);
|
fpSubscript : ACanvas.TextOut(x - hLine div 2, y, s);
|
||||||
fpSuperscript: ACanvas.TextOut(x + hLine div 6, y, ch);
|
fpSuperscript: ACanvas.TextOut(x + hLine div 6, y, s);
|
||||||
end;
|
end;
|
||||||
inc(y, ACanvas.TextWidth(ch));
|
inc(y, ACanvas.TextWidth(s));
|
||||||
end;
|
end;
|
||||||
rt90DegreeCounterClockwiseRotation:
|
rt90DegreeCounterClockwiseRotation:
|
||||||
begin
|
begin
|
||||||
ACanvas.Font.Orientation := +900;
|
ACanvas.Font.Orientation := +900;
|
||||||
case fntpos of
|
case fntpos of
|
||||||
fpNormal : ACanvas.TextOut(x, y, ch);
|
fpNormal : ACanvas.TextOut(x, y, s);
|
||||||
fpSubscript : ACanvas.TextOut(x + hLine div 2, y, ch);
|
fpSubscript : ACanvas.TextOut(x + hLine div 2, y, s);
|
||||||
fpSuperscript: ACanvas.TextOut(x - hLine div 6, y, ch);
|
fpSuperscript: ACanvas.TextOut(x - hLine div 6, y, s);
|
||||||
end;
|
end;
|
||||||
dec(y, ACanvas.TextWidth(ch));
|
dec(y, ACanvas.TextWidth(s));
|
||||||
end;
|
end;
|
||||||
rtStacked:
|
rtStacked:
|
||||||
begin
|
begin
|
||||||
ACanvas.Font.Orientation := 0;
|
ACanvas.Font.Orientation := 0;
|
||||||
w := ACanvas.TextWidth(ch);
|
w := ACanvas.TextWidth(s);
|
||||||
// chars centered around x
|
// chars centered around x
|
||||||
case fntpos of
|
case fntpos of
|
||||||
fpNormal : ACanvas.TextOut(x - w div 2, y, ch);
|
fpNormal : ACanvas.TextOut(x - w div 2, y, s);
|
||||||
fpSubscript : ACanvas.TextOut(x - w div 2, y + hLine div 2, ch);
|
fpSubscript : ACanvas.TextOut(x - w div 2, y + hLine div 2, s);
|
||||||
fpSuperscript: ACanvas.TextOut(x - w div 2, y - hLine div 6, ch);
|
fpSuperscript: ACanvas.TextOut(x - w div 2, y - hLine div 6, s);
|
||||||
end;
|
end;
|
||||||
inc(y, h);
|
inc(y, h);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
inc(P, UTF8CharacterLength(p));
|
inc(P, charLen);
|
||||||
if P >= PEnd then break;
|
if P >= PEnd then break;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -425,11 +427,10 @@ begin
|
|||||||
iRtp := -1;
|
iRtp := -1;
|
||||||
totalHeight := 0;
|
totalHeight := 0;
|
||||||
|
|
||||||
|
Convert_sFont_to_Font(AWorkbook.GetFont(AFontIndex), ACanvas.Font);
|
||||||
|
|
||||||
if ARotation = rtStacked then
|
if ARotation = rtStacked then
|
||||||
begin
|
|
||||||
Convert_sFont_to_Font(AWorkbook.GetFont(AFontIndex), ACanvas.Font);
|
|
||||||
stackPeriod := ACanvas.TextWidth('M') * 2;
|
stackPeriod := ACanvas.TextWidth('M') * 2;
|
||||||
end;
|
|
||||||
|
|
||||||
// Get layout of lines:
|
// Get layout of lines:
|
||||||
// "lineinfos" collect data on where lines start and end, their width and
|
// "lineinfos" collect data on where lines start and end, their width and
|
||||||
|
@ -516,18 +516,20 @@ end;
|
|||||||
procedure TsSpreadBIFF5Reader.ReadRichString(AStream: TStream);
|
procedure TsSpreadBIFF5Reader.ReadRichString(AStream: TStream);
|
||||||
var
|
var
|
||||||
L: Word;
|
L: Word;
|
||||||
B: BYTE;
|
B, F: Byte;
|
||||||
ARow, ACol: Cardinal;
|
ARow, ACol: Cardinal;
|
||||||
XF: Word;
|
XF: Word;
|
||||||
AStrValue: ansistring;
|
ansistr: ansistring;
|
||||||
|
valueStr: UTF8String;
|
||||||
cell: PCell;
|
cell: PCell;
|
||||||
|
rtfRuns: TsRichTextFormattingRuns;
|
||||||
begin
|
begin
|
||||||
ReadRowColXF(AStream, ARow, ACol, XF);
|
ReadRowColXF(AStream, ARow, ACol, XF);
|
||||||
|
|
||||||
{ Byte String with 16-bit size }
|
{ Byte String with 16-bit size }
|
||||||
L := WordLEtoN(AStream.ReadWord());
|
L := WordLEtoN(AStream.ReadWord);
|
||||||
SetLength(AStrValue,L);
|
SetLength(ansistr, L);
|
||||||
AStream.ReadBuffer(AStrValue[1], L);
|
AStream.ReadBuffer(ansistr[1], L);
|
||||||
|
|
||||||
{ Create cell }
|
{ Create cell }
|
||||||
if FIsVirtualMode then begin
|
if FIsVirtualMode then begin
|
||||||
@ -537,16 +539,20 @@ begin
|
|||||||
cell := FWorksheet.AddCell(ARow, ACol);
|
cell := FWorksheet.AddCell(ARow, ACol);
|
||||||
|
|
||||||
{ Save the data }
|
{ Save the data }
|
||||||
FWorksheet.WriteUTF8Text(cell, ISO_8859_1ToUTF8(AStrValue));
|
valueStr := ConvertEncoding(ansistr, FCodePage, encodingUTF8);
|
||||||
//Read formatting runs (not supported)
|
FWorksheet.WriteUTF8Text(cell, valuestr);
|
||||||
|
|
||||||
|
// Read rich-text formatting runs
|
||||||
B := AStream.ReadByte;
|
B := AStream.ReadByte;
|
||||||
|
SetLength(rtfRuns, B);
|
||||||
for L := 0 to B-1 do begin
|
for L := 0 to B-1 do begin
|
||||||
AStream.ReadByte; // First formatted character
|
rtfRuns[L].FirstIndex := AStream.ReadByte; // Index of first formatted character
|
||||||
AStream.ReadByte; // Index to FONT record
|
rtfRuns[L].FontIndex := AStream.ReadByte; // Index of font used
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Add attributes to cell }
|
{ Add attributes to cell }
|
||||||
ApplyCellFormatting(cell, XF);
|
ApplyCellFormatting(cell, XF);
|
||||||
|
ApplyRichTextFormattingRuns(cell, rtfRuns);
|
||||||
|
|
||||||
if FIsVirtualMode then
|
if FIsVirtualMode then
|
||||||
Workbook.OnReadCellData(Workbook, ARow, ACol, cell);
|
Workbook.OnReadCellData(Workbook, ARow, ACol, cell);
|
||||||
@ -626,7 +632,7 @@ var
|
|||||||
dw: DWord;
|
dw: DWord;
|
||||||
fill: Word;
|
fill: Word;
|
||||||
fs: TsFillStyle;
|
fs: TsFillStyle;
|
||||||
fnt: TsFont;
|
// fnt: TsFont;
|
||||||
begin
|
begin
|
||||||
InitFormatRecord(fmt);
|
InitFormatRecord(fmt);
|
||||||
fmt.ID := FCellFormatList.Count;
|
fmt.ID := FCellFormatList.Count;
|
||||||
@ -638,10 +644,13 @@ begin
|
|||||||
// Font index
|
// Font index
|
||||||
i := WordLEToN(rec.FontIndex);
|
i := WordLEToN(rec.FontIndex);
|
||||||
if i > 4 then dec(i); // Watch out for the nasty missing font #4...
|
if i > 4 then dec(i); // Watch out for the nasty missing font #4...
|
||||||
|
fmt.FontIndex := FixFontIndex(i);
|
||||||
|
{
|
||||||
fnt := TsFont(FFontList[i]);
|
fnt := TsFont(FFontList[i]);
|
||||||
fmt.FontIndex := Workbook.FindFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color);
|
fmt.FontIndex := Workbook.FindFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color);
|
||||||
if fmt.FontIndex = -1 then
|
if fmt.FontIndex = -1 then
|
||||||
fmt.FontIndex := Workbook.AddFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color);
|
fmt.FontIndex := Workbook.AddFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color);
|
||||||
|
}
|
||||||
if fmt.FontIndex > 1 then
|
if fmt.FontIndex > 1 then
|
||||||
Include(fmt.UsedFormattingFields, uffFont);
|
Include(fmt.UsedFormattingFields, uffFont);
|
||||||
|
|
||||||
|
@ -353,14 +353,18 @@ type
|
|||||||
FIncompleteNoteLength: Word;
|
FIncompleteNoteLength: Word;
|
||||||
FFirstNumFormatIndexInFile: Integer;
|
FFirstNumFormatIndexInFile: Integer;
|
||||||
FPalette: TsPalette;
|
FPalette: TsPalette;
|
||||||
|
|
||||||
procedure AddBuiltinNumFormats; override;
|
procedure AddBuiltinNumFormats; override;
|
||||||
procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); virtual; //overload;
|
procedure ApplyCellFormatting(ACell: PCell; XFIndex: Word); virtual;
|
||||||
|
procedure ApplyRichTextFormattingRuns(ACell: PCell;
|
||||||
|
ARuns: TsRichTextFormattingRuns);
|
||||||
// Extracts a number out of an RK value
|
// Extracts a number out of an RK value
|
||||||
function DecodeRKValue(const ARK: DWORD): Double;
|
function DecodeRKValue(const ARK: DWORD): Double;
|
||||||
// Returns the numberformat for a given XF record
|
// Returns the numberformat for a given XF record
|
||||||
procedure ExtractNumberFormat(AXFIndex: WORD;
|
procedure ExtractNumberFormat(AXFIndex: WORD;
|
||||||
out ANumberFormat: TsNumberFormat; out ANumberFormatStr: String); virtual;
|
out ANumberFormat: TsNumberFormat; out ANumberFormatStr: String); virtual;
|
||||||
procedure FixColors;
|
procedure FixColors;
|
||||||
|
function FixFontIndex(AFontIndex: Integer): Integer;
|
||||||
// Tries to find if a number cell is actually a date/datetime/time cell and retrieves the value
|
// Tries to find if a number cell is actually a date/datetime/time cell and retrieves the value
|
||||||
function IsDateTime(Number: Double; ANumberFormat: TsNumberFormat;
|
function IsDateTime(Number: Double; ANumberFormat: TsNumberFormat;
|
||||||
ANumberFormatStr: String; out ADateTime: TDateTime): Boolean;
|
ANumberFormatStr: String; out ADateTime: TDateTime): Boolean;
|
||||||
@ -837,6 +841,46 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Converts the rich-text formatting run data as read from the file to the
|
||||||
|
internal format used by the cell.
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
procedure TsSpreadBIFFReader.ApplyRichTextFormattingRuns(ACell: PCell;
|
||||||
|
ARuns: TsRichTextFormattingRuns);
|
||||||
|
var
|
||||||
|
fntIndex: Integer;
|
||||||
|
cellFntIndex: Integer;
|
||||||
|
cellStr: String;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
if Length(ARuns) = 0 then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
cellStr := ACell^.UTF8StringValue;
|
||||||
|
cellFntIndex := FWorksheet.ReadCellFontIndex(ACell);
|
||||||
|
|
||||||
|
SetLength(ACell^.RichTextParams, 0);
|
||||||
|
for i := 0 to High(ARuns) do begin
|
||||||
|
// Make sure that the fontindex defined in the formatting runs array points to
|
||||||
|
// the workbook's fontlist, not to the reader's fontlist.
|
||||||
|
fntIndex := FixFontIndex(ARuns[i].FontIndex);
|
||||||
|
// Ony fonts different from the cell's standard font are considered to be
|
||||||
|
// elements in the TsRichTextParams array used by the cell.
|
||||||
|
if fntIndex <> cellFntIndex then
|
||||||
|
begin
|
||||||
|
SetLength(ACell^.RichTextParams, Length(ACell^.RichTextParams)+1);
|
||||||
|
with ACell^.RichTextParams[High(ACell^.RichTextParams)] do
|
||||||
|
begin
|
||||||
|
FontIndex := fntIndex;
|
||||||
|
StartIndex := ARuns[i].FirstIndex;
|
||||||
|
if i < High(ARuns) then
|
||||||
|
EndIndex := ARuns[i+1].FirstIndex else
|
||||||
|
EndIndex := Length(cellStr);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Extracts a number out of an RK value.
|
Extracts a number out of an RK value.
|
||||||
Valid since BIFF3.
|
Valid since BIFF3.
|
||||||
@ -934,6 +978,21 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Converts the index of a font in the reader fontlist to the index of this font
|
||||||
|
in the workbook's fontlist. If the font is not yet contained in the workbook
|
||||||
|
fontlist it is added.
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
function TsSpreadBIFFReader.FixFontIndex(AFontIndex: Integer): Integer;
|
||||||
|
var
|
||||||
|
fnt: TsFont;
|
||||||
|
begin
|
||||||
|
fnt := TsFont(FFontList[AFontIndex]);
|
||||||
|
Result := FWorkbook.FindFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color, fnt.Position);
|
||||||
|
if Result = -1 then
|
||||||
|
Result := FWorkbook.AddFont(fnt.FontName, fnt.Size, fnt.Style, fnt.Color, fnt.Position);
|
||||||
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Converts the number to a date/time and return that if it is
|
Converts the number to a date/time and return that if it is
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
|
Reference in New Issue
Block a user