fpspreadsheet: Extend TsFont for sub-/superscripts. Initial implementation of "rich-text" cell format: display in WorksheetGrid and SpreadsheetInspector, read from xlsx, extend TsWorksheet.WriteUTF8Text() for rich-text formatting runs.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4203 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2015-07-09 11:10:15 +00:00
parent f80c3a9df0
commit 9b89738158
9 changed files with 798 additions and 88 deletions

View File

@@ -1525,7 +1525,7 @@ var
sfnt: TsFont; sfnt: TsFont;
begin begin
sfnt := TsFont.Create; sfnt := TsFont.Create;
Convert_Font_to_sFont(Workbook, GetDialog.Font, sfnt); Convert_Font_to_sFont(GetDialog.Font, sfnt);
Worksheet.WriteFont(ACell, Workbook.AddFont(sfnt)); Worksheet.WriteFont(ACell, Workbook.AddFont(sfnt));
end; end;
@@ -1550,7 +1550,7 @@ begin
else else
sfnt := Workbook.GetDefaultFont; sfnt := Workbook.GetDefaultFont;
end; end;
Convert_sFont_to_Font(Workbook, sfnt, fnt); Convert_sFont_to_Font(sfnt, fnt);
GetDialog.Font.Assign(fnt); GetDialog.Font.Assign(fnt);
finally finally
fnt.Free; fnt.Free;

View File

