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:
wp_xxyyzz
2015-07-09 15:29:44 +00:00
parent 9b89738158
commit 5b319c45bf
4 changed files with 117 additions and 39 deletions

View File

@ -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);

View File

@ -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;
if ARotation = rtStacked then
begin
Convert_sFont_to_Font(AWorkbook.GetFont(AFontIndex), ACanvas.Font); Convert_sFont_to_Font(AWorkbook.GetFont(AFontIndex), ACanvas.Font);
if ARotation = rtStacked then
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

View File

@ -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);

View File

@ -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
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}