|
@@ -1,20 +1,16 @@
|
|
-unit Fresnel.TextLayouter;
|
|
|
|
|
|
+unit Fresnel.TextLayouter;
|
|
|
|
|
|
{$mode objfpc}
|
|
{$mode objfpc}
|
|
{$H+}
|
|
{$H+}
|
|
{$modeswitch advancedrecords}
|
|
{$modeswitch advancedrecords}
|
|
|
|
|
|
-{$IF FPC_FULLVERSION>30300}
|
|
|
|
- {$DEFINE HasTObjectToString}
|
|
|
|
-{$ENDIF}
|
|
|
|
-
|
|
|
|
interface
|
|
interface
|
|
|
|
|
|
uses
|
|
uses
|
|
{$IFDEF FPC_DOTTEDUNITS}
|
|
{$IFDEF FPC_DOTTEDUNITS}
|
|
System.Classes, System.SysUtils, System.Types, System.Contnrs, fpImage, System.UITypes;
|
|
System.Classes, System.SysUtils, System.Types, System.Contnrs, fpImage, System.UITypes;
|
|
{$ELSE}
|
|
{$ELSE}
|
|
- Classes, SysUtils, Fresnel.Classes, Contnrs, fpImage, System.UITypes;
|
|
|
|
|
|
+ Classes, SysUtils, Types, Contnrs, fpImage, System.UITypes;
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
|
|
Const
|
|
Const
|
|
@@ -22,8 +18,8 @@ Const
|
|
|
|
|
|
Type
|
|
Type
|
|
{$IF SIZEOF(CHAR)=1}
|
|
{$IF SIZEOF(CHAR)=1}
|
|
- TTextString = String;
|
|
|
|
- TFontNameString = String;
|
|
|
|
|
|
+ TTextString = UTF8String;
|
|
|
|
+ TFontNameString = AnsiString;
|
|
{$ELSE}
|
|
{$ELSE}
|
|
TTextString = UnicodeString;
|
|
TTextString = UnicodeString;
|
|
TFontNameString = UnicodeString;
|
|
TFontNameString = UnicodeString;
|
|
@@ -41,7 +37,7 @@ Type
|
|
TOverlappingRangesAction = (oraError,oraFit);
|
|
TOverlappingRangesAction = (oraError,oraFit);
|
|
TCullThreshold = 1..100;
|
|
TCullThreshold = 1..100;
|
|
|
|
|
|
- TTextUnits = TFresnelLength;
|
|
|
|
|
|
+ TTextUnits = single;
|
|
|
|
|
|
{ No hyphenation:
|
|
{ No hyphenation:
|
|
|
|
|
|
@@ -81,41 +77,49 @@ Type
|
|
Width, Height : TTextUnits;
|
|
Width, Height : TTextUnits;
|
|
Ascender, Descender : TTextUnits;
|
|
Ascender, Descender : TTextUnits;
|
|
end;
|
|
end;
|
|
- TTextPoint = TFresnelPoint;
|
|
|
|
|
|
+ TTextPoint = {$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}Types.TPointF;
|
|
|
|
|
|
TFontAttribute = (faBold,faItalic,faUnderline,faStrikeOut);
|
|
TFontAttribute = (faBold,faItalic,faUnderline,faStrikeOut);
|
|
TFontAttributes = set of TFontAttribute;
|
|
TFontAttributes = set of TFontAttribute;
|
|
|
|
|
|
{ TTextFont }
|
|
{ TTextFont }
|
|
|
|
|
|
- TTextFont = Class(TPersistent)
|
|
|
|
|
|
+ TTextFont = class (TPersistent)
|
|
private
|
|
private
|
|
FOwner : TPersistent;
|
|
FOwner : TPersistent;
|
|
- FAttrs: TFontAttributes;
|
|
|
|
- FName: string;
|
|
|
|
- FSize: Smallint;
|
|
|
|
|
|
+ FAttrs : TFontAttributes;
|
|
|
|
+ FName : string;
|
|
|
|
+ FSize : Smallint;
|
|
FColor : TFPColor;
|
|
FColor : TFPColor;
|
|
function GetColor: TColor;
|
|
function GetColor: TColor;
|
|
procedure SetAttrs(AValue: TFontAttributes);
|
|
procedure SetAttrs(AValue: TFontAttributes);
|
|
procedure SetColor(AValue: TColor);
|
|
procedure SetColor(AValue: TColor);
|
|
procedure SetFPColor(AValue: TFPColor);
|
|
procedure SetFPColor(AValue: TFPColor);
|
|
- procedure SetName(AValue: string);
|
|
|
|
|
|
+ procedure SetName(const AValue: String);
|
|
procedure SetSize(AValue: Smallint);
|
|
procedure SetSize(AValue: Smallint);
|
|
- Public
|
|
|
|
|
|
+
|
|
|
|
+ public
|
|
|
|
+ constructor Create(aOwner : TPersistent); virtual;
|
|
|
|
+
|
|
procedure Assign(Source: TPersistent); override;
|
|
procedure Assign(Source: TPersistent); override;
|
|
- Constructor Create(aOwner : TPersistent); virtual;
|
|
|
|
- procedure Changed;virtual;
|
|
|
|
- Function Clone(aOwner : TPersistent=nil): TTextFont;
|
|
|
|
- Property FPColor : TFPColor Read FColor write SetFPColor;
|
|
|
|
- Published
|
|
|
|
|
|
+
|
|
|
|
+ procedure Changed; virtual;
|
|
|
|
+
|
|
|
|
+ function Clone(aOwner : TPersistent=nil): TTextFont;
|
|
|
|
+
|
|
|
|
+ function IsSameFont(aFont : TTextFont) : Boolean; virtual;
|
|
|
|
+
|
|
|
|
+ property FPColor : TFPColor Read FColor write SetFPColor;
|
|
|
|
+
|
|
|
|
+ published
|
|
// In name
|
|
// In name
|
|
- Property Name : TFontNameString Read FName Write SetName;
|
|
|
|
|
|
+ property Name : TFontNameString Read FName Write SetName;
|
|
// In pixels
|
|
// In pixels
|
|
- Property Size : Smallint Read FSize write SetSize;
|
|
|
|
|
|
+ property Size : Smallint Read FSize write SetSize;
|
|
// attributes
|
|
// attributes
|
|
- Property Attrs : TFontAttributes read FAttrs Write SetAttrs;
|
|
|
|
|
|
+ property Attrs : TFontAttributes read FAttrs Write SetAttrs;
|
|
// Color. Not needed for calculations, but allows for easier management
|
|
// Color. Not needed for calculations, but allows for easier management
|
|
- Property Color : TColor read GetColor Write SetColor;
|
|
|
|
|
|
+ property Color : TColor read GetColor Write SetColor;
|
|
end;
|
|
end;
|
|
TTextFontClass = class of TTextFont;
|
|
TTextFontClass = class of TTextFont;
|
|
|
|
|
|
@@ -150,13 +154,16 @@ Type
|
|
private
|
|
private
|
|
FLayouter: TTextLayouter;
|
|
FLayouter: TTextLayouter;
|
|
FWhiteSpaceWidth : TTextUnits;
|
|
FWhiteSpaceWidth : TTextUnits;
|
|
|
|
+ FLineHeight : TTextUnits;
|
|
public
|
|
public
|
|
Constructor Create(aLayouter : TTextLayouter); virtual;
|
|
Constructor Create(aLayouter : TTextLayouter); virtual;
|
|
// Font size in points.
|
|
// Font size in points.
|
|
Procedure SetFont(const aFontName : TFontNameString; aSize : SmallInt; Attrs : TFontAttributes); virtual; abstract;
|
|
Procedure SetFont(const aFontName : TFontNameString; aSize : SmallInt; Attrs : TFontAttributes); virtual; abstract;
|
|
Procedure SetFont(const aFont: TTextFont);
|
|
Procedure SetFont(const aFont: TTextFont);
|
|
- Function MeasureText(aText : TTextString) : TTextMeasures; virtual; abstract;
|
|
|
|
- Function WhitespaceWidth : TTextUnits;
|
|
|
|
|
|
+ Function MeasureText(const aText : TTextString) : TTextMeasures; virtual; abstract;
|
|
|
|
+ function MeasureTextWidth(const aText : TTextString) : Single; virtual;
|
|
|
|
+ function LineHeight : TTextUnits;
|
|
|
|
+ function WhitespaceWidth : TTextUnits;
|
|
Property Layouter : TTextLayouter Read FLayouter;
|
|
Property Layouter : TTextLayouter Read FLayouter;
|
|
|
|
|
|
end;
|
|
end;
|
|
@@ -174,7 +181,7 @@ Type
|
|
Public
|
|
Public
|
|
Constructor Create(aLayouter : TTextLayouter); override;
|
|
Constructor Create(aLayouter : TTextLayouter); override;
|
|
Procedure SetFont(const aFontName : TFontNameString; aSize : SmallInt; aAttrs : TFontAttributes); override;
|
|
Procedure SetFont(const aFontName : TFontNameString; aSize : SmallInt; aAttrs : TFontAttributes); override;
|
|
- Function MeasureText(aText : TTextString) : TTextMeasures; override;
|
|
|
|
|
|
+ Function MeasureText(const aText : TTextString) : TTextMeasures; override;
|
|
Property CharHeight : TTextUnits Read FHeight Write FHeight;
|
|
Property CharHeight : TTextUnits Read FHeight Write FHeight;
|
|
Property CharWidth : TTextUnits Read FWidth Write FWidth;
|
|
Property CharWidth : TTextUnits Read FWidth Write FWidth;
|
|
Property Size : SmallInt Read FSize;
|
|
Property Size : SmallInt Read FSize;
|
|
@@ -205,9 +212,7 @@ Type
|
|
// At pos is relative to the text here, zero based
|
|
// At pos is relative to the text here, zero based
|
|
function Split(atPos : integer) : TTextBlock; virtual;
|
|
function Split(atPos : integer) : TTextBlock; virtual;
|
|
procedure Assign(aBlock : TTextBlock); virtual;
|
|
procedure Assign(aBlock : TTextBlock); virtual;
|
|
- {$IFDEF HasTObjectToString}
|
|
|
|
function ToString : RTLString; override;
|
|
function ToString : RTLString; override;
|
|
- {$ENDIF}
|
|
|
|
Procedure TrimTrailingWhiteSpace;
|
|
Procedure TrimTrailingWhiteSpace;
|
|
// Text for this block. Calculated from offset/len
|
|
// Text for this block. Calculated from offset/len
|
|
Property Text : TTextString Read GetText;
|
|
Property Text : TTextString Read GetText;
|
|
@@ -251,9 +256,7 @@ Type
|
|
destructor destroy; override;
|
|
destructor destroy; override;
|
|
procedure Assign(Source: TPersistent); override;
|
|
procedure Assign(Source: TPersistent); override;
|
|
Procedure Changed;
|
|
Procedure Changed;
|
|
- {$IFDEF HasTObjectToString}
|
|
|
|
function ToString : RTLString; override;
|
|
function ToString : RTLString; override;
|
|
- {$ENDIF}
|
|
|
|
Published
|
|
Published
|
|
// Offset is 0 based and is the offset from the first character in the text.
|
|
// Offset is 0 based and is the offset from the first character in the text.
|
|
Property CharOffset : SizeInt Read FCharOffset Write SetCharOffSet;
|
|
Property CharOffset : SizeInt Read FCharOffset Write SetCharOffSet;
|
|
@@ -283,9 +286,9 @@ Type
|
|
FWidth: TTextUnits;
|
|
FWidth: TTextUnits;
|
|
FLayouter : TTextLayouter;
|
|
FLayouter : TTextLayouter;
|
|
function GetAsPoint: TTextPoint;
|
|
function GetAsPoint: TTextPoint;
|
|
- procedure SetAsPoint(const AValue: TTextPoint);
|
|
|
|
- procedure SetHeight(const AValue: TTextUnits);
|
|
|
|
- procedure SetWidth(const AValue: TTextUnits);
|
|
|
|
|
|
+ procedure SetAsPoint(AValue: TTextPoint);
|
|
|
|
+ procedure SetHeight(AValue: TTextUnits);
|
|
|
|
+ procedure SetWidth(AValue: TTextUnits);
|
|
protected
|
|
protected
|
|
procedure Changed; virtual;
|
|
procedure Changed; virtual;
|
|
function GetOwner: TPersistent; override;
|
|
function GetOwner: TPersistent; override;
|
|
@@ -347,21 +350,20 @@ Type
|
|
class Function CreateSplitter(aLayouter : TTextLayouter): TTextSplitter; virtual;
|
|
class Function CreateSplitter(aLayouter : TTextLayouter): TTextSplitter; virtual;
|
|
class function CreateRanges(aLayouter: TTextLayouter): TTextRangeList; virtual;
|
|
class function CreateRanges(aLayouter: TTextLayouter): TTextRangeList; virtual;
|
|
class function CreateBlock(aLayouter: TTextLayouter; aOffset,aLength : SizeInt) : TTextBlock; virtual;
|
|
class function CreateBlock(aLayouter: TTextLayouter; aOffset,aLength : SizeInt) : TTextBlock; virtual;
|
|
- function FindWrapPosition(B: TTextBlock; S: String; var aPos: integer; var CurrPos: TTextPoint): Boolean;
|
|
|
|
|
|
+ function FindWrapPosition(aB: TTextBlock; const aStr: String; var aPos: integer; var aCurrXPos : TTextUnits): Boolean;
|
|
function AddBlock(aOffset, aLength: SizeInt; aFont: TTextFont): TTextBlock; virtual;
|
|
function AddBlock(aOffset, aLength: SizeInt; aFont: TTextFont): TTextBlock; virtual;
|
|
procedure ApplyStretchMode(const ADesiredHeight: TTextUnits); virtual;
|
|
procedure ApplyStretchMode(const ADesiredHeight: TTextUnits); virtual;
|
|
- function WrapBlock(B: TTextBlock; S: String; var Idx: integer; var CurrPos: TTextPoint) : Boolean; virtual;
|
|
|
|
|
|
+ function WrapBlock(aB: TTextBlock; const aStr: String; var aIdx: integer; var aCurrPos: TTextPoint) : Boolean; virtual;
|
|
procedure CullTextHorizontally(B: TTextBlock);
|
|
procedure CullTextHorizontally(B: TTextBlock);
|
|
procedure HandleRanges; virtual;
|
|
procedure HandleRanges; virtual;
|
|
procedure HandleNewLines; virtual;
|
|
procedure HandleNewLines; virtual;
|
|
- // Apply vertical text alignment
|
|
|
|
|
|
+
|
|
procedure ApplyVertTextAlignment;
|
|
procedure ApplyVertTextAlignment;
|
|
- // Apply horizontal text alignment
|
|
|
|
procedure ApplyHorzTextAlignment;
|
|
procedure ApplyHorzTextAlignment;
|
|
- // Remove text that falls outside bounds vertically.
|
|
|
|
|
|
+
|
|
procedure CullTextOutOfBoundsVertically;
|
|
procedure CullTextOutOfBoundsVertically;
|
|
- // Handle text that falls outside bounds horizontally, depending on WordOverFlow.
|
|
|
|
procedure CullTextOutOfBoundsHorizontally;
|
|
procedure CullTextOutOfBoundsHorizontally;
|
|
|
|
+
|
|
// Return true if a split occurred.
|
|
// Return true if a split occurred.
|
|
function WrapLayout: Boolean; virtual;
|
|
function WrapLayout: Boolean; virtual;
|
|
// Return True if there are multiple lines.
|
|
// Return True if there are multiple lines.
|
|
@@ -381,9 +383,7 @@ Type
|
|
Procedure Reset;
|
|
Procedure Reset;
|
|
// Check if ranges do not overlap.
|
|
// Check if ranges do not overlap.
|
|
procedure CheckRanges;
|
|
procedure CheckRanges;
|
|
- {$IFDEF HasTObjectToString}
|
|
|
|
function ToString : RTLString; override;
|
|
function ToString : RTLString; override;
|
|
- {$ENDIF}
|
|
|
|
Property TextBlocks[aIndex : Integer] : TTextBlock Read GetBlock;
|
|
Property TextBlocks[aIndex : Integer] : TTextBlock Read GetBlock;
|
|
Property TextBlockCount : Integer Read GetBlockCount;
|
|
Property TextBlockCount : Integer Read GetBlockCount;
|
|
function Execute : integer; virtual;
|
|
function Execute : integer; virtual;
|
|
@@ -394,8 +394,8 @@ Type
|
|
Function GetMaxRight : TTextUnits;
|
|
Function GetMaxRight : TTextUnits;
|
|
Function GetMinTop : TTextUnits;
|
|
Function GetMinTop : TTextUnits;
|
|
Function GetMaxBottom : TTextUnits;
|
|
Function GetMaxBottom : TTextUnits;
|
|
- Function GetTotalSize : TFresnelPoint;
|
|
|
|
- Function GetBoundsRect : TFresnelRect;
|
|
|
|
|
|
+ Function GetTotalSize : TSizeF;
|
|
|
|
+ Function GetBoundsRect : TRectF;
|
|
// Color of font
|
|
// Color of font
|
|
Property FPColor : TFPColor Read GetColor Write SetColor;
|
|
Property FPColor : TFPColor Read GetColor Write SetColor;
|
|
Published
|
|
Published
|
|
@@ -496,7 +496,7 @@ begin
|
|
Self.TextLen:=AtPos;
|
|
Self.TextLen:=AtPos;
|
|
// Reset formatting stuff on new
|
|
// Reset formatting stuff on new
|
|
Result.ForceNewLine:=False;
|
|
Result.ForceNewLine:=False;
|
|
- Result.LayoutPos:=Default(TTextPoint);
|
|
|
|
|
|
+ Result.LayoutPos:=Default(TPointF);
|
|
Result.Size.Width:=0;
|
|
Result.Size.Width:=0;
|
|
Result.Size.Height:=0;
|
|
Result.Size.Height:=0;
|
|
Result.Size.Descender:=0;
|
|
Result.Size.Descender:=0;
|
|
@@ -516,13 +516,11 @@ begin
|
|
ForceNewLine:=aBlock.ForceNewLine;
|
|
ForceNewLine:=aBlock.ForceNewLine;
|
|
end;
|
|
end;
|
|
|
|
|
|
-{$IFDEF HasTObjectToString}
|
|
|
|
function TTextBlock.ToString: RTLString;
|
|
function TTextBlock.ToString: RTLString;
|
|
begin
|
|
begin
|
|
Result:=Inherited ToString;
|
|
Result:=Inherited ToString;
|
|
Result:=Result+Format(': (x: %g, y: %g, w: %g, h:%g) [Off: %d, len: %d]: >>%s<< ',[LayoutPos.X,LayoutPos.Y,Size.Width,Size.Height,TextOffset,TextLen,Text]);
|
|
Result:=Result+Format(': (x: %g, y: %g, w: %g, h:%g) [Off: %d, len: %d]: >>%s<< ',[LayoutPos.X,LayoutPos.Y,Size.Width,Size.Height,TextOffset,TextLen,Text]);
|
|
end;
|
|
end;
|
|
-{$ENDIF}
|
|
|
|
|
|
|
|
procedure TTextBlock.TrimTrailingWhiteSpace;
|
|
procedure TTextBlock.TrimTrailingWhiteSpace;
|
|
|
|
|
|
@@ -607,12 +605,10 @@ begin
|
|
TTextLayouter(Collection.Owner).Reset;
|
|
TTextLayouter(Collection.Owner).Reset;
|
|
end;
|
|
end;
|
|
|
|
|
|
-{$IFDEF HasTObjectToString}
|
|
|
|
function TTextRange.ToString: RTLString;
|
|
function TTextRange.ToString: RTLString;
|
|
begin
|
|
begin
|
|
Result:=Format('[offset %d, len: %d]',[CharOffset,CharLength]);
|
|
Result:=Format('[offset %d, len: %d]',[CharOffset,CharLength]);
|
|
end;
|
|
end;
|
|
-{$ENDIF}
|
|
|
|
|
|
|
|
{ TTextRangeList }
|
|
{ TTextRangeList }
|
|
|
|
|
|
@@ -637,7 +633,7 @@ end;
|
|
|
|
|
|
{ TTextLayoutBounds }
|
|
{ TTextLayoutBounds }
|
|
|
|
|
|
-procedure TTextLayoutBounds.SetHeight(const AValue: TTextUnits);
|
|
|
|
|
|
+procedure TTextLayoutBounds.SetHeight(AValue: TTextUnits);
|
|
begin
|
|
begin
|
|
if FHeight=AValue then Exit;
|
|
if FHeight=AValue then Exit;
|
|
FHeight:=AValue;
|
|
FHeight:=AValue;
|
|
@@ -646,18 +642,17 @@ end;
|
|
|
|
|
|
function TTextLayoutBounds.GetAsPoint: TTextPoint;
|
|
function TTextLayoutBounds.GetAsPoint: TTextPoint;
|
|
begin
|
|
begin
|
|
- Result.X:=Width;
|
|
|
|
- Result.Y:=Height;
|
|
|
|
|
|
+ Result:=PointF(Width,Height);
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure TTextLayoutBounds.SetAsPoint(const AValue: TTextPoint);
|
|
|
|
|
|
+procedure TTextLayoutBounds.SetAsPoint(AValue: TTextPoint);
|
|
begin
|
|
begin
|
|
FWidth:=aValue.X;
|
|
FWidth:=aValue.X;
|
|
FHeight:=aValue.Y;
|
|
FHeight:=aValue.Y;
|
|
Changed;
|
|
Changed;
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure TTextLayoutBounds.SetWidth(const AValue: TTextUnits);
|
|
|
|
|
|
+procedure TTextLayoutBounds.SetWidth(AValue: TTextUnits);
|
|
begin
|
|
begin
|
|
if FWidth=AValue then Exit;
|
|
if FWidth=AValue then Exit;
|
|
FWidth:=AValue;
|
|
FWidth:=AValue;
|
|
@@ -817,7 +812,22 @@ procedure TTextMeasurer.SetFont(const aFont: TTextFont);
|
|
begin
|
|
begin
|
|
With aFont do
|
|
With aFont do
|
|
SetFont(Name,Size,Attrs);
|
|
SetFont(Name,Size,Attrs);
|
|
- FWhiteSpaceWidth:=0;
|
|
|
|
|
|
+ FWhiteSpaceWidth := 0;
|
|
|
|
+ FLineHeight := 0;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TTextMeasurer.MeasureTextWidth(const aText: TTextString): Single;
|
|
|
|
+begin
|
|
|
|
+ Result := MeasureText(aText).Width;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+// LineHeight
|
|
|
|
+//
|
|
|
|
+function TTextMeasurer.LineHeight: TTextUnits;
|
|
|
|
+begin
|
|
|
|
+ if FLineHeight = 0 then
|
|
|
|
+ FLineHeight := MeasureText('Ply|').Height;
|
|
|
|
+ Result := FLineHeight;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TTextMeasurer.WhitespaceWidth: TTextUnits;
|
|
function TTextMeasurer.WhitespaceWidth: TTextUnits;
|
|
@@ -843,7 +853,7 @@ begin
|
|
FAttrs:=aAttrs;
|
|
FAttrs:=aAttrs;
|
|
end;
|
|
end;
|
|
|
|
|
|
-function TFixedSizeTextMeasurer.MeasureText(aText: TTextString): TTextMeasures;
|
|
|
|
|
|
+function TFixedSizeTextMeasurer.MeasureText(const aText: TTextString): TTextMeasures;
|
|
|
|
|
|
var
|
|
var
|
|
Scale: TTextUnits;
|
|
Scale: TTextUnits;
|
|
@@ -887,7 +897,7 @@ begin
|
|
Changed;
|
|
Changed;
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure TTextFont.SetName(AValue: string);
|
|
|
|
|
|
+procedure TTextFont.SetName(const AValue: string);
|
|
begin
|
|
begin
|
|
if FName=AValue then Exit;
|
|
if FName=AValue then Exit;
|
|
FName:=AValue;
|
|
FName:=AValue;
|
|
@@ -929,12 +939,21 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
function TTextFont.Clone(aOwner : TPersistent): TTextFont;
|
|
function TTextFont.Clone(aOwner : TPersistent): TTextFont;
|
|
-
|
|
|
|
begin
|
|
begin
|
|
Result:=TTextFontClass(Self.ClassType).Create(aOwner);
|
|
Result:=TTextFontClass(Self.ClassType).Create(aOwner);
|
|
Result.Assign(Self);
|
|
Result.Assign(Self);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+// IsSameFont
|
|
|
|
+//
|
|
|
|
+function TTextFont.IsSameFont(aFont : TTextFont) : Boolean;
|
|
|
|
+begin
|
|
|
|
+ Result := (FSize = aFont.FSize)
|
|
|
|
+ and (FAttrs = aFont.FAttrs)
|
|
|
|
+ and (FColor = aFont.FColor)
|
|
|
|
+ and (FName = aFont.FName);
|
|
|
|
+end;
|
|
|
|
+
|
|
{ TTextLayouter }
|
|
{ TTextLayouter }
|
|
|
|
|
|
procedure TTextLayouter.SetCullTreshold(AValue: TCullThreshold);
|
|
procedure TTextLayouter.SetCullTreshold(AValue: TCullThreshold);
|
|
@@ -1090,7 +1109,7 @@ begin
|
|
FRanges:=CreateRanges(Self);
|
|
FRanges:=CreateRanges(Self);
|
|
FMeasurer:=CreateMeasurer(Self);
|
|
FMeasurer:=CreateMeasurer(Self);
|
|
FSplitter:=CreateSplitter(Self);
|
|
FSplitter:=CreateSplitter(Self);
|
|
- FLineSpacing:=1.0;
|
|
|
|
|
|
+ FLineSpacing:=0;
|
|
HyphenationChar:='-';
|
|
HyphenationChar:='-';
|
|
AllowHyphenation:=False;
|
|
AllowHyphenation:=False;
|
|
end;
|
|
end;
|
|
@@ -1134,7 +1153,6 @@ begin
|
|
FBlocks.Clear;
|
|
FBlocks.Clear;
|
|
end;
|
|
end;
|
|
|
|
|
|
-{$IFDEF HasTObjectToString}
|
|
|
|
function TTextLayouter.ToString: RTLString;
|
|
function TTextLayouter.ToString: RTLString;
|
|
var
|
|
var
|
|
I : Integer;
|
|
I : Integer;
|
|
@@ -1143,7 +1161,6 @@ begin
|
|
For I:=0 to TextBlockCount-1 do
|
|
For I:=0 to TextBlockCount-1 do
|
|
Result:=Result+TextBlocks[I].ToString+sLineBreak;
|
|
Result:=Result+TextBlocks[I].ToString+sLineBreak;
|
|
end;
|
|
end;
|
|
-{$ENDIF}
|
|
|
|
|
|
|
|
function TTextLayouter.AddBlock(aOffset,aLength : SizeInt; aFont : TTextFont) : TTextBlock;
|
|
function TTextLayouter.AddBlock(aOffset,aLength : SizeInt; aFont : TTextFont) : TTextBlock;
|
|
|
|
|
|
@@ -1223,202 +1240,219 @@ begin
|
|
Font.FPColor:=aValue;
|
|
Font.FPColor:=aValue;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+// HandleNewLines
|
|
|
|
+//
|
|
procedure TTextLayouter.HandleNewLines;
|
|
procedure TTextLayouter.HandleNewLines;
|
|
-
|
|
|
|
var
|
|
var
|
|
i : Integer;
|
|
i : Integer;
|
|
aPos : sizeInt;
|
|
aPos : sizeInt;
|
|
- SplitPos : TTextSplitPoint;
|
|
|
|
- B,BN : TTextBlock;
|
|
|
|
-
|
|
|
|
|
|
+ splitPos : TTextSplitPoint;
|
|
|
|
+ B, BN : TTextBlock;
|
|
begin
|
|
begin
|
|
- I:=0;
|
|
|
|
- While (I<FBlocks.Count) do
|
|
|
|
- begin
|
|
|
|
|
|
+ i := 0;
|
|
|
|
+ while (i < FBlocks.Count) do
|
|
|
|
+ begin
|
|
B:=FBlocks[i];
|
|
B:=FBlocks[i];
|
|
- Repeat
|
|
|
|
- SplitPos:=Splitter.GetNextNewLine(Text,1+B.TextOffset);
|
|
|
|
- if SplitPos.Offset<>-1 then
|
|
|
|
- begin
|
|
|
|
- aPos:=Splitpos.offset+Splitpos.whitespace;
|
|
|
|
- BN:=B.Split(aPos);
|
|
|
|
- BN.ForceNewLine:=True;
|
|
|
|
- B.TextLen:=B.TextLen-SplitPos.WhiteSpace;
|
|
|
|
|
|
+ repeat
|
|
|
|
+ splitPos := Splitter.GetNextNewLine(Text, 1+B.TextOffset);
|
|
|
|
+ if splitPos.Offset <> -1 then
|
|
|
|
+ begin
|
|
|
|
+ aPos := splitPos.offset + splitPos.whitespace;
|
|
|
|
+ BN := B.Split(aPos);
|
|
|
|
+ BN.ForceNewLine := True;
|
|
|
|
+ B.TextLen := B.TextLen - splitPos.WhiteSpace;
|
|
B.TrimTrailingWhiteSpace;
|
|
B.TrimTrailingWhiteSpace;
|
|
- inc(I);
|
|
|
|
|
|
+ Inc(I);
|
|
FBlocks.Insert(I,BN);
|
|
FBlocks.Insert(I,BN);
|
|
- B:=BN;
|
|
|
|
- end;
|
|
|
|
- until SplitPos.Offset=-1;
|
|
|
|
|
|
+ B := BN;
|
|
|
|
+ end;
|
|
|
|
+ until (splitPos.Offset = -1);
|
|
Inc(I);
|
|
Inc(I);
|
|
- end;
|
|
|
|
|
|
+ end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+// FindWrapPosition
|
|
|
|
+//
|
|
// Returns true if the line is full.
|
|
// Returns true if the line is full.
|
|
-function TTextLayouter.FindWrapPosition(B : TTextBlock; S : String; var aPos : integer; var CurrPos : TTextPoint) : Boolean;
|
|
|
|
-
|
|
|
|
|
|
+function TTextLayouter.FindWrapPosition(aB : TTextBlock; const aStr : String; var aPos : integer; var aCurrXPos : TTextUnits) : Boolean;
|
|
var
|
|
var
|
|
lSplit : TTextSplitPoint;
|
|
lSplit : TTextSplitPoint;
|
|
lSize : TTextMeasures;
|
|
lSize : TTextMeasures;
|
|
wSpace : TTextUnits;
|
|
wSpace : TTextUnits;
|
|
- BlockWidth: TTextUnits;
|
|
|
|
- CurrPart : String;
|
|
|
|
|
|
+ lBlockWidth: TTextUnits;
|
|
|
|
+ lCurrPart : String;
|
|
maxLen : integer;
|
|
maxLen : integer;
|
|
useHyphen : Boolean;
|
|
useHyphen : Boolean;
|
|
-
|
|
|
|
begin
|
|
begin
|
|
- maxLen:=Length(S);
|
|
|
|
- BlockWidth:=0;
|
|
|
|
- UseHyphen:=False;
|
|
|
|
- Repeat
|
|
|
|
- lSplit:=Splitter.GetNextSplitPoint(S,aPos,UseHyphen);
|
|
|
|
- CurrPart:=Copy(S,aPos,lSplit.offset-aPos+1);
|
|
|
|
|
|
+ maxLen := Length(aStr);
|
|
|
|
+ lBlockWidth := 0;
|
|
|
|
+ UseHyphen := False;
|
|
|
|
+ repeat
|
|
|
|
+ lSplit := Splitter.GetNextSplitPoint(aStr, aPos, UseHyphen);
|
|
|
|
+ lCurrPart := Copy(aStr, aPos, lSplit.offset - aPos + 1);
|
|
if UseHyphen then
|
|
if UseHyphen then
|
|
- CurrPart:=CurrPart+HyphenationChar;
|
|
|
|
- lSize:=Measurer.MeasureText(CurrPart);
|
|
|
|
- Result:=CurrPos.X+lSize.Width>=Bounds.Width;
|
|
|
|
- if not Result then
|
|
|
|
- begin
|
|
|
|
- // CurrPart still fits on Line, add it
|
|
|
|
- BlockWidth:=BlockWidth+lSize.Width;
|
|
|
|
- CurrPos.x:=CurrPos.X+lSize.Width;
|
|
|
|
- // Update pos for GetNextSplitPoint.
|
|
|
|
- aPos:=lSplit.Offset+lSplit.whitespace+1;
|
|
|
|
- // Check if the whitespace would flow over:
|
|
|
|
- WSpace:=lSplit.whitespace*Measurer.WhitespaceWidth;
|
|
|
|
- Result:=(CurrPos.X+WSpace)>=Bounds.Width;
|
|
|
|
- if UseHyphen then
|
|
|
|
- B.Suffix:=HyphenationChar;
|
|
|
|
- if not Result then
|
|
|
|
- CurrPos.X:=CurrPos.X+WSpace;
|
|
|
|
- end
|
|
|
|
- else
|
|
|
|
- begin
|
|
|
|
|
|
+ lCurrPart := lCurrPart + HyphenationChar;
|
|
|
|
+
|
|
|
|
+ lSize := Measurer.MeasureText(lCurrPart);
|
|
|
|
+ Result := (aCurrXPos + lSize.Width >= Bounds.Width);
|
|
|
|
+ if Result then begin
|
|
// Currpart will no longer fit on the line. Attempt splitting, if we were not yet splitting.
|
|
// Currpart will no longer fit on the line. Attempt splitting, if we were not yet splitting.
|
|
- if (not UseHyphen) and AllowHyphenation then
|
|
|
|
- begin
|
|
|
|
|
|
+ if (not UseHyphen) and AllowHyphenation then begin
|
|
Result:=False;
|
|
Result:=False;
|
|
UseHyphen:=True;
|
|
UseHyphen:=True;
|
|
- end
|
|
|
|
- else
|
|
|
|
|
|
+ end else begin
|
|
// One word and it does not fit...
|
|
// One word and it does not fit...
|
|
if aPos=1 then
|
|
if aPos=1 then
|
|
aPos:=MaxLen
|
|
aPos:=MaxLen
|
|
end;
|
|
end;
|
|
|
|
+ end else begin
|
|
|
|
+ // lCurrPart still fits on Line, add it
|
|
|
|
+ lBlockWidth:=lBlockWidth+lSize.Width;
|
|
|
|
+ aCurrXPos += lSize.Width;
|
|
|
|
+ // Update pos for GetNextSplitPoint.
|
|
|
|
+ aPos := lSplit.Offset + lSplit.whitespace + 1;
|
|
|
|
+ // Check if the whitespace would flow over:
|
|
|
|
+ wSpace := lSplit.whitespace * Measurer.WhitespaceWidth;
|
|
|
|
+ Result := ((aCurrXPos + WSpace) >= Bounds.Width);
|
|
|
|
+ if UseHyphen then
|
|
|
|
+ aB.Suffix := HyphenationChar;
|
|
|
|
+ if not Result then
|
|
|
|
+ aCurrXPos += wSpace;
|
|
|
|
+ end;
|
|
until Result or (aPos>=MaxLen);
|
|
until Result or (aPos>=MaxLen);
|
|
end;
|
|
end;
|
|
|
|
|
|
-function TTextLayouter.WrapBlock(B: TTextBlock; S: String; var Idx: integer; var CurrPos: TTextPoint): Boolean;
|
|
|
|
-
|
|
|
|
|
|
+// WrapBlock
|
|
|
|
+//
|
|
|
|
+function TTextLayouter.WrapBlock(aB: TTextBlock; const aStr: String; var aIdx: integer; var aCurrPos: TTextPoint): Boolean;
|
|
var
|
|
var
|
|
- aPosOffset,aPos,MaxLen: integer;
|
|
|
|
- LineFull : Boolean;
|
|
|
|
- NB : TTextBlock;
|
|
|
|
- T : String;
|
|
|
|
-
|
|
|
|
-begin
|
|
|
|
- Result:=False;
|
|
|
|
- aPos:=1;
|
|
|
|
- aPosOffset:=1;
|
|
|
|
- maxLen:=Length(S);
|
|
|
|
|
|
+ lPosOffset, lPos, lMaxLen: integer;
|
|
|
|
+ lLineFull : Boolean;
|
|
|
|
+ nextBlock : TTextBlock;
|
|
|
|
+begin
|
|
|
|
+ Result := False;
|
|
|
|
+ lPos := 1;
|
|
|
|
+ lPosOffset := 1;
|
|
|
|
+ lMaxLen := Length(aStr);
|
|
// We can have multiple lines
|
|
// We can have multiple lines
|
|
- Repeat
|
|
|
|
- B.LayoutPos:=CurrPos;
|
|
|
|
- LineFull:=FindWrapPosition(B,S,aPos,CurrPos);
|
|
|
|
- // At this point, aPos is the maximum size that will fit.
|
|
|
|
- if aPos>=MaxLen then
|
|
|
|
- begin
|
|
|
|
|
|
+ repeat
|
|
|
|
+ aB.LayoutPos := aCurrPos;
|
|
|
|
+ lLineFull := FindWrapPosition(aB, aStr, lPos, aCurrPos.x);
|
|
|
|
+
|
|
|
|
+ // At this point, lPos is the maximum size that will fit.
|
|
|
|
+ if lPos >= lMaxLen then begin
|
|
|
|
+
|
|
// Correct size.
|
|
// Correct size.
|
|
- B.Size:=Measurer.MeasureText(B.Text);
|
|
|
|
- end
|
|
|
|
- else
|
|
|
|
|
|
+ aB.Size := Measurer.MeasureText(aB.Text);
|
|
|
|
+
|
|
|
|
+ end else begin
|
|
|
|
+
|
|
// We're not yet at the end, so we need to split
|
|
// We're not yet at the end, so we need to split
|
|
- begin
|
|
|
|
- Result:=True;
|
|
|
|
- NB:=B.Split(aPos-aPosOffset);
|
|
|
|
- T:=NB.Text;
|
|
|
|
- // Writeln('T new: >>',T,'<<');
|
|
|
|
- B.TrimTrailingWhiteSpace;
|
|
|
|
- T:=B.Text;
|
|
|
|
- // Writeln('T old: >>',T,'<<');
|
|
|
|
- B.Size:=Measurer.MeasureText(T);
|
|
|
|
- Inc(Idx);
|
|
|
|
- FBlocks.Insert(Idx,NB);
|
|
|
|
- NB.ForceNewLine:=True;
|
|
|
|
- aPosOffset:=NB.TextOffset;
|
|
|
|
- if LineFull then
|
|
|
|
- begin
|
|
|
|
- CurrPos.X:=0;
|
|
|
|
- CurrPos.Y:=CurrPos.Y+B.Size.Height+LineSpacing;
|
|
|
|
- end;
|
|
|
|
- B:=NB;
|
|
|
|
|
|
+ Result := True;
|
|
|
|
+
|
|
|
|
+ nextBlock := aB.Split(lPos - lPosOffset - 1);
|
|
|
|
+ aB.TrimTrailingWhiteSpace;
|
|
|
|
+ aB.Size := Measurer.MeasureText(aB.Text);
|
|
|
|
+
|
|
|
|
+ Inc(aIdx);
|
|
|
|
+ FBlocks.Insert(aIdx, nextBlock);
|
|
|
|
+
|
|
|
|
+ nextBlock.ForceNewLine := lLineFull;
|
|
|
|
+ lPosOffset := nextBlock.TextOffset;
|
|
|
|
+ if lLineFull then begin
|
|
|
|
+ aCurrPos.X := 0;
|
|
|
|
+ aCurrPos.Y += aB.Size.Height + LineSpacing;
|
|
|
|
+ end else begin
|
|
|
|
+ aCurrPos.X += aB.Size.Width;
|
|
end;
|
|
end;
|
|
- until (aPos>=MaxLen);
|
|
|
|
|
|
+ aB := nextBlock;
|
|
|
|
+
|
|
|
|
+ end;
|
|
|
|
+ until (lPos >= lMaxLen);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+// WrapLayout
|
|
|
|
+//
|
|
function TTextLayouter.WrapLayout : Boolean;
|
|
function TTextLayouter.WrapLayout : Boolean;
|
|
-
|
|
|
|
var
|
|
var
|
|
- CurrPos : TTextPoint; // value in pixels
|
|
|
|
- i: integer;
|
|
|
|
- lSize : TTextMeasures;
|
|
|
|
- B : TTextBlock;
|
|
|
|
|
|
+ lCurrPos : TTextPoint; // value in pixels
|
|
|
|
+ i : Integer;
|
|
|
|
+ currBlock : TTextBlock;
|
|
lText : TTextString;
|
|
lText : TTextString;
|
|
-
|
|
|
|
|
|
+ lastFont : TTextFont;
|
|
begin
|
|
begin
|
|
- Result:=False;
|
|
|
|
- CurrPos:=Default(TTextPoint);
|
|
|
|
- I:=0;
|
|
|
|
- While I<FBlocks.Count do
|
|
|
|
|
|
+ Result := False;
|
|
|
|
+
|
|
|
|
+ lastFont := nil;
|
|
|
|
+ lCurrPos.x := 0;
|
|
|
|
+ lCurrPos.y := 0;
|
|
|
|
+ i := 0;
|
|
|
|
+ while i < FBlocks.Count do
|
|
|
|
+ begin
|
|
|
|
+ currBlock := FBlocks[i];
|
|
|
|
+ if currBlock.ForceNewLine then
|
|
begin
|
|
begin
|
|
- B:=FBlocks[i];
|
|
|
|
- if B.ForceNewLine then
|
|
|
|
- CurrPos.X:=0;
|
|
|
|
- lText:=B.Text;
|
|
|
|
- Measurer.SetFont(B.Font);
|
|
|
|
- lSize:=Measurer.MeasureText(lText);
|
|
|
|
- if CurrPos.X+lSize.Width>Bounds.Width then
|
|
|
|
- Result:=WrapBlock(B,lText,I,CurrPos) or Result;
|
|
|
|
- inc(I);
|
|
|
|
|
|
+ lCurrPos.X := 0;
|
|
|
|
+ Result := True;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ if (lastFont = nil) or not lastFont.IsSameFont(currBlock.Font) then
|
|
|
|
+ begin
|
|
|
|
+ lastFont := currBlock.Font;
|
|
|
|
+ Measurer.SetFont(lastFont);
|
|
end;
|
|
end;
|
|
- B:=FBlocks[FBlocks.Count-1];
|
|
|
|
|
|
+ Measurer.SetFont(currBlock.Font);
|
|
|
|
+
|
|
|
|
+ lText := currBlock.Text;
|
|
|
|
+ currBlock.Size := Measurer.MeasureText(lText);
|
|
|
|
+ if lCurrPos.X + currBlock.Size.Width > Bounds.Width then
|
|
|
|
+ Result := WrapBlock(currBlock, lText, i, lCurrPos) or Result;
|
|
|
|
+ Inc(i);
|
|
|
|
+ end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+// NoWrapLayout
|
|
|
|
+//
|
|
function TTextLayouter.NoWrapLayout: Boolean;
|
|
function TTextLayouter.NoWrapLayout: Boolean;
|
|
-
|
|
|
|
var
|
|
var
|
|
- CurrPos : TTextPoint;
|
|
|
|
- CurrHeight : TTextUnits;
|
|
|
|
- i: integer;
|
|
|
|
- B : TTextBlock;
|
|
|
|
- lText : TTextString;
|
|
|
|
-
|
|
|
|
|
|
+ currPos : TTextPoint;
|
|
|
|
+ currHeight : TTextUnits;
|
|
|
|
+ i : Integer;
|
|
|
|
+ currBlock : TTextBlock;
|
|
|
|
+ lastFont : TTextFont;
|
|
begin
|
|
begin
|
|
Result:=False;
|
|
Result:=False;
|
|
- CurrPos.X:=0;
|
|
|
|
- CurrPos.Y:=0;
|
|
|
|
- CurrHeight:=0;
|
|
|
|
- I:=0;
|
|
|
|
- While I<FBlocks.Count do
|
|
|
|
|
|
+
|
|
|
|
+ lastFont := nil;
|
|
|
|
+ currPos.X := 0;
|
|
|
|
+ currPos.Y := 0;
|
|
|
|
+ currHeight := 0;
|
|
|
|
+ i := 0;
|
|
|
|
+ while i < FBlocks.Count do
|
|
|
|
+ begin
|
|
|
|
+ currBlock := FBlocks[i];
|
|
|
|
+ if currBlock.ForceNewLine then
|
|
begin
|
|
begin
|
|
- B:=FBlocks[i];
|
|
|
|
- if B.ForceNewLine then
|
|
|
|
- begin
|
|
|
|
- CurrPos.X:=0;
|
|
|
|
- CurrPos.Y:=CurrPos.Y+CurrHeight+LineSpacing;
|
|
|
|
- CurrHeight:=0;
|
|
|
|
- Result:=True;
|
|
|
|
- end;
|
|
|
|
- lText:=B.Text;
|
|
|
|
- Measurer.SetFont(B.Font);
|
|
|
|
- B.Size:=Measurer.MeasureText(lText);
|
|
|
|
- B.LayoutPos:=CurrPos;
|
|
|
|
- // Shift pos
|
|
|
|
- CurrPos.X:=CurrPos.X+B.Width;
|
|
|
|
- if B.Height>CurrHeight then
|
|
|
|
- CurrHeight:=B.Height;
|
|
|
|
- inc(I);
|
|
|
|
|
|
+ currPos.X := 0;
|
|
|
|
+ currPos.Y += currHeight + LineSpacing;
|
|
|
|
+ currHeight:=0;
|
|
|
|
+ Result := True;
|
|
end;
|
|
end;
|
|
|
|
+
|
|
|
|
+ if (lastFont = nil) or not lastFont.IsSameFont(currBlock.Font) then
|
|
|
|
+ begin
|
|
|
|
+ lastFont := currBlock.Font;
|
|
|
|
+ Measurer.SetFont(lastFont);
|
|
|
|
+ end;
|
|
|
|
+ currBlock.Size := Measurer.MeasureText(currBlock.Text);
|
|
|
|
+ currBlock.LayoutPos := currPos;
|
|
|
|
+
|
|
|
|
+ // Shift pos
|
|
|
|
+ currPos.X += currBlock.Width;
|
|
|
|
+ if currBlock.Height > currHeight then
|
|
|
|
+ currHeight := currBlock.Height;
|
|
|
|
+ Inc(i);
|
|
|
|
+ end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
@@ -1436,40 +1470,42 @@ function TTextLayouter.Execute: integer;
|
|
Inc(Result);
|
|
Inc(Result);
|
|
end;
|
|
end;
|
|
|
|
|
|
- Function CalcNeededHeight : TTextUnits;
|
|
|
|
-
|
|
|
|
|
|
+ function CalcNeededHeight : TTextUnits;
|
|
var
|
|
var
|
|
- I : Integer;
|
|
|
|
|
|
+ i : Integer;
|
|
NewH : TTextUnits;
|
|
NewH : TTextUnits;
|
|
-
|
|
|
|
|
|
+ block : TTextBlock;
|
|
begin
|
|
begin
|
|
- Result:=0;
|
|
|
|
- For I:=0 to TextBlockCount-1 do
|
|
|
|
- begin
|
|
|
|
- With TextBlocks[i] do
|
|
|
|
- NewH:=LayoutPos.Y+Size.Height;
|
|
|
|
- if NewH>Result then
|
|
|
|
- Result:=NewH;
|
|
|
|
- end;
|
|
|
|
|
|
+ Result := 0;
|
|
|
|
+ for i := 0 to TextBlockCount-1 do
|
|
|
|
+ begin
|
|
|
|
+ block := TextBlocks[i];
|
|
|
|
+ NewH := block.LayoutPos.Y + block.Size.Height;
|
|
|
|
+ if NewH > Result then
|
|
|
|
+ Result := NewH;
|
|
|
|
+ end;
|
|
end;
|
|
end;
|
|
|
|
|
|
begin
|
|
begin
|
|
Reset;
|
|
Reset;
|
|
HandleRanges;
|
|
HandleRanges;
|
|
HandleNewLines;
|
|
HandleNewLines;
|
|
|
|
+
|
|
if WordWrap then
|
|
if WordWrap then
|
|
WrapLayout
|
|
WrapLayout
|
|
else
|
|
else
|
|
NoWrapLayout;
|
|
NoWrapLayout;
|
|
|
|
+
|
|
|
|
+ ApplyVertTextAlignment;
|
|
|
|
+ ApplyHorzTextAlignment;
|
|
|
|
+
|
|
|
|
+ // can only cull after alignment
|
|
if StretchMode = TStretchMode.smDontStretch then
|
|
if StretchMode = TStretchMode.smDontStretch then
|
|
CullTextOutOfBoundsVertically
|
|
CullTextOutOfBoundsVertically
|
|
- else
|
|
|
|
- ApplyStretchMode(CalcNeededHeight);
|
|
|
|
- // We do this after vertical culling, potentially less blocks...
|
|
|
|
|
|
+ else ApplyStretchMode(CalcNeededHeight);
|
|
CullTextOutOfBoundsHorizontally;
|
|
CullTextOutOfBoundsHorizontally;
|
|
- ApplyVertTextAlignment;
|
|
|
|
- ApplyHorzTextAlignment;
|
|
|
|
- Result:=TextBlockCount;
|
|
|
|
|
|
+
|
|
|
|
+ Result := TextBlockCount;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TTextLayouter.Execute(const aText: String): Integer;
|
|
function TTextLayouter.Execute(const aText: String): Integer;
|
|
@@ -1608,15 +1644,14 @@ begin
|
|
Result:=yMax;
|
|
Result:=yMax;
|
|
end;
|
|
end;
|
|
|
|
|
|
-function TTextLayouter.GetTotalSize: TFresnelPoint;
|
|
|
|
|
|
+function TTextLayouter.GetTotalSize: TSizeF;
|
|
begin
|
|
begin
|
|
- Result.X:=GetTotalWidth;
|
|
|
|
- Result.Y:=GetTotalHeight;
|
|
|
|
|
|
+ Result:=TSizeF.Create(GetTotalWidth,GetTotalHeight);
|
|
end;
|
|
end;
|
|
|
|
|
|
-function TTextLayouter.GetBoundsRect: TFresnelRect;
|
|
|
|
|
|
+function TTextLayouter.GetBoundsRect: TRectF;
|
|
begin
|
|
begin
|
|
- Result:=TFresnelRect.Create(GetMinLeft,GetMinTop,GetMaxRight,GetMaxBottom);
|
|
|
|
|
|
+ Result:=TRectF.Create(GetMinLeft,GetMinTop,GetMaxRight,GetMaxBottom);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TTextLayouter.ApplyStretchMode(const ADesiredHeight: TTextUnits);
|
|
procedure TTextLayouter.ApplyStretchMode(const ADesiredHeight: TTextUnits);
|
|
@@ -1653,7 +1688,7 @@ var
|
|
i: integer;
|
|
i: integer;
|
|
lBlock: TTextBlock;
|
|
lBlock: TTextBlock;
|
|
MaxHeight, vPos : TTextUnits;
|
|
MaxHeight, vPos : TTextUnits;
|
|
- lRemainingHeight: TFresnelLength;
|
|
|
|
|
|
+ lRemainingHeight: single;
|
|
d: single;
|
|
d: single;
|
|
doDelete : Boolean;
|
|
doDelete : Boolean;
|
|
aSize : TTextMeasures;
|
|
aSize : TTextMeasures;
|
|
@@ -1788,6 +1823,8 @@ var
|
|
for idx := lList.Count-1 downto 0 do
|
|
for idx := lList.Count-1 downto 0 do
|
|
begin
|
|
begin
|
|
b := TTextBlock(lList[idx]);
|
|
b := TTextBlock(lList[idx]);
|
|
|
|
+ if b.Size.Width = 0 then
|
|
|
|
+ b.Size := Measurer.MeasureText(b.Text);
|
|
b.LayoutPos.X := lXOffset - b.Size.Width;
|
|
b.LayoutPos.X := lXOffset - b.Size.Width;
|
|
lXOffset := b.LayoutPos.X;
|
|
lXOffset := b.LayoutPos.X;
|
|
end;
|
|
end;
|
|
@@ -1804,6 +1841,8 @@ var
|
|
for idx := 0 to lList.Count-1 do
|
|
for idx := 0 to lList.Count-1 do
|
|
begin
|
|
begin
|
|
b := TTextBlock(lList[idx]);
|
|
b := TTextBlock(lList[idx]);
|
|
|
|
+ if b.Size.Width = 0 then
|
|
|
|
+ b.Size := Measurer.MeasureText(b.Text);
|
|
lTotalWidth := lTotalWidth + b.Width;
|
|
lTotalWidth := lTotalWidth + b.Width;
|
|
end;
|
|
end;
|
|
lXOffset := (Bounds.Width - lTotalWidth) / 2;
|
|
lXOffset := (Bounds.Width - lTotalWidth) / 2;
|
|
@@ -1847,193 +1886,124 @@ var
|
|
*)
|
|
*)
|
|
|
|
|
|
begin
|
|
begin
|
|
|
|
+ if FBlocks.Count = 0 then
|
|
|
|
+ Exit;
|
|
|
|
+
|
|
lList := TFPList.Create;
|
|
lList := TFPList.Create;
|
|
try
|
|
try
|
|
- lLastYPos := 0;
|
|
|
|
- i := 0;
|
|
|
|
- While I<FBlocks.Count do
|
|
|
|
- begin
|
|
|
|
- tb := FBlocks[i];
|
|
|
|
- if tb.LayoutPos.Y = lLastYPos then // still on the same text line
|
|
|
|
- lList.Add(tb)
|
|
|
|
- else
|
|
|
|
|
|
+ tb := FBlocks[0];
|
|
|
|
+ lLastYPos := tb.LayoutPos.y;
|
|
|
|
+ lList.Add(tb);
|
|
|
|
+ i := 1;
|
|
|
|
+ while i < FBlocks.Count do
|
|
|
|
+ begin
|
|
|
|
+ tb := FBlocks[i];
|
|
|
|
+ if tb.LayoutPos.Y = lLastYPos then // still on the same text line
|
|
|
|
+ lList.Add(tb)
|
|
|
|
+ else
|
|
|
|
+ begin
|
|
|
|
+ { a new line has started - process what we have collected in lList }
|
|
|
|
+ case HorizontalAlign of
|
|
|
|
+ TTextAlign.Leading: ; // Nothing to do
|
|
|
|
+ TTextAlign.Trailing: ProcessRightJustified;
|
|
|
|
+ TTextAlign.Center: ProcessCentered;
|
|
|
|
+ // taWidth: ProcessWidth;
|
|
|
|
+ end;
|
|
|
|
+ lList.Clear;
|
|
|
|
+ lLastYPos := tb.LayoutPos.Y;
|
|
|
|
+ lList.Add(tb)
|
|
|
|
+ end; { if..else }
|
|
|
|
+ Inc(i);
|
|
|
|
+ end; { while i<fblocks.count }
|
|
|
|
+
|
|
|
|
+ { process the last text line's items }
|
|
|
|
+ if lList.Count > 0 then
|
|
begin
|
|
begin
|
|
- { a new line has started - process what we have collected in lList }
|
|
|
|
case HorizontalAlign of
|
|
case HorizontalAlign of
|
|
TTextAlign.Leading: ; // Nothing to do
|
|
TTextAlign.Leading: ; // Nothing to do
|
|
TTextAlign.Trailing: ProcessRightJustified;
|
|
TTextAlign.Trailing: ProcessRightJustified;
|
|
TTextAlign.Center: ProcessCentered;
|
|
TTextAlign.Center: ProcessCentered;
|
|
// taWidth: ProcessWidth;
|
|
// taWidth: ProcessWidth;
|
|
end;
|
|
end;
|
|
- lList.Clear;
|
|
|
|
- lLastYPos := tb.LayoutPos.Y;
|
|
|
|
- lList.Add(tb)
|
|
|
|
- end; { if..else }
|
|
|
|
- inc(I);
|
|
|
|
- end; { while i<fblocks.count }
|
|
|
|
-
|
|
|
|
- { process the last text line's items }
|
|
|
|
- if lList.Count > 0 then
|
|
|
|
- begin
|
|
|
|
- case HorizontalAlign of
|
|
|
|
- TTextAlign.Leading: ; // Nothing to do
|
|
|
|
- TTextAlign.Trailing: ProcessRightJustified;
|
|
|
|
- TTextAlign.Center: ProcessCentered;
|
|
|
|
- // taWidth: ProcessWidth;
|
|
|
|
end;
|
|
end;
|
|
- end;
|
|
|
|
|
|
|
|
finally
|
|
finally
|
|
llist.Free;
|
|
llist.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
-{ this affects only Y coordinate of text blocks }
|
|
|
|
|
|
+// ApplyVertTextAlignment
|
|
|
|
+//
|
|
|
|
+// this affects only Y coordinate of text blocks
|
|
procedure TTextLayouter.ApplyVertTextAlignment;
|
|
procedure TTextLayouter.ApplyVertTextAlignment;
|
|
var
|
|
var
|
|
- i: integer;
|
|
|
|
- tb: TTextBlock;
|
|
|
|
- lList: TFPList;
|
|
|
|
- lLastYPos: TTextUnits;
|
|
|
|
- lTotalHeight: TTextUnits;
|
|
|
|
- lYOffset: TTextUnits;
|
|
|
|
|
|
+ lineHeight : TTextUnits;
|
|
|
|
|
|
- procedure ProcessTop;
|
|
|
|
|
|
+ function ComputeTotalHeight : TTextUnits;
|
|
var
|
|
var
|
|
- idx: integer;
|
|
|
|
- b: TTextBlock;
|
|
|
|
|
|
+ i : Integer;
|
|
|
|
+ tb : TTextBlock;
|
|
|
|
+ lLastYPos : TTextUnits;
|
|
begin
|
|
begin
|
|
- if lList.Count = 0 then
|
|
|
|
- Exit;
|
|
|
|
- for idx := 0 to lList.Count-1 do
|
|
|
|
- begin
|
|
|
|
- b := TTextBlock(lList[idx]);
|
|
|
|
- b.LayoutPos.Y := lYOffset;
|
|
|
|
- end;
|
|
|
|
- lYOffset := lYOffset + LineSpacing + b.Height + b.Descender;
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
- procedure ProcessCenter;
|
|
|
|
- var
|
|
|
|
- idx: integer;
|
|
|
|
- b: TTextBlock;
|
|
|
|
- begin
|
|
|
|
- for idx := 0 to lList.Count-1 do
|
|
|
|
- begin
|
|
|
|
- b := TTextBlock(lList[idx]);
|
|
|
|
- b.LayoutPos.Y := lYOffset;
|
|
|
|
- end;
|
|
|
|
- lYOffset := lYOffset + LineSpacing + b.Height + b.Descender;
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
- procedure ProcessBottom;
|
|
|
|
- var
|
|
|
|
- idx: integer;
|
|
|
|
- b: TTextBlock;
|
|
|
|
- begin
|
|
|
|
- for idx := 0 to lList.Count-1 do
|
|
|
|
- begin
|
|
|
|
- b := TTextBlock(lList[idx]);
|
|
|
|
- b.LayoutPos.Y := lYOffset;
|
|
|
|
|
|
+ tb := FBlocks[0];
|
|
|
|
+ lLastYPos := tb.LayoutPos.y;
|
|
|
|
+ Result := lineHeight;
|
|
|
|
+
|
|
|
|
+ for i := 1 to FBlocks.Count-1 do begin
|
|
|
|
+ tb := FBlocks[i];
|
|
|
|
+ if tb.LayoutPos.Y <> lLastYPos then begin
|
|
|
|
+ Result += lineHeight + LineSpacing;
|
|
|
|
+ lLastYPos := tb.LayoutPos.Y;
|
|
|
|
+ end;
|
|
end;
|
|
end;
|
|
- lYOffset := lYOffset - LineSpacing - b.Height - b.Descender;
|
|
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+var
|
|
|
|
+ i: integer;
|
|
|
|
+ tb: TTextBlock;
|
|
|
|
+ lLastYPos: TTextUnits;
|
|
|
|
+ lTotalHeight: TTextUnits;
|
|
|
|
+ lYOffset: TTextUnits;
|
|
begin
|
|
begin
|
|
if FBlocks.Count = 0 then
|
|
if FBlocks.Count = 0 then
|
|
Exit;
|
|
Exit;
|
|
- lList := TFPList.Create;
|
|
|
|
- try
|
|
|
|
- lLastYPos := FBlocks[FBlocks.Count-1].LayoutPos.Y; // last textblock's Y coordinate
|
|
|
|
- lTotalHeight := 0;
|
|
|
|
|
|
|
|
- Case VerticalAlign of
|
|
|
|
- TTextAlign.Leading:
|
|
|
|
- begin
|
|
|
|
- // Nothing to do
|
|
|
|
- end;
|
|
|
|
|
|
+ // We vertically reflow the text blocks, blocks LayoutPos.y is used to determine
|
|
|
|
+ // which blocks are on the same line (actual value discarded)
|
|
|
|
+ // Current limitation: assumes all blocks are of same height (ignores block height)
|
|
|
|
+ // because block height will vary on actual content, which can be problematic
|
|
|
|
+ // (f.i. if a line has only '-' characters)
|
|
|
|
|
|
- TTextAlign.Trailing:
|
|
|
|
- begin
|
|
|
|
- lYOffset := Bounds.Height;
|
|
|
|
- for i := FBlocks.Count-1 downto 0 do
|
|
|
|
- begin
|
|
|
|
- tb := FBlocks[i];
|
|
|
|
- if i = FBlocks.Count-1 then
|
|
|
|
- lYOffset := lYOffset - tb.Height - tb.Descender; // only need to do this for one line
|
|
|
|
- if tb.LayoutPos.Y = lLastYPos then // still on the same text line
|
|
|
|
- lList.Add(tb)
|
|
|
|
- else
|
|
|
|
- begin
|
|
|
|
- { a new line has started - process what we have collected in lList }
|
|
|
|
- ProcessBottom;
|
|
|
|
-
|
|
|
|
- lList.Clear;
|
|
|
|
- lLastYPos := tb.LayoutPos.Y;
|
|
|
|
- lList.Add(tb)
|
|
|
|
- end; { if..else }
|
|
|
|
- end; { for i }
|
|
|
|
- end; // TTextAlign.Trailing:
|
|
|
|
-
|
|
|
|
- TTextAlign.Center:
|
|
|
|
- begin
|
|
|
|
- { First, collect the total height of all the text lines }
|
|
|
|
- lTotalHeight := 0;
|
|
|
|
- lLastYPos := 0;
|
|
|
|
- for i := 0 to FBlocks.Count-1 do
|
|
|
|
- begin
|
|
|
|
- tb := FBlocks[i];
|
|
|
|
- // Writeln('AlignV: block ',i,' h:',tb.Height,' desc :',tb.Descender);
|
|
|
|
- if i = 0 then // do this only for the first block
|
|
|
|
- lTotalHeight := tb.Height; // + tb.Descender;
|
|
|
|
- if tb.LayoutPos.Y = lLastYPos then // still on the same text line
|
|
|
|
- Continue
|
|
|
|
- else
|
|
|
|
- begin
|
|
|
|
- { a new line has started - process what we have collected in lList }
|
|
|
|
- lTotalHeight := lTotalHeight + LineSpacing + tb.Height // + tb.Descender;
|
|
|
|
- end; { if..else }
|
|
|
|
- lLastYPos := tb.LayoutPos.Y;
|
|
|
|
- end; { for i }
|
|
|
|
|
|
+ lineHeight := Measurer.LineHeight;
|
|
|
|
|
|
- { Now process them line-by-line }
|
|
|
|
- lList.Clear;
|
|
|
|
- lYOffset := (Bounds.Height - lTotalHeight) / 2;
|
|
|
|
- // Writeln('AlignV: Bounds.Height : ',Bounds.Height:8:2,' total : ',lTotalHeight :8:2,',offset: ',lYOffset:8:2);
|
|
|
|
|
|
+ case VerticalAlign of
|
|
|
|
+ TTextAlign.Center :
|
|
|
|
+ lYOffset := (Bounds.Height - ComputeTotalHeight) / 2;
|
|
|
|
|
|
- lLastYPos := 0;
|
|
|
|
- for i := 0 to FBlocks.Count-1 do
|
|
|
|
- begin
|
|
|
|
- tb := FBlocks[i];
|
|
|
|
- if tb.LayoutPos.Y = lLastYPos then // still on the same text line
|
|
|
|
- lList.Add(tb)
|
|
|
|
- else
|
|
|
|
- begin
|
|
|
|
- { a new line has started - process what we have collected in lList }
|
|
|
|
- ProcessCenter;
|
|
|
|
|
|
+ TTextAlign.Trailing :
|
|
|
|
+ lYOffset := Bounds.Height - ComputeTotalHeight;
|
|
|
|
+ else
|
|
|
|
+ // TTextAlign.Leading & fallback
|
|
|
|
+ lYOffset := 0;
|
|
|
|
+ end;
|
|
|
|
|
|
- lList.Clear;
|
|
|
|
- lLastYPos := tb.LayoutPos.Y;
|
|
|
|
- lList.Add(tb)
|
|
|
|
- end; { if..else }
|
|
|
|
- end; { for i }
|
|
|
|
- end; // TTextAlign.Center
|
|
|
|
- end; // Case
|
|
|
|
|
|
+ tb := FBlocks[0];
|
|
|
|
+ lLastYPos := tb.LayoutPos.y;
|
|
|
|
+ tb.LayoutPos.y := lYOffset;
|
|
|
|
+ //Writeln('yOffset block 0 ', lYOffset, ' for bounds height ', bounds.Height);
|
|
|
|
|
|
- { process the last text line's items }
|
|
|
|
- if lList.Count > 0 then
|
|
|
|
- begin
|
|
|
|
- case VerticalAlign of
|
|
|
|
- TTextAlign.Leading: ProcessTop;
|
|
|
|
- TTextAlign.Center: ProcessCenter;
|
|
|
|
- TTextAlign.Trailing: ProcessBottom;
|
|
|
|
- end;
|
|
|
|
|
|
+ for i := 1 to FBlocks.Count-1 do begin
|
|
|
|
+ tb := FBlocks[i];
|
|
|
|
+ if tb.LayoutPos.y = lLastYPos then
|
|
|
|
+ tb.LayoutPos.y := lYOffset
|
|
|
|
+ else begin
|
|
|
|
+ lLastYPos := tb.LayoutPos.y;
|
|
|
|
+ lYOffset += lineHeight + LineSpacing;
|
|
|
|
+ tb.LayoutPos.y := lYOffset;
|
|
end;
|
|
end;
|
|
- finally
|
|
|
|
- lList.Free;
|
|
|
|
|
|
+ //Writeln(lYOffset);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
-
|
|
|
|
end.
|
|
end.
|
|
|
|
|