@@ -172,6 +172,7 @@ type
function ReadCellBorderStyle(ACell: PCell; ABorder: TsCellBorder): TsCellBorderStyle; function ReadCellBorderStyle(ACell: PCell; ABorder: TsCellBorder): TsCellBorderStyle;
function ReadCellBorderStyles(ACell: PCell): TsCellBorderStyles; function ReadCellBorderStyles(ACell: PCell): TsCellBorderStyles;
function ReadCellFont(ACell: PCell): TsFont; function ReadCellFont(ACell: PCell): TsFont;
function ReadCellFontIndex(ACell: PCell): Integer;
function ReadCellFormat(ACell: PCell): TsCellFormat; function ReadCellFormat(ACell: PCell): TsCellFormat;
function ReadHorAlignment(ACell: PCell): TsHorAlignment; function ReadHorAlignment(ACell: PCell): TsHorAlignment;
procedure ReadNumFormat(ACell: PCell; out ANumFormat: TsNumberFormat; procedure ReadNumFormat(ACell: PCell; out ANumFormat: TsNumberFormat;
@@ -239,7 +240,8 @@ type
AFormula: TsRPNFormula); overload; AFormula: TsRPNFormula); overload;
function WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring): PCell; overload; function WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring): PCell; overload;
procedure WriteUTF8Text(ACell: PCell; AText: ansistring); overload; // procedure WriteUTF8Text(ACell: PCell; AText: ansistring); overload;
procedure WriteUTF8Text(ACell: PCell; AText: String; ARichTextparams: TsRichTextParams = nil); overload;
{ Writing of cell attributes } { Writing of cell attributes }
function WriteBackground(ARow, ACol: Cardinal; AStyle: TsFillStyle; function WriteBackground(ARow, ACol: Cardinal; AStyle: TsFillStyle;
@@ -286,9 +288,11 @@ type
procedure WriteDecimals(ACell: PCell; ADecimals: Byte); overload; procedure WriteDecimals(ACell: PCell; ADecimals: Byte); overload;
function WriteFont(ARow, ACol: Cardinal; const AFontName: String; function WriteFont(ARow, ACol: Cardinal; const AFontName: String;
AFontSize: Single; AFontStyle: TsFontStyles; AFontColor: TsColor): Integer; overload; AFontSize: Single; AFontStyle: TsFontStyles; AFontColor: TsColor;
APosition: TsFontPosition = fpNormal): Integer; overload;
function WriteFont(ACell: PCell; const AFontName: String; function WriteFont(ACell: PCell; const AFontName: String;
AFontSize: Single; AFontStyle: TsFontStyles; AFontColor: TsColor): Integer; overload; AFontSize: Single; AFontStyle: TsFontStyles; AFontColor: TsColor;
APosition: TsFontPosition = fpNormal): Integer; overload;
function WriteFont(ARow, ACol: Cardinal; AFontIndex: Integer): PCell; overload; function WriteFont(ARow, ACol: Cardinal; AFontIndex: Integer): PCell; overload;
procedure WriteFont(ACell: PCell; AFontIndex: Integer); overload; procedure WriteFont(ACell: PCell; AFontIndex: Integer); overload;
function WriteFontColor(ARow, ACol: Cardinal; AFontColor: TsColor): Integer; overload; function WriteFontColor(ARow, ACol: Cardinal; AFontColor: TsColor): Integer; overload;
@@ -644,12 +648,12 @@ type
function GetPointerToCellFormat(AIndex: Integer): PsCellFormat; function GetPointerToCellFormat(AIndex: Integer): PsCellFormat;
{ Font handling } { Font handling }
function AddFont(const AFontName: String; ASize: Single; function AddFont(const AFontName: String; ASize: Single; AStyle: TsFontStyles;
AStyle: TsFontStyles; AColor: TsColor): Integer; overload; AColor: TsColor; APosition: TsFontPosition = fpNormal): Integer; overload;
function AddFont(const AFont: TsFont): Integer; overload; function AddFont(const AFont: TsFont): Integer; overload;
procedure DeleteFont(AFontIndex: Integer); procedure DeleteFont(AFontIndex: Integer);
function FindFont(const AFontName: String; ASize: Single; function FindFont(const AFontName: String; ASize: Single; AStyle: TsFontStyles;
AStyle: TsFontStyles; AColor: TsColor): Integer; AColor: TsColor; APosition: TsFontPosition = fpNormal): Integer;
function GetBuiltinFontCount: Integer; function GetBuiltinFontCount: Integer;
function GetDefaultFont: TsFont; function GetDefaultFont: TsFont;
function GetDefaultFontSize: Single; function GetDefaultFontSize: Single;
@@ -660,7 +664,8 @@ type
procedure InitFonts; procedure InitFonts;
procedure RemoveAllFonts; procedure RemoveAllFonts;
procedure ReplaceFont(AFontIndex: Integer; AFontName: String; procedure ReplaceFont(AFontIndex: Integer; AFontName: String;
ASize: Single; AStyle: TsFontStyles; AColor: TsColor); ASize: Single; AStyle: TsFontStyles; AColor: TsColor;
APosition: TsFontPosition = fpNormal);
procedure SetDefaultFont(const AFontName: String; ASize: Single); procedure SetDefaultFont(const AFontName: String; ASize: Single);
{ Number format handling } { Number format handling }
@@ -2710,8 +2715,7 @@ end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Determines the font used by a specified cell. Returns the workbook's default Determines the font used by a specified cell. Returns the workbook's default
font if the cell does not exist. Considers the uffBold and uffFont formatting font if the cell does not exist.
fields of the cell
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorksheet.ReadCellFont(ACell: PCell): TsFont; function TsWorksheet.ReadCellFont(ACell: PCell): TsFont;
var var
@@ -2727,6 +2731,23 @@ begin
Result := Workbook.GetDefaultFont; Result := Workbook.GetDefaultFont;
end; end;
{@@ ----------------------------------------------------------------------------
Determines the index of the font used by a specified cell, referring to the
workbooks font list. Returns 0 (the default font index) if the cell does not
exist.
-------------------------------------------------------------------------------}
function TsWorksheet.ReadCellFontIndex(ACell: PCell): Integer;
var
fmt: PsCellFormat;
begin
Result := DEFAULT_FONTINDEX;
if ACell <> nil then
begin
fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex);
Result := fmt^.FontIndex;
end;
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Returns the format record that is assigned to a specified cell Returns the format record that is assigned to a specified cell
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
@@ -3460,9 +3481,11 @@ end;
@param ACell Pointer to the cell @param ACell Pointer to the cell
@param AText The text to be written encoded in utf-8 @param AText The text to be written encoded in utf-8
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsWorksheet.WriteUTF8Text(ACell: PCell; AText: ansistring); procedure TsWorksheet.WriteUTF8Text(ACell: PCell; AText: String;
ARichTextParams: TsRichTextParams = nil);
var var
r, c: Cardinal; r, c: Cardinal;
i: Integer;
hyperlink: TsHyperlink; hyperlink: TsHyperlink;
begin begin
if ACell = nil then if ACell = nil then
@@ -3499,6 +3522,12 @@ begin
ACell^.ContentType := cctUTF8String; ACell^.ContentType := cctUTF8String;
ACell^.UTF8StringValue := AText; ACell^.UTF8StringValue := AText;
if Length(ARichTextParams) > 0 then begin
SetLength(ACell^.RichTextParams, Length(ARichTextParams));
for i:=0 to High(ARichTextParams) do
ACell^.RichTextParams[i] := ARichTextParams[i];
end;
ChangedCell(ACell^.Row, ACell^.Col); ChangedCell(ACell^.Row, ACell^.Col);
end; end;
@@ -4558,12 +4587,16 @@ end;
@param AFontSize Size of the font, in points @param AFontSize Size of the font, in points
@param AFontStyle Set with font style attributes @param AFontStyle Set with font style attributes
(don't use those of unit "graphics" !) (don't use those of unit "graphics" !)
@param AFontColor RGB value of the font's color
@param APosition Specifies sub- or superscript text
@return Index of the font in the workbook's font list. @return Index of the font in the workbook's font list.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorksheet.WriteFont(ARow, ACol: Cardinal; const AFontName: String; function TsWorksheet.WriteFont(ARow, ACol: Cardinal; const AFontName: String;
AFontSize: Single; AFontStyle: TsFontStyles; AFontColor: TsColor): Integer; AFontSize: Single; AFontStyle: TsFontStyles; AFontColor: TsColor;
APosition: TsFontPosition = fpNormal): Integer;
begin begin
Result := WriteFont(GetCell(ARow, ACol), AFontName, AFontSize, AFontStyle, AFontColor); Result := WriteFont(GetCell(ARow, ACol), AFontName, AFontSize, AFontStyle,
AFontColor, APosition);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@@ -4576,10 +4609,13 @@ end;
@param AFontSize Size of the font, in points @param AFontSize Size of the font, in points
@param AFontStyle Set with font style attributes @param AFontStyle Set with font style attributes
(don't use those of unit "graphics" !) (don't use those of unit "graphics" !)
@param AFontColor RGB value of the font's color
@param APosition Specified subscript or superscript text.
@return Index of the font in the workbook's font list. @return Index of the font in the workbook's font list.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorksheet.WriteFont(ACell: PCell; const AFontName: String; function TsWorksheet.WriteFont(ACell: PCell; const AFontName: String;
AFontSize: Single; AFontStyle: TsFontStyles; AFontColor: TsColor): Integer; AFontSize: Single; AFontStyle: TsFontStyles; AFontColor: TsColor;
APosition: TsFontPosition = fpNormal): Integer;
var var
fmt: TsCellFormat; fmt: TsCellFormat;
begin begin
@@ -4589,9 +4625,9 @@ begin
Exit; Exit;
end; end;
Result := FWorkbook.FindFont(AFontName, AFontSize, AFontStyle, AFontColor); Result := FWorkbook.FindFont(AFontName, AFontSize, AFontStyle, AFontColor, APosition);
if Result = -1 then if Result = -1 then
result := FWorkbook.AddFont(AFontName, AFontSize, AFontStyle, AFontColor); result := FWorkbook.AddFont(AFontName, AFontSize, AFontStyle, AFontColor, APosition);
fmt := Workbook.GetCellFormat(ACell^.FormatIndex); fmt := Workbook.GetCellFormat(ACell^.FormatIndex);
Include(fmt.UsedFormattingFields, uffFont); Include(fmt.UsedFormattingFields, uffFont);
@@ -7036,10 +7072,12 @@ end;
@param ASize Size of the font in points @param ASize Size of the font in points
@param AStyle Style of the font, a combination of TsFontStyle elements @param AStyle Style of the font, a combination of TsFontStyle elements
@param AColor RGB valoe of the font color @param AColor RGB valoe of the font color
@param APosition Specifies subscript or superscript text.
@return Index of the font in the workbook's font list @return Index of the font in the workbook's font list
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorkbook.AddFont(const AFontName: String; ASize: Single; function TsWorkbook.AddFont(const AFontName: String; ASize: Single;
AStyle: TsFontStyles; AColor: TsColor): Integer; AStyle: TsFontStyles; AColor: TsColor;
APosition: TsFontPosition = fpNormal): Integer;
var var
fnt: TsFont; fnt: TsFont;
begin begin
@@ -7048,6 +7086,7 @@ begin
fnt.Size := ASize; fnt.Size := ASize;
fnt.Style := AStyle; fnt.Style := AStyle;
fnt.Color := AColor; fnt.Color := AColor;
fnt.Position := APosition;
Result := AddFont(fnt); Result := AddFont(fnt);
end; end;
@@ -7088,10 +7127,11 @@ end;
@param ASize Size of the font in points @param ASize Size of the font in points
@param AStyle Style of the font, a combination of TsFontStyle elements @param AStyle Style of the font, a combination of TsFontStyle elements
@param AColor RGB value of the font color @param AColor RGB value of the font color
@param APosition Specified subscript or superscript text.
@return Index of the font in the font list, or -1 if not found. @return Index of the font in the font list, or -1 if not found.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorkbook.FindFont(const AFontName: String; ASize: Single; function TsWorkbook.FindFont(const AFontName: String; ASize: Single;
AStyle: TsFontStyles; AColor: TsColor): Integer; AStyle: TsFontStyles; AColor: TsColor; APosition: TsFontPosition = fpNormal): Integer;
const const
EPS = 1e-3; EPS = 1e-3;
var var
@@ -7104,7 +7144,8 @@ begin
SameText(AFontName, fnt.FontName) and SameText(AFontName, fnt.FontName) and
SameValue(ASize, fnt.Size, EPS) and // careful when comparing floating point numbers SameValue(ASize, fnt.Size, EPS) and // careful when comparing floating point numbers
(AStyle = fnt.Style) and (AStyle = fnt.Style) and
(AColor = fnt.Color) (AColor = fnt.Color) and
(APosition = fnt.Position)
then then
exit; exit;
end; end;
@@ -7164,17 +7205,19 @@ end;
Replaces the built-in font at a specific index with different font parameters Replaces the built-in font at a specific index with different font parameters
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsWorkbook.ReplaceFont(AFontIndex: Integer; AFontName: String; procedure TsWorkbook.ReplaceFont(AFontIndex: Integer; AFontName: String;
ASize: Single; AStyle: TsFontStyles; AColor: TsColor); ASize: Single; AStyle: TsFontStyles; AColor: TsColor;
APosition: TsFontPosition = fpNormal);
var var
fnt: TsFont; fnt: TsFont;
begin begin
if (AFontIndex < FBuiltinFontCount) and (AFontIndex <> 4) then if (AFontIndex < FBuiltinFontCount) then //and (AFontIndex <> 4) then
begin begin
fnt := TsFont(FFontList[AFontIndex]); fnt := TsFont(FFontList[AFontIndex]);
fnt.FontName := AFontName; fnt.FontName := AFontName;
fnt.Size := ASize; fnt.Size := ASize;
fnt.Style := AStyle; fnt.Style := AStyle;
fnt.Color := AColor; fnt.Color := AColor;
fnt.Position := APosition;
end; end;
end; end;
@@ -7255,6 +7298,8 @@ begin
if (fssItalic in fnt.Style) then Result := Result + '; italic'; if (fssItalic in fnt.Style) then Result := Result + '; italic';
if (fssUnderline in fnt.Style) then Result := Result + '; underline'; if (fssUnderline in fnt.Style) then Result := Result + '; underline';
if (fssStrikeout in fnt.Style) then result := Result + '; strikeout'; if (fssStrikeout in fnt.Style) then result := Result + '; strikeout';
if fnt.Position = fpSubscript then Result := Result + '; subscript';
if fnt.Position = fpSuperscript then Result := Result + '; superscript';
end else end else
Result := ''; Result := '';
end; end;

View File

@@ -2675,6 +2675,7 @@ var
r1, r2, c1, c2: Cardinal; r1, r2, c1, c2: Cardinal;
fmt: TsCellFormat; fmt: TsCellFormat;
numFmt: TsNumFormatParams; numFmt: TsNumFormatParams;
rtp: TsRichTextParam;
begin begin
if (ACell <> nil) then if (ACell <> nil) then
fmt := Workbook.GetCellFormat(ACell^.FormatIndex) fmt := Workbook.GetCellFormat(ACell^.FormatIndex)
@@ -2692,6 +2693,17 @@ begin
Workbook.GetFontAsString(fmt.FontIndex) Workbook.GetFontAsString(fmt.FontIndex)
])); ]));
if (ACell <> nil) and (Length(ACell^.RichTextParams) > 0) then
begin
s := '';
for rtp in ACell^.RichTextParams do
s := Format('%s; Font #%d @ %d-%d', [s, rtp.FontIndex, rtp.StartIndex, rtp.EndIndex]);
Delete(s, 1, 2);
if s = '' then s := '(none)';
AStrings.Add('Rich-text parameters='+s);
end else
AStrings.Add('Rich-text parameters=(none)');
if (ACell=nil) or not (uffTextRotation in fmt.UsedFormattingFields) if (ACell=nil) or not (uffTextRotation in fmt.UsedFormattingFields)
then AStrings.Add('TextRotation=(default)') then AStrings.Add('TextRotation=(default)')
else AStrings.Add(Format('TextRotation=%s', [ else AStrings.Add(Format('TextRotation=%s', [

View File

@@ -168,10 +168,16 @@ type
function GetEditText(ACol, ARow: Integer): String; override; function GetEditText(ACol, ARow: Integer): String; override;
function HasBorder(ACell: PCell; ABorder: TsCellBorder): Boolean; function HasBorder(ACell: PCell; ABorder: TsCellBorder): Boolean;
procedure HeaderSized(IsColumn: Boolean; AIndex: Integer); override; procedure HeaderSized(IsColumn: Boolean; AIndex: Integer); override;
procedure InternalDrawTextInCell(AText: String; ARect: TRect;
ACellHorAlign: TsHorAlignment; ACellVertAlign: TsVertAlignment;
ATextRot: TsTextRotation; ATextWrap: Boolean; AFontIndex: Integer;
ARichTextParams: TsRichTextParams);
{
procedure InternalDrawTextInCell(AText, AMeasureText: String; ARect: TRect; procedure InternalDrawTextInCell(AText, AMeasureText: String; ARect: TRect;
AJustification: Byte; ACellHorAlign: TsHorAlignment; AJustification: Byte; ACellHorAlign: TsHorAlignment;
ACellVertAlign: TsVertAlignment; ATextRot: TsTextRotation; ACellVertAlign: TsVertAlignment; ATextRot: TsTextRotation;
ATextWrap, ReplaceTooLong: Boolean); ATextWrap, ReplaceTooLong: Boolean; ARichTextParams: TsRichTextParams);
}
procedure KeyDown(var Key : Word; Shift : TShiftState); override; procedure KeyDown(var Key : Word; Shift : TShiftState); override;
procedure Loaded; override; procedure Loaded; override;
procedure LoadFromWorksheet(AWorksheet: TsWorksheet); procedure LoadFromWorksheet(AWorksheet: TsWorksheet);
@@ -1164,7 +1170,7 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsCustomWorksheetGrid.Convert_sFont_to_Font(sFont: TsFont; AFont: TFont); procedure TsCustomWorksheetGrid.Convert_sFont_to_Font(sFont: TsFont; AFont: TFont);
begin begin
fpsVisualUtils.Convert_sFont_to_Font(Workbook, sFont, AFont); fpsVisualUtils.Convert_sFont_to_Font(sFont, AFont);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@@ -1176,7 +1182,7 @@ end;
procedure TsCustomWorksheetGrid.Convert_Font_to_sFont(AFont: TFont; procedure TsCustomWorksheetGrid.Convert_Font_to_sFont(AFont: TFont;
sFont: TsFont); sFont: TsFont);
begin begin
fpsVisualUtils.Convert_Font_to_sFont(Workbook, AFont, sFont); fpsVisualUtils.Convert_Font_to_sFont(AFont, sFont);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@@ -2081,6 +2087,7 @@ var
horAlign: TsHorAlignment; horAlign: TsHorAlignment;
vertAlign: TsVertAlignment; vertAlign: TsVertAlignment;
txtRot: TsTextRotation; txtRot: TsTextRotation;
fntIndex: Integer;
lCell: PCell; lCell: PCell;
justif: Byte; justif: Byte;
fmt: PsCellFormat; fmt: PsCellFormat;
@@ -2110,10 +2117,29 @@ begin
// Cells // Cells
fmt := Workbook.GetPointerToCellFormat(lCell^.FormatIndex); fmt := Workbook.GetPointerToCellFormat(lCell^.FormatIndex);
wrapped := (uffWordWrap in fmt^.UsedFormattingFields) or (fmt^.TextRotation = rtStacked); wrapped := (uffWordWrap in fmt^.UsedFormattingFields) or (fmt^.TextRotation = rtStacked);
txtRot := fmt^.TextRotation; if (uffTextRotation in fmt^.UsedFormattingFields)
vertAlign := fmt^.VertAlignment; then txtRot := fmt^.TextRotation
if vertAlign = vaDefault then vertAlign := vaBottom; else txtRot := trHorizontal;
if fmt^.HorAlignment <> haDefault then if (uffVertAlign in fmt^.UsedFormattingFields)
then vertAlign := fmt^.VertAlignment
else vertAlign := vaDefault;
if vertAlign = vaDefault then
vertAlign := vaBottom;
if (uffHorAlign in fmt^.UsedFormattingFields)
then horAlign := fmt^.HorAlignment
else horAlign := haDefault;
if (horAlign = haDefault) then
begin
if (lCell^.ContentType in [cctNumber, cctDateTime]) then
horAlign := haRight
else
if (lCell^.ContentType in [cctBool]) then
horAlign := haCenter
else
horAlign := haLeft;
end;
{
fmt^.HorAlignment <> haDefault then
horAlign := fmt^.HorAlignment horAlign := fmt^.HorAlignment
else else
begin begin
@@ -2121,11 +2147,14 @@ begin
horAlign := haRight horAlign := haRight
else else
horAlign := haLeft; horAlign := haLeft;
end; end; }
if (uffFont in fmt^.UsedFormattingFields)
then fntIndex := fmt^.FontIndex
else fntIndex := DEFAULT_FONTINDEX;
InflateRect(ARect, -constCellPadding, -constCellPadding); InflateRect(ARect, -constCellPadding, -constCellPadding);
// txt := GetCellText(ACol, ARow);
txt := GetCellText(GetGridRow(lCell^.Col), GetGridCol(lCell^.Row)); txt := GetCellText(GetGridRow(lCell^.Col), GetGridCol(lCell^.Row));
if txt = '' then if txt = '' then
exit; exit;
@@ -2151,8 +2180,12 @@ begin
vaBottom: justif := 0; vaBottom: justif := 0;
end; end;
end; end;
InternalDrawTextInCell(txt, ARect, horAlign, vertAlign, txtRot, wrapped,
fntIndex, lCell^.RichTextParams);
{
InternalDrawTextInCell(txt, txt, ARect, justif, horAlign, vertAlign, InternalDrawTextInCell(txt, txt, ARect, justif, horAlign, vertAlign,
txtRot, wrapped, false); txtRot, wrapped, false, lCell^.RichTextParams);
}
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@@ -3084,18 +3117,34 @@ end;
@param ACellVertAlign Is the VertAlignment property stored in the cell @param ACellVertAlign Is the VertAlignment property stored in the cell
@param ATextRot Determines the rotation angle of the text. @param ATextRot Determines the rotation angle of the text.
@param ATextWrap Determines if the text can wrap into multiple lines @param ATextWrap Determines if the text can wrap into multiple lines
@param ReplaceTooLang If true too-long texts are replaced by a series of @param AFontIndex Font index to be used for drawing non-rich-text.
# chars filling the cell. @param ARichTextParams an array of character and font index combinations for
rich-text formatting of text in cell
@Note The reason to separate AJustification from ACellHorAlign and ACelVertAlign is @Note The reason to separate AJustification from ACellHorAlign and ACelVertAlign is
the output of nfAccounting formatted numbers where the numbers are always the output of nfAccounting formatted numbers where the numbers are always
right-aligned, and the currency symbol is left-aligned. right-aligned, and the currency symbol is left-aligned.
THIS FEATURE IS CURRENTLY NO LONGER SUPPORTED. THIS FEATURE IS CURRENTLY NO LONGER SUPPORTED.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsCustomWorksheetGrid.InternalDrawTextInCell(AText: String;
ARect: TRect; ACellHorAlign: TsHorAlignment; ACellVertAlign: TsVertAlignment;
ATextRot: TsTextRotation; ATextWrap: Boolean; AFontIndex: Integer;
ARichTextParams: TsRichTextParams);
begin
// Since - due to the rich-text mode - characters are drawn individually their
// background occasionally overpaints the prev characters (italic). To avoid
// this we do not paint the character background - it is not needed anyway.
Canvas.Brush.Style := bsClear;
// Work horse for text drawing, both standard text and rich-text
DrawRichText(Canvas, Workbook, ARect, AText, AFontIndex, ARichTextParams,
ATextWrap, ACellHorAlign, ACellVertAlign, ATextRot);
end;
(*
procedure TsCustomWorksheetGrid.InternalDrawTextInCell(AText, AMeasureText: String; procedure TsCustomWorksheetGrid.InternalDrawTextInCell(AText, AMeasureText: String;
ARect: TRect; AJustification: Byte; ACellHorAlign: TsHorAlignment; ARect: TRect; AJustification: Byte; ACellHorAlign: TsHorAlignment;
ACellVertAlign: TsVertAlignment; ATextRot: TsTextRotation; ACellVertAlign: TsVertAlignment; ATextRot: TsTextRotation;
ATextWrap, ReplaceTooLong: Boolean); ATextWrap, ReplaceTooLong: Boolean; ARichTextParams: TsRichTextParams);
var var
ts: TTextStyle; ts: TTextStyle;
flags: Cardinal; flags: Cardinal;
@@ -3281,7 +3330,7 @@ begin
end; end;
end; end;
end; end;
*)
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Standard key handling method inherited from TCustomGrid. Is overridden to Standard key handling method inherited from TCustomGrid. Is overridden to
catch the ESC key during editing in order to restore the old cell text catch the ESC key during editing in order to restore the old cell text

View File

@@ -403,6 +403,9 @@ type
{@@ Set of font styles } {@@ Set of font styles }
TsFontStyles = set of TsFontStyle; TsFontStyles = set of TsFontStyle;
{@@ Font position (subscript or superscript) }
TsFontPosition = (fpNormal, fpSubscript, fpSuperscript);
{@@ Font record used in fpspreadsheet. Contains the font name, the font size {@@ Font record used in fpspreadsheet. Contains the font name, the font size
(in points), the font style, and the font color. } (in points), the font style, and the font color. }
TsFont = class TsFont = class
@@ -414,8 +417,20 @@ type
Style: TsFontStyles; Style: TsFontStyles;
{@@ Text color given as rgb value } {@@ Text color given as rgb value }
Color: TsColor; Color: TsColor;
{@@ Text position }
Position: TsFontPosition;
end; end;
{@@ Parameter describing formatting of an text range in cell text }
TsRichTextParam = record
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;
{@@ 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);
@@ -583,7 +598,8 @@ type
{ Index of format record in the workbook's FCellFormatList } { Index of format record in the workbook's FCellFormatList }
FormatIndex: Integer; FormatIndex: Integer;
{ Cell content } { Cell content }
UTF8StringValue: String; // Strings cannot be part of a variant record UTF8StringValue: String; // Strings cannot be part of a variant record
RichTextParams: TsRichTextParams; // Formatting of individual text ranges
FormulaValue: String; FormulaValue: String;
case ContentType: TCellContentType of // variant part must be at the end case ContentType: TCellContentType of // variant part must be at the end
cctEmpty : (); // has no data at all cctEmpty : (); // has no data at all

View File

@@ -16,11 +16,23 @@ procedure Convert_Font_to_sFont(AWorkbook: TsWorkbook; AFont: TFont; sFont: TsFo
function WrapText(ACanvas: TCanvas; const AText: string; AMaxWidth: integer): string; function WrapText(ACanvas: TCanvas; const AText: string; AMaxWidth: integer): string;
procedure DrawRichText(ACanvas: TCanvas; AWorkbook: TsWorkbook; const ARect: TRect;
const AText: String; AFontIndex: Integer; ARichTextParams: TsRichTextParams;
AWordwrap: Boolean; AHorAlignment: TsHorAlignment; AVertAlignment: TsVertAlignment;
ARotation: TsTextRotation);
function RichTextWidth(ACanvas: TCanvas; AWorkbook: TsWorkbook; const AText: String;
AFontIndex: Integer; ARichTextParams: TsRichTextParams): Integer;
implementation implementation
uses uses
Types, LCLType, LCLIntf, fpsUtils; Types, Math, LCLType, LCLIntf, LazUTF8, fpsUtils;
const
{@@ Font size factor for sub-/superscript characters }
SUBSCRIPT_SUPERSCRIPT_FACTOR = 0.6;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Converts a spreadsheet font to a font used for painting (TCanvas.Font). Converts a spreadsheet font to a font used for painting (TCanvas.Font).
@@ -151,4 +163,444 @@ begin
end; end;
end; end;
procedure DrawRichText(ACanvas: TCanvas; AWorkbook: TsWorkbook; const ARect: TRect;
const AText: String; AFontIndex: Integer; ARichTextParams: TsRichTextParams;
AWordwrap: Boolean; AHorAlignment: TsHorAlignment; AVertAlignment: TsVertAlignment;
ARotation: TsTextRotation);
type
TLineInfo = record
pStart, pEnd: PChar;
NumSpaces: Integer;
FirstRtpIndex: Integer;
NextRtpIndex: Integer;
Width: Integer;
Height: Integer;
end;
TRtState = (rtEnter, rtExit);
var
xpos, ypos: Integer;
p, pStartText: PChar;
iRtp: Integer;
lineInfo: TLineInfo;
lineInfos: Array of TLineInfo = nil;
totalHeight, stackPeriod: Integer;
procedure InitFont(P: PChar; out rtState: TRtState;
PendingRtpIndex: Integer; out AHeight: Integer);
var
fnt: TsFont;
hasRtp: Boolean;
rtp: 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;
end;
Convert_sFont_to_Font(fnt, ACanvas.Font);
AHeight := ACanvas.TextHeight('Tg');
if (fnt <> nil) and (fnt.Position <> fpNormal) then
ACanvas.Font.Size := round(ACanvas.Font.Size * SUBSCRIPT_SUPERSCRIPT_FACTOR);
end;
procedure UpdateFont(P:PChar; var rtState: TRtState;
var PendingRtpIndex: Integer; var AHeight: Integer;
out AFontPos: TsFontPosition);
var
hasRtp: Boolean;
rtp: TsRichTextParam;
fnt: TsFont;
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(ACanvas.Font.Size * SUBSCRIPT_SUPERSCRIPT_FACTOR);
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(ACanvas.Font.Size * SUBSCRIPT_SUPERSCRIPT_FACTOR);
end;
end;
AFontPos := fnt.Position;
end;
procedure ScanLine(var P: PChar; var NumSpaces: Integer;
var PendingRtpIndex: Integer; var width, height: Integer);
var
ch: Char;
pEOL: PChar;
savedSpaces: Integer;
savedWidth: Integer;
savedRtpIndex: Integer;
maxWidth: Integer;
rtState: TRtState;
dw, h: Integer;
fntpos: TsFontPosition;
spaceFound: Boolean;
begin
NumSpaces := 0;
InitFont(p, rtState, PendingRtpIndex, h);
height := h;
pEOL := p;
width := 0;
savedWidth := 0;
savedSpaces := 0;
savedRtpIndex := PendingRtpIndex;
spaceFound := false;
if AWordwrap then
begin
if ARotation = trHorizontal then
maxWidth := ARect.Right - ARect.Left
else
maxWidth := ARect.Bottom - ARect.Top;
end
else
maxWidth := MaxInt;
while p^ <> #0 do begin
UpdateFont(p, rtState, PendingRtpIndex, h, fntpos);
if h > height then height := h;
ch := p^;
case ch of
' ': begin
spaceFound := true;
pEOL := p;
savedWidth := width;
savedSpaces := NumSpaces;
savedRtpIndex := PendingRtpIndex;
dw := Math.IfThen(ARotation = rtStacked, h, ACanvas.TextWidth(ch));
if width + dw < MaxWidth then
begin
inc(NumSpaces);
width := width + dw;
end else
break;
end;
#13,
#10: begin
dec(p);
width := savedWidth;
numSpaces := savedspaces;
PendingRtpIndex := savedRtpIndex;
exit;
end;
else begin
dw := Math.IfThen(ARotation = rtStacked, h, ACanvas.TextWidth(ch));
width := width + dw;
if width > maxWidth then
begin
if spaceFound then
begin
p := pEOL;
width := savedWidth;
NumSpaces := savedSpaces;
PendingRtpIndex := savedRtpIndex;
end else
begin
width := width - dw;
if width = 0 then
inc(p);
end;
break;
end;
end;
end;
inc(P, UTF8CharacterLength(p));
end;
end;
procedure DrawLine(pStart, pEnd: PChar; x,y, hLine: Integer; PendingRtpIndex: Integer);
var
ch: Char;
p: PChar;
rtState: TRtState;
h, w: Integer;
fntpos: TsFontPosition;
begin
p := pStart;
InitFont(p, rtState, PendingRtpIndex, h);
while p^ <> #0 do begin
UpdateFont(p, rtState, PendingRtpIndex, h, fntpos);
ch := p^;
case ARotation of
trHorizontal:
begin
ACanvas.Font.Orientation := 0;
case fntpos of
fpNormal : ACanvas.TextOut(x, y, ch);
fpSubscript : ACanvas.TextOut(x, y + hLine div 2, ch);
fpSuperscript: ACanvas.TextOut(x, y - hLine div 6, ch);
end;
inc(x, ACanvas.TextWidth(ch));
end;
rt90DegreeClockwiseRotation:
begin
ACanvas.Font.Orientation := -900;
case fntpos of
fpNormal : ACanvas.TextOut(x, y, ch);
fpSubscript : ACanvas.TextOut(x - hLine div 2, y, ch);
fpSuperscript: ACanvas.TextOut(x + hLine div 6, y, ch);
end;
inc(y, ACanvas.TextWidth(ch));
end;
rt90DegreeCounterClockwiseRotation:
begin
ACanvas.Font.Orientation := +900;
case fntpos of
fpNormal : ACanvas.TextOut(x, y, ch);
fpSubscript : ACanvas.TextOut(x + hLine div 2, y, ch);
fpSuperscript: ACanvas.TextOut(x - hLine div 6, y, ch);
end;
dec(y, ACanvas.TextWidth(ch));
end;
rtStacked:
begin
ACanvas.Font.Orientation := 0;
w := ACanvas.TextWidth(ch);
// chars centered around x
case fntpos of
fpNormal : ACanvas.TextOut(x - w div 2, y, ch);
fpSubscript : ACanvas.TextOut(x - w div 2, y + hLine div 2, ch);
fpSuperscript: ACanvas.TextOut(x - w div 2, y - hLine div 6, ch);
end;
inc(y, h);
end;
end;
inc(P, UTF8CharacterLength(p));
if P >= PEnd then break;
end;
end;
begin
if AText = '' then
exit;
p := PChar(AText);
pStartText := p; // first char of text
if (Length(ARichTextParams) > 0) then
iRTP := 0
else
iRtp := -1;
totalHeight := 0;
if ARotation = rtStacked then
begin
Convert_sFont_to_Font(AWorkbook.GetFont(AFontIndex), ACanvas.Font);
stackPeriod := ACanvas.TextWidth('M') * 2;
end;
// Get layout of lines:
// "lineinfos" collect data on where lines start and end, their width and
// height, the rich-text parameter index range, and the number of spaces
// (for text justification)
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);
if ARotation = rtStacked then
totalHeight := totalHeight + stackPeriod
else
totalHeight := totalHeight + Height;
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;
end;
until p^ = #0;
// Draw lines
case ARotation of
trHorizontal:
case AVertAlignment of
vaTop : ypos := ARect.Top;
vaBottom: ypos := ARect.Bottom - totalHeight;
vaCenter: ypos := (ARect.Top + ARect.Bottom - totalHeight) div 2;
end;
rt90DegreeClockwiseRotation:
case AHorAlignment of
haLeft : xpos := ARect.Left + totalHeight;
haRight : xpos := ARect.Right;
haCenter: xpos := (ARect.Left + ARect.Right + totalHeight) div 2;
end;
rt90DegreeCounterClockwiseRotation:
case AHorAlignment of
haLeft : xpos := ARect.Left;
haRight : xpos := ARect.Right - totalHeight;
haCenter: xpos := (ARect.Left + ARect.Right - totalHeight) div 2;
end;
rtStacked:
begin
case AHorAlignment of
haLeft : xpos := ARect.Left + stackPeriod div 2;
haRight : xpos := ARect.Right - totalHeight + stackPeriod div 2;
haCenter: xpos := (ARect.Left + ARect.Right - totalHeight) div 2;
end;
end;
end;
for lineInfo in lineInfos do begin
with lineInfo do
begin
p := pStart;
case ARotation of
trHorizontal:
begin
case AHorAlignment of
haLeft : xpos := ARect.Left;
haRight : xpos := ARect.Right - Width;
haCenter : xpos := (ARect.Left + ARect.Right - Width) div 2;
end;
DrawLine(pStart, pEnd, xpos, ypos, Height, FirstRtpIndex);
inc(ypos, Height);
end;
rt90DegreeClockwiseRotation:
begin
case AVertAlignment of
vaTop : ypos := ARect.Top;
vaBottom : ypos := ARect.Bottom - Width;
vaCenter : ypos := (ARect.Top + ARect.Bottom - Width) div 2;
end;
DrawLine(pStart, pEnd, xpos, ypos, Height, FirstRtpIndex);
dec(xpos, Height);
end;
rt90DegreeCounterClockwiseRotation:
begin
case AVertAlignment of
vaTop : ypos := ARect.Top + Width;
vaBottom : ypos := ARect.Bottom;
vaCenter : ypos := (ARect.Top + ARect.Bottom + Width) div 2;
end;
DrawLine(pStart, pEnd, xpos, ypos, Height, FirstRtpIndex);
inc(xpos, Height);
end;
rtStacked:
begin
case AVertAlignment of
vaTop : ypos := ARect.Top;
vaBottom : ypos := ARect.Bottom - Width;
vaCenter : ypos := (ARect.Top + ARect.Bottom - Width) div 2;
end;
DrawLine(pStart, pEnd, xpos, ypos, Height, FirstRtpIndex);
inc(xpos, stackPeriod);
end;
end;
end;
end;
end;
function RichTextWidth(ACanvas: TCanvas; AWorkbook: TsWorkbook; const AText: String;
AFontIndex: Integer; ARichTextParams: TsRichTextParams): Integer;
var
s: String;
p: Integer;
w, n: Integer;
rtp, next_rtp: TsRichTextParam;
fnt, fnt0: TsFont;
begin
Result := 0;
if (ACanvas=nil) or (AWorkbook=nil) or (AText = '') then exit;
fnt0 := AWorkbook.GetFont(AFontIndex);
Convert_sFont_to_Font(fnt0, ACanvas.Font);
if Length(ARichTextParams) = 0 then
begin
Result := ACanvas.TextWidth(AText);
if fnt0.Position <> fpNormal then
Result := Round(Result * SUBSCRIPT_SUPERSCRIPT_FACTOR);
exit;
end;
// Part with normal font before first rich-text parameter element
rtp := ARichTextParams[0];
if rtp.StartIndex > 0 then begin
s := copy(AText, 1, rtp.StartIndex+1); // StartIndex is 0-based
Result := ACanvas.TextWidth(s);
if fnt0.Position <> fpNormal then
Result := Round(Result * SUBSCRIPT_SUPERSCRIPT_FACTOR);
end;
p := 0;
while p < Length(ARichTextParams) do
begin
// Part with rich-text font
rtp := ARichTextParams[p];
fnt := AWorkbook.GetFont(rtp.FontIndex);
Convert_sFont_to_Font(fnt, ACanvas.Font);
s := copy(AText, rtp.StartIndex+1, rtp.EndIndex-rtp.StartIndex);
w := ACanvas.TextWidth(s);
if fnt.Position <> fpNormal then
w := Round(w * SUBSCRIPT_SUPERSCRIPT_FACTOR);
Result := Result + w;
// Part with normal font
if (p < High(ARichTextParams)-1) then
begin
next_rtp := ARichTextParams[p+1];
n := next_rtp.StartIndex - rtp.EndIndex;
if n > 0 then
begin
Convert_sFont_to_Font(fnt0, ACanvas.Font);
s := Copy(AText, rtp.EndIndex, n);
w := ACanvas.TextWidth(s);
if fnt0.Position <> fpNormal then
w := Round(w * SUBSCRIPT_SUPERSCRIPT_FACTOR);
Result := Result + w;
end;
end;
inc(p);
end;
end;
end. end.

View File

@@ -20,7 +20,8 @@
<CompilerMessages> <CompilerMessages>
<IgnoredMessages idx5028="True" idx4055="True" idx2005="True"/> <IgnoredMessages idx5028="True" idx4055="True" idx2005="True"/>
</CompilerMessages> </CompilerMessages>
<CustomOptions Value="$(IDEBuildOptions)"/> <CustomOptions Value="$(IDEBuildOptions)
-dDisableWrapperFunctions"/>
</Other> </Other>
</CompilerOptions> </CompilerOptions>
<Description Value="laz_fpspreadsheet is a non-visual component that allows you to use the fpspreadsheet package to read/write spreadsheet files in .xls (BIFF/Excel), .ods OpenDocument (LibreOffice/OpenOffice) and .xlsx Open XML (Excel) formats. <Description Value="laz_fpspreadsheet is a non-visual component that allows you to use the fpspreadsheet package to read/write spreadsheet files in .xls (BIFF/Excel), .ods OpenDocument (LibreOffice/OpenOffice) and .xlsx Open XML (Excel) formats.

View File

@@ -1547,7 +1547,7 @@ var
begin begin
// Paper size // Paper size
w := WordLEToN(AStream.ReadWord); w := WordLEToN(AStream.ReadWord);
if (w >= 0) and (w <= High(PAPER_SIZES)) then if (w <= High(PAPER_SIZES)) then
begin begin
FWorksheet.PageLayout.PageWidth := PAPER_SIZES[w, 0]; FWorksheet.PageLayout.PageWidth := PAPER_SIZES[w, 0];
FWorksheet.PageLayout.PageHeight := PAPER_SIZES[w, 1]; FWorksheet.PageLayout.PageHeight := PAPER_SIZES[w, 1];

View File

@@ -74,7 +74,7 @@ type
procedure ReadDateMode(ANode: TDOMNode); procedure ReadDateMode(ANode: TDOMNode);
procedure ReadFileVersion(ANode: TDOMNode); procedure ReadFileVersion(ANode: TDOMNode);
procedure ReadFills(ANode: TDOMNode); procedure ReadFills(ANode: TDOMNode);
procedure ReadFont(ANode: TDOMNode); function ReadFont(ANode: TDOMNode): Integer;
procedure ReadFonts(ANode: TDOMNode); procedure ReadFonts(ANode: TDOMNode);
procedure ReadHeaderFooter(ANode: TDOMNode; AWorksheet: TsWorksheet); procedure ReadHeaderFooter(ANode: TDOMNode; AWorksheet: TsWorksheet);
procedure ReadHyperlinks(ANode: TDOMNode); procedure ReadHyperlinks(ANode: TDOMNode);
@@ -130,6 +130,7 @@ type
procedure WriteComments(AWorksheet: TsWorksheet); procedure WriteComments(AWorksheet: TsWorksheet);
procedure WriteDimension(AStream: TStream; AWorksheet: TsWorksheet); procedure WriteDimension(AStream: TStream; AWorksheet: TsWorksheet);
procedure WriteFillList(AStream: TStream); procedure WriteFillList(AStream: TStream);
procedure WriteFont(AStream: TStream; AFont: TsFont; ATag: String);
procedure WriteFontList(AStream: TStream); procedure WriteFontList(AStream: TStream);
procedure WriteHeaderFooter(AStream: TStream; AWorksheet: TsWorksheet); procedure WriteHeaderFooter(AStream: TStream; AWorksheet: TsWorksheet);
procedure WriteHyperlinks(AStream: TStream; AWorksheet: TsWorksheet); procedure WriteHyperlinks(AStream: TStream; AWorksheet: TsWorksheet);
@@ -205,7 +206,7 @@ var
implementation implementation
uses uses
variants, fileutil, strutils, math, lazutf8, uriparser, variants, strutils, math, lazutf8, LazFileUtils, uriparser,
{%H-}fpsPatches, fpsStrings, fpsStreams, fpsNumFormatParser, fpsClasses; {%H-}fpsPatches, fpsStrings, fpsStreams, fpsNumFormatParser, fpsClasses;
const const
@@ -339,6 +340,8 @@ begin
for j := FHyperlinkList.Count-1 downto 0 do TObject(FHyperlinkList[j]).Free; for j := FHyperlinkList.Count-1 downto 0 do TObject(FHyperlinkList[j]).Free;
FHyperlinkList.Free; FHyperlinkList.Free;
for j := FSharedStrings.Count-1 downto 0 do
if FSharedstrings.Objects[j] <> nil then FSharedStrings.Objects[j].Free;
FSharedStrings.Free; FSharedStrings.Free;
FSharedFormulaBaseList.Free; // Don't free items, they are worksheet cells FSharedFormulaBaseList.Free; // Don't free items, they are worksheet cells
@@ -533,6 +536,10 @@ var
number: Double; number: Double;
fmt: TsCellFormat; fmt: TsCellFormat;
numFmt: TsNumFormatParams = nil; numFmt: TsNumFormatParams = nil;
ms: TMemoryStream;
n: Integer;
rtp: TsRichTextParam;
richTextParams: TsRichTextParams;
begin begin
if ANode = nil then if ANode = nil then
exit; exit;
@@ -626,7 +633,30 @@ begin
if s = 's' then begin if s = 's' then begin
// String from shared strings table // String from shared strings table
sstIndex := StrToInt(dataStr); sstIndex := StrToInt(dataStr);
AWorksheet.WriteUTF8Text(cell, FSharedStrings[sstIndex]); // Standard cell, no rich-text parameters
if FSharedStrings.Objects[sstIndex] = nil then
AWorksheet.WriteUTF8Text(cell, FSharedStrings[sstIndex])
else
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
);
end;
end else end else
if s = 'str' then if s = 'str' then
// literal string // literal string
@@ -1035,7 +1065,10 @@ begin
end; end;
end; end;
procedure TsSpreadOOXMLReader.ReadFont(ANode: TDOMNode); { Reads the font described by the specified node. If the node is already
contained in the font list the font's index is returned; otherwise the
new font is added to the list and its index is returned. }
function TsSpreadOOXMLReader.ReadFont(ANode: TDOMNode): Integer;
var var
node: TDOMNode; node: TDOMNode;
fnt: TsFont; fnt: TsFont;
@@ -1043,67 +1076,106 @@ var
fntSize: Single; fntSize: Single;
fntStyles: TsFontStyles; fntStyles: TsFontStyles;
fntColor: TsColor; fntColor: TsColor;
fntPos: TsFontPosition;
nodename: String; nodename: String;
s: String; s: String;
begin begin
fnt := Workbook.GetDefaultFont; fnt := Workbook.GetDefaultFont;
if fnt <> nil then begin if fnt <> nil then
begin
fntName := fnt.FontName; fntName := fnt.FontName;
fntSize := fnt.Size; fntSize := fnt.Size;
fntStyles := fnt.Style; fntStyles := fnt.Style;
fntColor := fnt.Color; fntColor := fnt.Color;
end else begin fntPos := fnt.Position;
end else
begin
fntName := DEFAULT_FONTNAME; fntName := DEFAULT_FONTNAME;
fntSize := DEFAULT_FONTSIZE; fntSize := DEFAULT_FONTSIZE;
fntStyles := []; fntStyles := [];
fntColor := scBlack; fntColor := scBlack;
fntPos := fpNormal;
end; end;
node := ANode.FirstChild; node := ANode.FirstChild;
while node <> nil do begin while node <> nil do
begin
nodename := node.NodeName; nodename := node.NodeName;
if nodename = 'name' then begin if (nodename = 'name') or (nodename = 'rFont') then
begin
s := GetAttrValue(node, 'val'); s := GetAttrValue(node, 'val');
if s <> '' then fntName := s; if s <> '' then fntName := s;
end end
else else
if nodename = 'sz' then begin if nodename = 'sz' then
begin
s := GetAttrValue(node, 'val'); s := GetAttrValue(node, 'val');
if s <> '' then fntSize := StrToFloat(s); if s <> '' then fntSize := StrToFloat(s);
end end
else else
if nodename = 'b' then begin if nodename = 'b' then
begin
if GetAttrValue(node, 'val') <> 'false' if GetAttrValue(node, 'val') <> 'false'
then fntStyles := fntStyles + [fssBold]; then fntStyles := fntStyles + [fssBold];
end end
else else
if nodename = 'i' then begin if nodename = 'i' then
begin
if GetAttrValue(node, 'val') <> 'false' if GetAttrValue(node, 'val') <> 'false'
then fntStyles := fntStyles + [fssItalic]; then fntStyles := fntStyles + [fssItalic];
end end
else else
if nodename = 'u' then begin if nodename = 'u' then
begin
if GetAttrValue(node, 'val') <> 'false' if GetAttrValue(node, 'val') <> 'false'
then fntStyles := fntStyles+ [fssUnderline] then fntStyles := fntStyles+ [fssUnderline]
end end
else else
if nodename = 'strike' then begin if nodename = 'strike' then
begin
if GetAttrValue(node, 'val') <> 'false' if GetAttrValue(node, 'val') <> 'false'
then fntStyles := fntStyles + [fssStrikeout]; then fntStyles := fntStyles + [fssStrikeout];
end end
else else
if nodename = 'vertAlign' then
begin
s := GetAttrValue(node, 'val');
if s = 'superscript' then
fntPos := fpSuperscript
else
if s = 'subscript' then
fntPos := fpSubscript
else
fntPos := fpNormal;
end
else
if nodename = 'color' then if nodename = 'color' then
fntColor := ReadColor(node); fntColor := ReadColor(node);
node := node.NextSibling; node := node.NextSibling;
end; end;
// Check whether font is already contained in font list
for Result := 0 to FFontList.Count-1 do
begin
fnt := TsFont(FFontList[Result]);
if (fnt.FontName = fntName) and
(fnt.Size = fntSize) and
(fnt.Style = fntStyles) and
(fnt.Color = fntColor) and
(fnt.Position = fntPos)
then
exit;
end;
// Font not yet stored --> create a new font and store it in list
fnt := TsFont.Create; fnt := TsFont.Create;
fnt.FontName := fntName; fnt.FontName := fntName;
fnt.Size := fntSize; fnt.Size := fntSize;
fnt.Style := fntStyles; fnt.Style := fntStyles;
fnt.Color := fntColor; fnt.Color := fntColor;
fnt.Position := fntPos;
FFontList.Add(fnt); Result := FFontList.Add(fnt);
end; end;
procedure TsSpreadOOXMLReader.ReadFonts(ANode: TDOMNode); procedure TsSpreadOOXMLReader.ReadFonts(ANode: TDOMNode);
@@ -1486,11 +1558,16 @@ var
valuenode: TDOMNode; valuenode: TDOMNode;
childnode: TDOMNode; childnode: TDOMNode;
nodename: String; nodename: String;
s: String; s, sval: String;
fntIndex, startIndex, count: Integer;
richTextParams: TsRichTextParams;
ms: TMemoryStream;
fnt: TsFont;
begin begin
while Assigned(ANode) do begin while Assigned(ANode) do begin
if ANode.NodeName = 'si' then begin if ANode.NodeName = 'si' then begin
s := ''; s := '';
richTextParams := nil;
valuenode := ANode.FirstChild; valuenode := ANode.FirstChild;
while valuenode <> nil do begin while valuenode <> nil do begin
nodename := valuenode.NodeName; nodename := valuenode.NodeName;
@@ -1498,15 +1575,56 @@ begin
s := GetNodeValue(valuenode) s := GetNodeValue(valuenode)
else else
if nodename = 'r' then begin if nodename = 'r' then begin
fntIndex := -1;
startIndex := -1;
count := -1;
childnode := valuenode.FirstChild; childnode := valuenode.FirstChild;
while childnode <> nil do begin while childnode <> nil do begin
s := s + GetNodeValue(childnode); 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;
end
else if nodename = 'rPr' then begin
fntIndex := ReadFont(childnode);
// Here we store the font in the internal font list of the reader.
// But this fontindex may be different from the one needed for the
// workbook's font list. We fix this here.
fnt := TsFont(FFontList[fntIndex]);
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;
end;
childnode := childnode.NextSibling; childnode := childnode.NextSibling;
end; end;
end; end;
valuenode := valuenode.NextSibling; valuenode := valuenode.NextSibling;
end; end;
FSharedStrings.Add(s); if Length(richTextParams) = 0 then
FSharedStrings.Add(s)
else
begin
ms := TMemoryStream.Create;
ms.WriteWord(Length(richTextParams));
ms.WriteBuffer(richTextParams[0], SizeOf(TsRichTextParam)*Length(richTextParams));
FSharedStrings.AddObject(s, ms);
end;
end; end;
ANode := ANode.NextSibling; ANode := ANode.NextSibling;
end; end;
@@ -1732,14 +1850,6 @@ begin
FreeAndNil(Doc); FreeAndNil(Doc);
end; end;
// process the sharedstrings.xml file
if FileExists(FilePath + OOXML_PATH_XL_STRINGS) then begin
ReadXMLFile(Doc, FilePath + OOXML_PATH_XL_STRINGS);
DeleteFile(FilePath + OOXML_PATH_XL_STRINGS);
ReadSharedStrings(Doc.DocumentElement.FindNode('si'));
FreeAndNil(Doc);
end;
// process the workbook.xml file // process the workbook.xml file
if not FileExists(FilePath + OOXML_PATH_XL_WORKBOOK) then if not FileExists(FilePath + OOXML_PATH_XL_WORKBOOK) then
raise Exception.CreateFmt(rsDefectiveInternalStructure, ['xlsx']); raise Exception.CreateFmt(rsDefectiveInternalStructure, ['xlsx']);
@@ -1763,6 +1873,14 @@ begin
FreeAndNil(Doc); FreeAndNil(Doc);
end; end;
// process the sharedstrings.xml file
if FileExists(FilePath + OOXML_PATH_XL_STRINGS) then begin
ReadXMLFile(Doc, FilePath + OOXML_PATH_XL_STRINGS);
DeleteFile(FilePath + OOXML_PATH_XL_STRINGS);
ReadSharedStrings(Doc.DocumentElement.FindNode('si'));
FreeAndNil(Doc);
end;
// read worksheets // read worksheets
for i:=0 to SheetList.Count-1 do begin for i:=0 to SheetList.Count-1 do begin
// Create worksheet // Create worksheet
@@ -2211,35 +2329,49 @@ begin
'</fills>'); '</fills>');
end; end;
{ Writes the fontlist of the workbook to the stream. The font id used in xf { Writes font parameters to the stream.
records is given by the index of a font in the list. Therefore, we have ATag is "font" for the entry in "styles.xml", or "rPr" for the entry for
to write an empty record for font #4 which is nil due to compatibility with BIFF } richtext parameters in the shared string list. }
procedure TsSpreadOOXMLWriter.WriteFont(AStream: TStream; AFont: TsFont;
ATag: String);
var
s: String;
begin
s := '';
s := s + Format('<sz val="%g" />', [AFont.Size], FPointSeparatorSettings);
s := s + Format('<name val="%s" />', [AFont.FontName]);
if (fssBold in AFont.Style) then
s := s + '<b />';
if (fssItalic in AFont.Style) then
s := s + '<i />';
if (fssUnderline in AFont.Style) then
s := s + '<u />';
if (fssStrikeout in AFont.Style) then
s := s + '<strike />';
if AFont.Color <> scBlack then
s := s + Format('<color rgb="%s" />', [Copy(ColorToHTMLColorStr(AFont.Color), 2, MaxInt)]);
case AFont.Position of
fpSubscript : s := s + '<vertAlign val="subscript" />';
fpSuperscript: s := s + '<vertAlign val="superscript" />';
end;
AppendToStream(AStream, Format(
'<%s>%s</%s>', [ATag, s, ATag]));
end;
{ Writes the fontlist of the workbook to the stream. }
procedure TsSpreadOOXMLWriter.WriteFontList(AStream: TStream); procedure TsSpreadOOXMLWriter.WriteFontList(AStream: TStream);
var var
i: Integer; i: Integer;
font: TsFont; font: TsFont;
s: String;
begin begin
AppendToStream(FSStyles, Format( AppendToStream(AStream, Format(
'<fonts count="%d">', [Workbook.GetFontCount])); '<fonts count="%d">', [Workbook.GetFontCount]));
for i:=0 to Workbook.GetFontCount-1 do begin for i:=0 to Workbook.GetFontCount-1 do begin
font := Workbook.GetFont(i); font := Workbook.GetFont(i);
s := Format('<sz val="%g" /><name val="%s" />', [font.Size, font.FontName], FPointSeparatorSettings); WriteFont(AStream, font, 'font');
if (fssBold in font.Style) then
s := s + '<b />';
if (fssItalic in font.Style) then
s := s + '<i />';
if (fssUnderline in font.Style) then
s := s + '<u />';
if (fssStrikeout in font.Style) then
s := s + '<strike />';
if font.Color <> scBlack then
s := s + Format('<color rgb="%s" />', [Copy(ColorToHTMLColorStr(font.Color), 2, MaxInt)]);
AppendToStream(AStream,
'<font>', s, '</font>');
end; end;
AppendToStream(AStream, AppendToStream(AStream,
'</fonts>'); '</fonts>');
end; end;
procedure TsSpreadOOXMLWriter.WriteHeaderFooter(AStream: TStream; procedure TsSpreadOOXMLWriter.WriteHeaderFooter(AStream: TStream;
@@ -3500,8 +3632,11 @@ begin
CellPosText := TsWorksheet.CellPosToText(ARow, ACol); CellPosText := TsWorksheet.CellPosToText(ARow, ACol);
lStyleIndex := GetStyleIndex(ACell); lStyleIndex := GetStyleIndex(ACell);
AppendToStream(AStream, Format( AppendToStream(AStream, Format(
'<c r="%s" s="%d" t="s"><v>%d</v></c>', [CellPosText, lStyleIndex, FSharedStringsCount])); '<c r="%s" s="%d" t="s">'+
'<v>%d</v>'+
'</c>',
[CellPosText, lStyleIndex, FSharedStringsCount]
));
inc(FSharedStringsCount); inc(FSharedStringsCount);
end; end;