diff --git a/components/fpspreadsheet/fpsactions.pas b/components/fpspreadsheet/fpsactions.pas index 8af3de2fc..f6b58be65 100644 --- a/components/fpspreadsheet/fpsactions.pas +++ b/components/fpspreadsheet/fpsactions.pas @@ -1525,7 +1525,7 @@ var sfnt: TsFont; begin sfnt := TsFont.Create; - Convert_Font_to_sFont(Workbook, GetDialog.Font, sfnt); + Convert_Font_to_sFont(GetDialog.Font, sfnt); Worksheet.WriteFont(ACell, Workbook.AddFont(sfnt)); end; @@ -1550,7 +1550,7 @@ begin else sfnt := Workbook.GetDefaultFont; end; - Convert_sFont_to_Font(Workbook, sfnt, fnt); + Convert_sFont_to_Font(sfnt, fnt); GetDialog.Font.Assign(fnt); finally fnt.Free; diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 8daeecfaa..509a524af 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -172,6 +172,7 @@ type function ReadCellBorderStyle(ACell: PCell; ABorder: TsCellBorder): TsCellBorderStyle; function ReadCellBorderStyles(ACell: PCell): TsCellBorderStyles; function ReadCellFont(ACell: PCell): TsFont; + function ReadCellFontIndex(ACell: PCell): Integer; function ReadCellFormat(ACell: PCell): TsCellFormat; function ReadHorAlignment(ACell: PCell): TsHorAlignment; procedure ReadNumFormat(ACell: PCell; out ANumFormat: TsNumberFormat; @@ -239,7 +240,8 @@ type AFormula: TsRPNFormula); 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 } function WriteBackground(ARow, ACol: Cardinal; AStyle: TsFillStyle; @@ -286,9 +288,11 @@ type procedure WriteDecimals(ACell: PCell; ADecimals: Byte); overload; 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; - 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; procedure WriteFont(ACell: PCell; AFontIndex: Integer); overload; function WriteFontColor(ARow, ACol: Cardinal; AFontColor: TsColor): Integer; overload; @@ -644,12 +648,12 @@ type function GetPointerToCellFormat(AIndex: Integer): PsCellFormat; { Font handling } - function AddFont(const AFontName: String; ASize: Single; - AStyle: TsFontStyles; AColor: TsColor): Integer; overload; + function AddFont(const AFontName: String; ASize: Single; AStyle: TsFontStyles; + AColor: TsColor; APosition: TsFontPosition = fpNormal): Integer; overload; function AddFont(const AFont: TsFont): Integer; overload; procedure DeleteFont(AFontIndex: Integer); - function FindFont(const AFontName: String; ASize: Single; - AStyle: TsFontStyles; AColor: TsColor): Integer; + function FindFont(const AFontName: String; ASize: Single; AStyle: TsFontStyles; + AColor: TsColor; APosition: TsFontPosition = fpNormal): Integer; function GetBuiltinFontCount: Integer; function GetDefaultFont: TsFont; function GetDefaultFontSize: Single; @@ -660,7 +664,8 @@ type procedure InitFonts; procedure RemoveAllFonts; 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); { Number format handling } @@ -2710,8 +2715,7 @@ end; {@@ ---------------------------------------------------------------------------- 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 - fields of the cell + font if the cell does not exist. -------------------------------------------------------------------------------} function TsWorksheet.ReadCellFont(ACell: PCell): TsFont; var @@ -2727,6 +2731,23 @@ begin Result := Workbook.GetDefaultFont; 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 -------------------------------------------------------------------------------} @@ -3460,9 +3481,11 @@ end; @param ACell Pointer to the cell @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 r, c: Cardinal; + i: Integer; hyperlink: TsHyperlink; begin if ACell = nil then @@ -3499,6 +3522,12 @@ begin ACell^.ContentType := cctUTF8String; 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); end; @@ -4558,12 +4587,16 @@ end; @param AFontSize Size of the font, in points @param AFontStyle Set with font style attributes (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. -------------------------------------------------------------------------------} 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 - Result := WriteFont(GetCell(ARow, ACol), AFontName, AFontSize, AFontStyle, AFontColor); + Result := WriteFont(GetCell(ARow, ACol), AFontName, AFontSize, AFontStyle, + AFontColor, APosition); end; {@@ ---------------------------------------------------------------------------- @@ -4576,10 +4609,13 @@ end; @param AFontSize Size of the font, in points @param AFontStyle Set with font style attributes (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. -------------------------------------------------------------------------------} 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 fmt: TsCellFormat; begin @@ -4589,9 +4625,9 @@ begin Exit; end; - Result := FWorkbook.FindFont(AFontName, AFontSize, AFontStyle, AFontColor); + Result := FWorkbook.FindFont(AFontName, AFontSize, AFontStyle, AFontColor, APosition); if Result = -1 then - result := FWorkbook.AddFont(AFontName, AFontSize, AFontStyle, AFontColor); + result := FWorkbook.AddFont(AFontName, AFontSize, AFontStyle, AFontColor, APosition); fmt := Workbook.GetCellFormat(ACell^.FormatIndex); Include(fmt.UsedFormattingFields, uffFont); @@ -7036,10 +7072,12 @@ end; @param ASize Size of the font in points @param AStyle Style of the font, a combination of TsFontStyle elements @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 -------------------------------------------------------------------------------} function TsWorkbook.AddFont(const AFontName: String; ASize: Single; - AStyle: TsFontStyles; AColor: TsColor): Integer; + AStyle: TsFontStyles; AColor: TsColor; + APosition: TsFontPosition = fpNormal): Integer; var fnt: TsFont; begin @@ -7048,6 +7086,7 @@ begin fnt.Size := ASize; fnt.Style := AStyle; fnt.Color := AColor; + fnt.Position := APosition; Result := AddFont(fnt); end; @@ -7088,10 +7127,11 @@ end; @param ASize Size of the font in points @param AStyle Style of the font, a combination of TsFontStyle elements @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. -------------------------------------------------------------------------------} function TsWorkbook.FindFont(const AFontName: String; ASize: Single; - AStyle: TsFontStyles; AColor: TsColor): Integer; + AStyle: TsFontStyles; AColor: TsColor; APosition: TsFontPosition = fpNormal): Integer; const EPS = 1e-3; var @@ -7104,7 +7144,8 @@ begin SameText(AFontName, fnt.FontName) and SameValue(ASize, fnt.Size, EPS) and // careful when comparing floating point numbers (AStyle = fnt.Style) and - (AColor = fnt.Color) + (AColor = fnt.Color) and + (APosition = fnt.Position) then exit; end; @@ -7164,17 +7205,19 @@ end; Replaces the built-in font at a specific index with different font parameters -------------------------------------------------------------------------------} procedure TsWorkbook.ReplaceFont(AFontIndex: Integer; AFontName: String; - ASize: Single; AStyle: TsFontStyles; AColor: TsColor); + ASize: Single; AStyle: TsFontStyles; AColor: TsColor; + APosition: TsFontPosition = fpNormal); var fnt: TsFont; begin - if (AFontIndex < FBuiltinFontCount) and (AFontIndex <> 4) then + if (AFontIndex < FBuiltinFontCount) then //and (AFontIndex <> 4) then begin fnt := TsFont(FFontList[AFontIndex]); fnt.FontName := AFontName; fnt.Size := ASize; fnt.Style := AStyle; fnt.Color := AColor; + fnt.Position := APosition; end; end; @@ -7255,6 +7298,8 @@ begin if (fssItalic in fnt.Style) then Result := Result + '; italic'; if (fssUnderline in fnt.Style) then Result := Result + '; underline'; 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 Result := ''; end; diff --git a/components/fpspreadsheet/fpspreadsheetctrls.pas b/components/fpspreadsheet/fpspreadsheetctrls.pas index 817ed9d5e..13baa9829 100644 --- a/components/fpspreadsheet/fpspreadsheetctrls.pas +++ b/components/fpspreadsheet/fpspreadsheetctrls.pas @@ -2675,6 +2675,7 @@ var r1, r2, c1, c2: Cardinal; fmt: TsCellFormat; numFmt: TsNumFormatParams; + rtp: TsRichTextParam; begin if (ACell <> nil) then fmt := Workbook.GetCellFormat(ACell^.FormatIndex) @@ -2692,6 +2693,17 @@ begin 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) then AStrings.Add('TextRotation=(default)') else AStrings.Add(Format('TextRotation=%s', [ diff --git a/components/fpspreadsheet/fpspreadsheetgrid.pas b/components/fpspreadsheet/fpspreadsheetgrid.pas index 77f7ff877..1330f85ff 100644 --- a/components/fpspreadsheet/fpspreadsheetgrid.pas +++ b/components/fpspreadsheet/fpspreadsheetgrid.pas @@ -168,10 +168,16 @@ type function GetEditText(ACol, ARow: Integer): String; override; function HasBorder(ACell: PCell; ABorder: TsCellBorder): Boolean; 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; AJustification: Byte; ACellHorAlign: TsHorAlignment; ACellVertAlign: TsVertAlignment; ATextRot: TsTextRotation; - ATextWrap, ReplaceTooLong: Boolean); + ATextWrap, ReplaceTooLong: Boolean; ARichTextParams: TsRichTextParams); + } procedure KeyDown(var Key : Word; Shift : TShiftState); override; procedure Loaded; override; procedure LoadFromWorksheet(AWorksheet: TsWorksheet); @@ -1164,7 +1170,7 @@ end; -------------------------------------------------------------------------------} procedure TsCustomWorksheetGrid.Convert_sFont_to_Font(sFont: TsFont; AFont: TFont); begin - fpsVisualUtils.Convert_sFont_to_Font(Workbook, sFont, AFont); + fpsVisualUtils.Convert_sFont_to_Font(sFont, AFont); end; {@@ ---------------------------------------------------------------------------- @@ -1176,7 +1182,7 @@ end; procedure TsCustomWorksheetGrid.Convert_Font_to_sFont(AFont: TFont; sFont: TsFont); begin - fpsVisualUtils.Convert_Font_to_sFont(Workbook, AFont, sFont); + fpsVisualUtils.Convert_Font_to_sFont(AFont, sFont); end; {@@ ---------------------------------------------------------------------------- @@ -2081,6 +2087,7 @@ var horAlign: TsHorAlignment; vertAlign: TsVertAlignment; txtRot: TsTextRotation; + fntIndex: Integer; lCell: PCell; justif: Byte; fmt: PsCellFormat; @@ -2110,10 +2117,29 @@ begin // Cells fmt := Workbook.GetPointerToCellFormat(lCell^.FormatIndex); wrapped := (uffWordWrap in fmt^.UsedFormattingFields) or (fmt^.TextRotation = rtStacked); - txtRot := fmt^.TextRotation; - vertAlign := fmt^.VertAlignment; - if vertAlign = vaDefault then vertAlign := vaBottom; - if fmt^.HorAlignment <> haDefault then + if (uffTextRotation in fmt^.UsedFormattingFields) + then txtRot := fmt^.TextRotation + else txtRot := trHorizontal; + 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 else begin @@ -2121,11 +2147,14 @@ begin horAlign := haRight else horAlign := haLeft; - end; + end; } + + if (uffFont in fmt^.UsedFormattingFields) + then fntIndex := fmt^.FontIndex + else fntIndex := DEFAULT_FONTINDEX; InflateRect(ARect, -constCellPadding, -constCellPadding); -// txt := GetCellText(ACol, ARow); txt := GetCellText(GetGridRow(lCell^.Col), GetGridCol(lCell^.Row)); if txt = '' then exit; @@ -2151,8 +2180,12 @@ begin vaBottom: justif := 0; end; end; + InternalDrawTextInCell(txt, ARect, horAlign, vertAlign, txtRot, wrapped, + fntIndex, lCell^.RichTextParams); +{ InternalDrawTextInCell(txt, txt, ARect, justif, horAlign, vertAlign, - txtRot, wrapped, false); + txtRot, wrapped, false, lCell^.RichTextParams); + } end; {@@ ---------------------------------------------------------------------------- @@ -3084,18 +3117,34 @@ end; @param ACellVertAlign Is the VertAlignment property stored in the cell @param ATextRot Determines the rotation angle of the text. @param ATextWrap Determines if the text can wrap into multiple lines - @param ReplaceTooLang If true too-long texts are replaced by a series of - # chars filling the cell. + @param AFontIndex Font index to be used for drawing non-rich-text. + @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 the output of nfAccounting formatted numbers where the numbers are always right-aligned, and the currency symbol is left-aligned. 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; ARect: TRect; AJustification: Byte; ACellHorAlign: TsHorAlignment; ACellVertAlign: TsVertAlignment; ATextRot: TsTextRotation; - ATextWrap, ReplaceTooLong: Boolean); + ATextWrap, ReplaceTooLong: Boolean; ARichTextParams: TsRichTextParams); var ts: TTextStyle; flags: Cardinal; @@ -3281,7 +3330,7 @@ begin end; end; end; - + *) {@@ ---------------------------------------------------------------------------- Standard key handling method inherited from TCustomGrid. Is overridden to catch the ESC key during editing in order to restore the old cell text diff --git a/components/fpspreadsheet/fpstypes.pas b/components/fpspreadsheet/fpstypes.pas index b08b2dc71..5a10954e0 100644 --- a/components/fpspreadsheet/fpstypes.pas +++ b/components/fpspreadsheet/fpstypes.pas @@ -403,6 +403,9 @@ type {@@ Set of font styles } 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 (in points), the font style, and the font color. } TsFont = class @@ -414,8 +417,20 @@ type Style: TsFontStyles; {@@ Text color given as rgb value } Color: TsColor; + {@@ Text position } + Position: TsFontPosition; 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 corresponding border is drawn in the style defined by the CellBorderStyle. } TsCellBorder = (cbNorth, cbWest, cbEast, cbSouth, cbDiagUp, cbDiagDown); @@ -583,7 +598,8 @@ type { Index of format record in the workbook's FCellFormatList } FormatIndex: Integer; { 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; case ContentType: TCellContentType of // variant part must be at the end cctEmpty : (); // has no data at all diff --git a/components/fpspreadsheet/fpsvisualutils.pas b/components/fpspreadsheet/fpsvisualutils.pas index 18a82cc11..8233749df 100644 --- a/components/fpspreadsheet/fpsvisualutils.pas +++ b/components/fpspreadsheet/fpsvisualutils.pas @@ -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; +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 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). @@ -151,4 +163,444 @@ begin 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. diff --git a/components/fpspreadsheet/laz_fpspreadsheet.lpk b/components/fpspreadsheet/laz_fpspreadsheet.lpk index 6dd461652..ae6dfc717 100644 --- a/components/fpspreadsheet/laz_fpspreadsheet.lpk +++ b/components/fpspreadsheet/laz_fpspreadsheet.lpk @@ -20,7 +20,8 @@ - + ', [AFont.Size], FPointSeparatorSettings); + s := s + Format('', [AFont.FontName]); + if (fssBold in AFont.Style) then + s := s + ''; + if (fssItalic in AFont.Style) then + s := s + ''; + if (fssUnderline in AFont.Style) then + s := s + ''; + if (fssStrikeout in AFont.Style) then + s := s + ''; + if AFont.Color <> scBlack then + s := s + Format('', [Copy(ColorToHTMLColorStr(AFont.Color), 2, MaxInt)]); + case AFont.Position of + fpSubscript : s := s + ''; + fpSuperscript: s := s + ''; + end; + AppendToStream(AStream, Format( + '<%s>%s', [ATag, s, ATag])); +end; + +{ Writes the fontlist of the workbook to the stream. } procedure TsSpreadOOXMLWriter.WriteFontList(AStream: TStream); var i: Integer; font: TsFont; - s: String; begin - AppendToStream(FSStyles, Format( - '', [Workbook.GetFontCount])); + AppendToStream(AStream, Format( + '', [Workbook.GetFontCount])); for i:=0 to Workbook.GetFontCount-1 do begin font := Workbook.GetFont(i); - s := Format('', [font.Size, font.FontName], FPointSeparatorSettings); - if (fssBold in font.Style) then - s := s + ''; - if (fssItalic in font.Style) then - s := s + ''; - if (fssUnderline in font.Style) then - s := s + ''; - if (fssStrikeout in font.Style) then - s := s + ''; - if font.Color <> scBlack then - s := s + Format('', [Copy(ColorToHTMLColorStr(font.Color), 2, MaxInt)]); - AppendToStream(AStream, - '', s, ''); + WriteFont(AStream, font, 'font'); end; AppendToStream(AStream, - ''); + ''); end; procedure TsSpreadOOXMLWriter.WriteHeaderFooter(AStream: TStream; @@ -3500,8 +3632,11 @@ begin CellPosText := TsWorksheet.CellPosToText(ARow, ACol); lStyleIndex := GetStyleIndex(ACell); AppendToStream(AStream, Format( - '%d', [CellPosText, lStyleIndex, FSharedStringsCount])); - + ''+ + '%d'+ + '', + [CellPosText, lStyleIndex, FSharedStringsCount] + )); inc(FSharedStringsCount); end;