FastReport_2022_VCL/Source/frxSVGBase.pas
2024-01-01 16:13:08 +01:00

1802 lines
43 KiB
ObjectPascal

{******************************************}
{ }
{ FastReport VCL }
{ SVG Base Elements }
{ }
{ Copyright (c) 1998-2021 }
{ by Fast Reports Inc. }
{ }
{******************************************}
unit frxSVGBase;
interface
{$I frx.inc}
uses
Windows, Classes, Graphics, Types,
frxSVGHelpers, frxSVGCanvas, frxSVGColor,
frxHelpers, frxSVGElement, frxSVGComponents, frxCSSStyle;
type
TSVGBaseObj = class(TSVGElementObj)
private
FCanvasClipList: TOwnObjList;
function GetStrokeData: TSVGStrokeData;
procedure SetFiller(DrawType: TSVGDrawType; SVGPaint: TSVGPaint; Opacity: Single; const Graphics: TSVGCanvas);
protected
FPath: TSVGCanvasPath;
procedure ConstructPath; override;
procedure CalcClipPathList;
function CalcSelfBounds: TSingleBounds; override;
procedure DoPaintToGraphics(Graphics: TSVGCanvas); virtual;
procedure PaintToPath(Path: TSVGCanvasPath); virtual;
public
destructor Destroy; override;
procedure PaintToGraphics(Graphics: TSVGCanvas); virtual;
end;
Tel_svg = class(TSVGBaseObj)
private
FRootMatrix: TSVGTransform;
procedure CalcRootMatrix;
function CalcExternalTranslateMatrix: TSVGTransform; virtual;
function CalcExternalScaleMatrix: TSVGTransform; virtual;
function CalcAngleMatrix: TSVGTransform; virtual;
private
function GetViewBox: TSingleBounds;
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
function CalcSelfBounds: TSingleBounds; override;
function CalcCenteredShift(Offset: TSinglePoint): TSinglePoint; virtual;
public
constructor Create; override;
procedure CalculateMatrices; override;
function GetHeight: Single; override;
function GetWidth: Single; override;
function GetViewBoxWidth: Single; virtual;
function GetViewBoxHeight: Single; virtual;
function GetNormalizedViewBoxDiagonal: Single;
property RootMatrix: TSVGTransform read FRootMatrix;
end;
TSVGRootObj = class(Tel_svg)
private
FSource: string;
FFileName: string;
FStyleList: TfrxCSSList;
FExternalOpacity: Single;
FExternalAngle: Single;
FExternalFontSize: Single;
FExternalFontName: string;
FExternalBounds: TSingleBounds;
FExternalScale: TSinglePoint;
FExternalCentered: Boolean;
function CalcExternalTranslateMatrix: TSVGTransform; override;
function CalcExternalScaleMatrix: TSVGTransform; override;
function CalcAngleMatrix: TSVGTransform; override;
procedure SetExternalBounds(const Value: TSingleBounds);
procedure Paint(const Graphics: TSVGCanvas);
procedure SetExternalAngle(const Value: Single);
procedure SetExternalFontName(const Value: string);
procedure SetExternalFontSize(const Value: Single);
procedure SetExternalOpacity(const Value: Single);
procedure SetExternalScale(const Value: TSinglePoint);
procedure SetExternalCentered(const Value: Boolean);
protected
FSVGCanvasClass: TSVGCanvasClass;
FIsPrepared: Boolean;
procedure AssignTo(Dest: TPersistent); override;
procedure AssignStylesApplyingSequenceTo(Obj: TSVGElementObj); override; // Empty
function New(Parent: TSVGElementObj): TSVGElementObj; override;
function GetCanvasClass: TSVGCanvasClass; override;
function CalcCenteredShift(Offset: TSinglePoint): TSinglePoint; override;
function IsSizeless: Boolean;
procedure Prepare;
public
constructor Create; override;
constructor Create(SVGCanvasClass: TSVGCanvasClass); overload;
destructor Destroy; override;
procedure Clear; override;
function IsRoot: Boolean; override;
procedure ReadStyle(const Node: TfrxSVGXMLItem);
procedure ClarifyOwnShorthandProperties; override;
procedure LoadFromText(const Text: string);
procedure LoadFromFile(const FileName: string);
procedure LoadFromStream(Stream: TStream); overload;
procedure SaveToFile(const FileName: string);
procedure SaveToStream(Stream: TStream);
procedure PaintTo(DC: HDC);
function GetHeight: Single; override;
function GetWidth: Single; override;
function GetOuterHeight: Single;
function GetOuterWidth: Single;
function GetHostViewBoxWidth: Single; override;
function GetHostViewBoxHeight: Single; override;
function GetHostNormalizedDiagonal: Single; override;
property StyleList: TfrxCSSList read FStyleList;
property ExternalOpacity: Single read FExternalOpacity write SetExternalOpacity;
property ExternalAngle: Single write SetExternalAngle; // Degree
property ExternalFontSize: Single read FExternalFontSize write SetExternalFontSize;
property ExternalFontName: string read FExternalFontName write SetExternalFontName;
property ExternalBounds: TSingleBounds read FExternalBounds write SetExternalBounds;
property ExternalScale: TSinglePoint write SetExternalScale;
property ExternalCentered: Boolean write SetExternalCentered;
property Source: string read FSource;
end;
Tel_g = class(TSVGBaseObj)
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
public
constructor Create; override;
end;
Tel_a = class(TSVGBaseObj)
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
public
constructor Create; override;
end;
Tel_switch = class(TSVGBaseObj)
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
function IsAnyChildHaveSystemLanguage: boolean;
procedure SwitchChildren; override;
procedure GetLanguage(out Language, PartLanguage: string);
public
constructor Create; override;
end;
Tel_use = class(TSVGBaseObj)
protected
FHrefObj: TSVGElementObj;
function New(Parent: TSVGElementObj): TSVGElementObj; override;
function IsHrefObjExists: Boolean;
public
constructor Create; override;
procedure Construct;
procedure PaintToGraphics(Graphics: TSVGCanvas); override;
procedure PaintToPath(Path: TSVGCanvasPath); override;
end;
Tel_rect = class(TSVGBaseObj)
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
procedure ConstructPath; override;
public
constructor Create; override;
end;
Tel_line = class(TSVGBaseObj)
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
procedure ConstructPath; override;
public
constructor Create; override;
end;
Tel_polyline = class(TSVGBaseObj)
private
FPoints: TSinglePointDynArray;
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
procedure ConstructPath; override;
function IsHasPoints: Boolean;
public
constructor Create; override;
end;
Tel_polygon = class(Tel_polyline)
private
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
procedure ConstructPath; override;
public
constructor Create; override;
end;
Tel_circle = class(TSVGBaseObj)
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
procedure ConstructPath; override;
public
constructor Create; override;
end;
Tel_ellipse = class(TSVGBaseObj)
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
procedure ConstructPath; override;
public
constructor Create; override;
end;
Tel_image = class(TSVGBaseObj)
private
FImage: TSVGCanvasImage;
FStream: TMemoryStream;
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
function IsFoundImage(S: string): boolean;
function CalcSelfBounds: TSingleBounds; override;
procedure ConstructPath; override;
public
constructor Create; override;
destructor Destroy; override;
procedure PaintToGraphics(Graphics: TSVGCanvas); override;
end;
Tel_clipPath = class(TSVGBaseObj)
private
FInnerClipPath: TSVGCanvasPath;
FTransformedPath: TSVGCanvasPath;
FLastMatrix: TSVGTransform;
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
procedure ConstructClipPath;
public
constructor Create; override;
destructor Destroy; override;
function GetInnerClipPath(BoundingBox: TSingleBounds; UserMatrix: TSVGTransform): TSVGCanvasPath;
end;
TSVGCustomText = class(TSVGBaseObj)
private
FText: string;
FTextWidth: Single;
FDecorations: TTextDecorations;
FTextOrigin: TTextOrigin;
FFontHeight: Single;
function GetCompleteWidth: Single;
procedure SetSize;
function IsInTextPath: Boolean;
protected
procedure AssignTo(Dest: TPersistent); override;
procedure ConstructPath; override;
procedure DoPaintToGraphics(Graphics: TSVGCanvas); override;
function GetFontData: TSVGFontData;
procedure ClearDecorations;
procedure ReadTextOrigin;
procedure DeleteCRLF;
public
class function CreatePlainText(AParent: TSVGElementObj; ChildNodeText: string;
ChildNodeIndex, ChildNodeCount: Integer): TSVGCustomText;
destructor Destroy; override;
end;
Tel_text = class(TSVGCustomText)
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
public
constructor Create; override;
end;
Tel_tspan = class(TSVGCustomText)
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
public
constructor Create; override;
end;
Tel_textPath = class(TSVGCustomText)
protected
function New(Parent: TSVGElementObj): TSVGElementObj; override;
procedure ConstructPath; override;
public
constructor Create; override;
end;
implementation
uses
Math, SysUtils, StrUtils,
frxSVGParse, frxSVGPaint, frxSVGPath, frxUtils, frxSVGGDIPCanvas, frxNetUtils;
const
DefaultStrokeWidth = 1.0;
DefaultStrokeDashOffset = 0.0;
{ TSVGBaseObj }
destructor TSVGBaseObj.Destroy;
begin
FPath.Free;
FCanvasClipList.Free;
inherited Destroy;
end;
procedure TSVGBaseObj.DoPaintToGraphics(Graphics: TSVGCanvas);
begin
Graphics.PaintPath(FPath, atSpecificWord(at_fill_rule));
end;
procedure TSVGBaseObj.CalcClipPathList;
var
Path: TSVGElementObj;
ClipURIHost: TSVGElementObj;
ClipRoot: Tel_clipPath;
CanvasPath, CP: TSVGCanvasPath;
begin
if Assigned(FCanvasClipList) then
FCanvasClipList.Clear
else
FCanvasClipList := TOwnObjList.Create;
ClipURIHost := Self;
while (ClipURIHost <> nil) and not (ClipURIHost is Tel_svg) do
begin
if (ClipURIHost is TSVGBaseObj) then
begin
Path := FRoot.FindByID(ClipURIHost.atURI(at_clip_path));
if Path is Tel_clipPath then
begin
ClipRoot := Tel_clipPath(Path);
CanvasPath := ClipRoot.GetInnerClipPath(ClipURIHost.GetBounds, ClipURIHost.InnerMatrix);
CP := CanvasPath.Clone;
CP.Matrix := ClipRoot.CompleteMatrix;
FCanvasClipList.Insert(0, CP);
end;
end;
ClipURIHost := ClipURIHost.Parent;
end;
end;
function TSVGBaseObj.CalcSelfBounds: TSingleBounds;
begin
if FPath = nil then
Result := EmptySingleBounds
else
Result := FPath.Bounds;
end;
procedure TSVGBaseObj.PaintToGraphics(Graphics: TSVGCanvas);
var
i: Integer;
CP: TSVGCanvasPath;
begin
if FPath = nil then
Exit;
CalcClipPathList;
try
if Assigned(FCanvasClipList) then
for i := 0 to FCanvasClipList.Count - 1 do
begin
CP := TSVGCanvasPath(FCanvasClipList[i]);
Graphics.SetTransform(CP.Matrix);
if i = 0 then
Graphics.SetClip(CP)
else
Graphics.IntersectClip(CP);
end;
Graphics.SetTransform(CompleteMatrix);
SetFiller(dtFill, atPaint(at_fill), CalcFillOpacity, Graphics);
SetFiller(dtStroke, atPaint(at_stroke), CalcStrokeOpacity, Graphics);
Graphics.SetStroke(GetStrokeData);
DoPaintToGraphics(Graphics);
finally
Graphics.ResetTransform;
Graphics.ResetClip;
end;
end;
procedure TSVGBaseObj.PaintToPath(Path: TSVGCanvasPath);
begin
if FPath <> nil then
Path.AddPath(FPath, False);
end;
procedure TSVGBaseObj.SetFiller(DrawType: TSVGDrawType; SVGPaint: TSVGPaint; Opacity: Single; const Graphics: TSVGCanvas);
var
Filler: TSVGElementObj;
GradientData: TSVGGradientData;
begin
if SVGPaint.SW = frx_URI then
begin
Filler := FRoot.FindByID(string(SVGPaint.URI));
if Assigned(Filler) then
if Filler is TSVGGradient then
begin
TSVGGradient(Filler).GetGradientData(Opacity, Self, GradientData);
Graphics.SetGradient(DrawType, GradientData);
end;
end
else
Graphics.SetSolidColor(DrawType, ToSVGColor(SVGPaint, Opacity));
end;
function TSVGBaseObj.GetStrokeData: TSVGStrokeData;
begin
Result.Width := atLengthInherit(at_stroke_width);
Result.Miterlimit := atNumberInherit(at_stroke_miterlimit);
Result.LineCap := atSpecificWord(at_stroke_linecap);
Result.LineJoin := atSpecificWord(at_stroke_linejoin);
Result.Dash.Offset := atLengthInherit(at_stroke_dashoffset);
Result.Dash.Arr := atLengthListNoneInherit(at_stroke_dasharray);
end;
procedure TSVGBaseObj.ConstructPath;
begin
FreeAndNil(FPath);
end;
{ Tel_svg }
function Tel_svg.CalcAngleMatrix: TSVGTransform;
begin
Result := tmIdentity;
end;
function Tel_svg.CalcCenteredShift(Offset: TSinglePoint): TSinglePoint;
begin
Result := Offset;
end;
function Tel_svg.CalcExternalScaleMatrix: TSVGTransform;
begin
Result := tmIdentity;
end;
function Tel_svg.CalcExternalTranslateMatrix: TSVGTransform;
begin
Result := tmIdentity;
end;
procedure Tel_svg.CalcRootMatrix;
var
TranslateMatrix, ViewBoxScaleMatrix, ViewBoxTranslateMatrix: TSVGTransform;
Scale, Offset, CenteredShift: TSinglePoint;
W, H: Single;
VB: TSingleBounds;
PAR: TSVGPreserveAspectRatio;
begin
TranslateMatrix := tmTranslation(atLength(at_x), atLength(at_y));
W := atLengthAuto(at_width);
H := atLengthAuto(at_height);
VB := GetViewBox;
PAR := atPAR;
Scale := CalcScale(W, H, VB, PAR);
Offset := CalcOffset(Scale, W, H, VB, PAR);
CenteredShift := CalcCenteredShift(Offset);
ViewBoxTranslateMatrix := tmTranslation(-CenteredShift.X, -CenteredShift.Y);
ViewBoxScaleMatrix := tmScaling(Scale.X, Scale.Y);
FRootMatrix := tmIdentity;
FRootMatrix := tmMultiply(CalcExternalTranslateMatrix, FRootMatrix);
FRootMatrix := tmMultiply(TranslateMatrix, FRootMatrix);
FRootMatrix := tmMultiply(ViewBoxTranslateMatrix, FRootMatrix);
FRootMatrix := tmMultiply(ViewBoxScaleMatrix, FRootMatrix);
FRootMatrix := tmMultiply(CalcExternalScaleMatrix, FRootMatrix);
FRootMatrix := tmMultiply(CalcAngleMatrix, FRootMatrix);
end;
function Tel_svg.CalcSelfBounds: TSingleBounds;
begin
Result := GetViewBox;
end;
procedure Tel_svg.CalculateMatrices;
var
i: Integer;
begin
CalcRootMatrix;
for i := 0 to Count - 1 do
Items[i].CalculateMatrices;
end;
constructor Tel_svg.Create;
begin
inherited Create;
ConstructAttributes(el_svg);
end;
function Tel_svg.GetHeight: Single;
begin
Result := atLengthAuto(at_height);
end;
function Tel_svg.GetViewBox: TSingleBounds;
begin
if atIsDefault(at_viewBox) then
Result := ToSingleBounds(0,
0,
atLengthAuto(at_width),
atLengthAuto(at_height))
else
Result := AttrObj[at_viewBox].GetBounds;
end;
function Tel_svg.GetViewBoxHeight: Single;
begin
Result := GetViewBox.Height;
end;
function Tel_svg.GetViewBoxWidth: Single;
begin
Result := GetViewBox.Width;
end;
function Tel_svg.GetWidth: Single;
begin
Result := atLengthAuto(at_width)
end;
function Tel_svg.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_svg.Create(Parent);
end;
function Tel_svg.GetNormalizedViewBoxDiagonal: Single;
begin
with GetViewBox do
Result := Sqrt((Sqr(Width) + Sqr(Height)) / 2.0);
end;
{ Tel_g }
constructor Tel_g.Create;
begin
inherited Create;
ConstructAttributes(el_g);
end;
function Tel_g.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_g.Create(Parent);
end;
{ Tel_switch }
constructor Tel_switch.Create;
begin
inherited Create;
ConstructAttributes(el_switch);
end;
{$IFNDEF Delphi15}
//function LCIDToLocaleName(Locale: LCID; lpName: LPWSTR; cchName: Integer;
// dwFlags: DWORD): Integer; stdcall;external kernel32 name 'LCIDToLocaleName';
function GetWindowsLanguage(LCTYPE: LCTYPE {type of information}): string;
var
Buffer : PChar;
Size : integer;
begin
Size := GetLocaleInfo (LOCALE_USER_DEFAULT, LCType, nil, 0);
GetMem(Buffer, Size);
try
GetLocaleInfo (LOCALE_USER_DEFAULT, LCTYPE, Buffer, Size);
Result := string(Buffer);
finally
FreeMem(Buffer);
end;
end;
{$ENDIF}
procedure Tel_switch.GetLanguage(out Language, PartLanguage: string);
var
{$IFDEF Delphi15}
Buffer: array [0..255] of WideChar;
{$ENDIF}
DefisPos: Integer;
begin
{$IFNDEF Delphi15}
Language := GetWindowsLanguage(LOCALE_SISO639LANGNAME) + '-' + GetWindowsLanguage(LOCALE_SISO3166CTRYNAME);
{$ELSE}
LCIDToLocaleName(GetSystemDefaultLCID, Buffer, 255, 0);
Language := Buffer;
{$ENDIF}
Language := AnsiLowercase(Language);
DefisPos := Pos('-', Language);
if DefisPos > 0 then
PartLanguage := Copy(Language, 1, Pos('-', Language) - 1)
else
PartLanguage := '';
end;
function Tel_switch.IsAnyChildHaveSystemLanguage: boolean;
var
i: Integer;
begin
Result := True;
for i := 0 to Count - 1 do
if Items[i].IsAttrEnabled[at_systemLanguage] and
not Items[i].atIsDefault(at_systemLanguage) then
Exit;
Result := False;
end;
function Tel_switch.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_switch.Create(Parent);
end;
procedure Tel_switch.SwitchChildren;
const
Unknown = -1;
var
Language, PartLanguage, S, st: string;
i, j, PartFit, CatchAll: Integer;
SL: TStrings;
begin
if not IsAnyChildHaveSystemLanguage then
Exit;
GetLanguage(Language, PartLanguage);
for i := 0 to Count - 1 do
Items[i].SwitchedOn := False;
PartFit := Unknown;
CatchAll := Unknown;
for i := 0 to Count - 1 do
begin
S := Items[i].atString(at_systemLanguage);
if S <> '' then
begin
SL := ToStringList(S, ',');
try
for j := 0 to SL.Count - 1 do
begin
st := AnsiLowercase(SL[j]);
if Language = st then
begin
Items[i].SwitchedOn := True;
Exit;
end
else if PartFit = Unknown then
if PartLanguage = st then
PartFit := i
else if SVGSpecificWord[svg_catch_all] = st then
CatchAll := 1;
end;
finally
SL.Free;
end;
end;
end;
if PartFit <> Unknown then
Items[PartFit].SwitchedOn := True
else if CatchAll <> Unknown then
Items[CatchAll].SwitchedOn := True;
end;
{ Tel_use }
procedure Tel_use.Construct;
var
Container: Tel_g;
ReferenceObjClone: TSVGElementObj;
Matrix: TSVGTransform;
begin
ClearItems;
if IsHrefObjExists then
begin
Matrix := tmTranslation(atLength(at_x), atLength(at_y));
Container := Tel_g.Create(Self);
Container.AttrObj[at_transform].SetTransform(Matrix);
ReferenceObjClone := FHrefObj.Clone(Container);
ReferenceObjClone.ClarifyUses;
end;
end;
constructor Tel_use.Create;
begin
inherited Create;
ConstructAttributes(el_use);
end;
function Tel_use.IsHrefObjExists: Boolean;
procedure TryFind(const Href: string);
begin
if (Href <> '') and (Href[1] = '#') then
FHrefObj := FRoot.FindByID(Copy(Href, 2, MaxInt));
end;
begin
if FHrefObj = nil then
TryFind(atString(at_href));
Result := FHrefObj <> nil;
end;
function Tel_use.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_use.Create(Parent);
end;
procedure Tel_use.PaintToGraphics(Graphics: TSVGCanvas);
begin
if Count > 0 then
TSVGBaseObj(Items[0]).PaintToGraphics(Graphics);
end;
procedure Tel_use.PaintToPath(Path: TSVGCanvasPath);
begin
if IsHrefObjExists then
TSVGBaseObj(FHrefObj).PaintToPath(Path);
end;
{ Tel_rect }
procedure Tel_rect.ConstructPath;
var
rx, ry, width, height: single;
begin
inherited ConstructPath;
FPath := CanvasClass.CreatePath;
rx := atLengthAuto(at_rx);
ry := atLengthAuto(at_ry);
width := atLengthAuto(at_width);
height := atLengthAuto(at_height);
// The way the value of the rx attribute is interpreted depend on both the ry attribute and the width of the rectangle:
// If a properly specified value is provided for rx but not for ry (or the opposite), then the browser will consider the missing value equal to the defined one.
// If neither rx nor ry has a properly specified value, then the browser will draw a rectangle with square corners.
// If rx is greater than half of the width of the rectangle, then the browser will consider the value for rx as half of the width of the rectangle.
if atIsDefault(at_rx) then rx := ry
else if atIsDefault(at_ry) then ry := rx;
rx := Min(rx, width / 2);
ry := Min(ry, height / 2);
FPath.AddRectangle(ToSingleBounds(atLength(at_x), atLength(at_y), width, height), rx, ry);
FObjBounds := FPath.Bounds;
end;
constructor Tel_rect.Create;
begin
inherited Create;
ConstructAttributes(el_rect);
end;
function Tel_rect.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_rect.Create(Parent);
end;
{ Tel_line }
procedure Tel_line.ConstructPath;
begin
inherited ConstructPath;
FPath := CanvasClass.CreatePath;
FPath.AddLine(atLength(at_x1), atLength(at_y1), atLength(at_x2), atLength(at_y2));
FObjBounds := FPath.Bounds;
end;
constructor Tel_line.Create;
begin
inherited Create;
ConstructAttributes(el_line);
end;
function Tel_line.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_line.Create(Parent);
end;
{ Tel_polyline }
procedure Tel_polyline.ConstructPath;
var
i: Integer;
begin
inherited ConstructPath;
FPoints := atPoints;
if IsHasPoints then
begin
FPath := CanvasClass.CreatePath;
for i := 1 to High(FPoints) do
FPath.AddLine(FPoints[i - 1].X, FPoints[i - 1].Y, FPoints[i].X, FPoints[i].Y);
FObjBounds := FPath.Bounds;
end;
end;
constructor Tel_polyline.Create;
begin
inherited Create;
ConstructAttributes(el_polyline);
end;
function Tel_polyline.IsHasPoints: Boolean;
begin
Result := FPoints <> nil;
end;
function Tel_polyline.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_polyline.Create(Parent);
end;
{ Tel_polygon }
procedure Tel_polygon.ConstructPath;
begin
inherited ConstructPath;
if IsHasPoints then
FPath.CloseFigure;
end;
constructor Tel_polygon.Create;
begin
inherited Create;
ConstructAttributes(el_polygon);
end;
function Tel_polygon.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_polygon.Create(Parent);
end;
{ Tel_ellipse }
procedure Tel_ellipse.ConstructPath;
var
rx, ry: Single;
begin
inherited ConstructPath;
FPath := CanvasClass.CreatePath;
// rx / ry: With a value lower or equal to zero the ellipse won't be drawn at all.
rx := atLengthAuto(at_rx);
ry := atLengthAuto(at_ry);
if Min(rx, ry) > 0 then
begin
FPath.AddEllipse(atLength(at_cx), atLength(at_cy), rx, ry);
FObjBounds := FPath.Bounds;
end;
end;
constructor Tel_ellipse.Create;
begin
inherited Create;
ConstructAttributes(el_ellipse);
end;
function Tel_ellipse.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_ellipse.Create(Parent);
end;
{ Tel_image }
function Tel_image.CalcSelfBounds: TSingleBounds;
begin
Result := FObjBounds;
end;
procedure Tel_image.ConstructPath;
begin
if (FImage <> nil) or IsFoundImage(atString(at_href)) then
begin
atSetLengthAuto(at_width, FImage.GetWidth);
atSetLengthAuto(at_height, FImage.GetHeight);
end;
FObjBounds := ToSingleBounds(atLength(at_x), atLength(at_y),
atLengthAuto(at_width), atLengthAuto(at_height));
end;
constructor Tel_image.Create;
begin
inherited Create;
ConstructAttributes(el_image);
end;
destructor Tel_image.Destroy;
begin
FImage.Free;
FStream.Free;
inherited Destroy;
end;
function Tel_image.IsFoundImage(S: string): boolean;
function IsValidInlineImage: Boolean;
var
Semicolon: Integer;
begin
Result := False;
if AnsiStartsStr('data:', S) then
begin
S := Copy(S, 6, MaxInt);
Semicolon := Pos(';', S);
if Semicolon = 0 then
Exit;
if Copy(S, Semicolon, 8) = ';base64,' then
begin
S := Copy(S, Semicolon + 8, MaxInt);
Result := True;
end;
end;
end;
procedure CreateImage;
var
SA: TStreamAdapter;
begin
FStream.Position := 0;
SA := TStreamAdapter.Create(FStream, soReference);
FImage := CanvasClass.CreateImage(SA);
end;
var
AnsiSt: AnsiString;
begin
if FImage = nil then
if IsValidInlineImage then
begin
AnsiSt := Base64Decode(AnsiString(S));
FStream := TMemoryStream.Create;
FStream.Write(AnsiSt[1], Length(AnsiSt));
CreateImage;
end
else if FileExists(S) then
begin
FStream := TMemoryStream.Create;
FStream.LoadFromFile(S);
CreateImage;
end;
Result := FImage <> nil;
end;
function Tel_image.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_image.Create(Parent);
end;
procedure Tel_image.PaintToGraphics(Graphics: TSVGCanvas);
var
i: Integer;
CP: TSVGCanvasPath;
begin
if (FImage <> nil) or IsFoundImage(atString(at_href)) then
begin
CalcClipPathList;
try
if Assigned(FCanvasClipList) then
for i := 0 to FCanvasClipList.Count - 1 do
begin
CP := TSVGCanvasPath(FCanvasClipList[i]);
Graphics.SetTransform(CP.Matrix);
if i = 0 then
Graphics.SetClip(CP)
else
Graphics.IntersectClip(CP);
end;
Graphics.SetTransform(CompleteMatrix);
Graphics.DrawImage(FImage, GetBounds, CalcFillOpacity);
finally
Graphics.ResetTransform;
Graphics.ResetClip;
end;
end;
end;
{ TSVGCustomText }
procedure TSVGCustomText.AssignTo(Dest: TPersistent);
begin
inherited AssignTo(Dest);
if Dest is TSVGCustomText then
begin
TSVGCustomText(Dest).FText := FText;
TSVGCustomText(Dest).FTextWidth := FTextWidth;
TSVGCustomText(Dest).FDecorations := FDecorations;
TSVGCustomText(Dest).FTextOrigin := FTextOrigin;
TSVGCustomText(Dest).FFontHeight := FFontHeight;
end;
end;
procedure TSVGCustomText.ClearDecorations;
begin
FreeAndNil(FDecorations[tdUnderline]);
FreeAndNil(FDecorations[tdLineThrow]);
FreeAndNil(FDecorations[tdOwerline]);
end;
procedure TSVGCustomText.ConstructPath;
procedure CreateDecorations(FD: TSVGFontData);
begin
ClearDecorations;
if svg_underline in FD.Decoration then FDecorations[tdUnderline] := CanvasClass.CreatePath;
if svg_line_through in FD.Decoration then FDecorations[tdLineThrow] := CanvasClass.CreatePath;
if svg_overline in FD.Decoration then FDecorations[tdOwerline] := CanvasClass.CreatePath;
end;
procedure Shift(A: TSingleDynArray; Value: Single);
var
i: Integer;
begin
for i := 0 to High(A) do
A[i] := A[i] + Value;
end;
var
FontData: TSVGFontData;
begin
inherited ConstructPath;
DeleteCRLF;
SetSize;
if (FText = '') or IsInTextPath then
Exit;
FPath := CanvasClass.CreatePath;
FontData := GetFontData;
CreateDecorations(FontData);
Shift(FTextOrigin.Y, -FFontHeight);
{$IFDEF Delphi12}
CanvasClass.AddStringToPath(FText, FPath, FDecorations, FTextOrigin, FontData);
{$ELSE}
CanvasClass.AddStringToPath(UTF8Decode(FText), FPath, FDecorations, FTextOrigin, FontData);
{$ENDIF}
Shift(FTextOrigin.Y, FFontHeight);
FObjBounds := FPath.Bounds;
end;
class function TSVGCustomText.CreatePlainText(AParent: TSVGElementObj; ChildNodeText: string; ChildNodeIndex, ChildNodeCount: Integer): TSVGCustomText;
begin
Result := nil;
if AParent is TSVGCustomText then
if ChildNodeCount = 1 then // only child
begin
if (AParent.Parent is TSVGCustomText) and (AParent.SerialNumber > 0) then
ChildNodeText := ' ' + ChildNodeText;
TSVGCustomText(AParent).FText := ChildNodeText;
end
else
begin
case AParent.Element of
el_text: Result := Tel_text.Create(AParent);
el_textPath: Result := Tel_textPath.Create(AParent);
el_tspan: Result := Tel_tspan.Create(AParent);
end;
if (ChildNodeIndex > 0) and (ChildNodeCount > 1) then
ChildNodeText := ' ' + ChildNodeText;
Result.FText := ChildNodeText;
end;
end;
procedure TSVGCustomText.DeleteCRLF;
var
pCRLF: Integer;
begin
repeat
pCRLF := Pos(AnsiString(#$D#$A), FText);
if pCRLF > 0 then
FText := Trim(Copy(FText, 1, pCRLF - 1)) + ' ' + Trim(Copy(FText, pCRLF + 1, MaxInt))
until pCRLF = 0;
end;
destructor TSVGCustomText.Destroy;
begin
ClearDecorations;
inherited Destroy;
end;
procedure TSVGCustomText.DoPaintToGraphics(Graphics: TSVGCanvas);
begin
Graphics.PaintPath(FDecorations[tdUnderline], atSpecificWord(at_fill_rule));
Graphics.PaintPath(FDecorations[tdOwerline], atSpecificWord(at_fill_rule));
inherited DoPaintToGraphics(Graphics);
Graphics.PaintPath(FDecorations[tdLineThrow], atSpecificWord(at_fill_rule));
end;
function TSVGCustomText.GetCompleteWidth: Single;
var
i: Integer;
begin
Result := FTextWidth;
for i := 0 to Count - 1 do
if Items[i] is TSVGCustomText then
Result := Result + TSVGCustomText(Items[i]).GetCompleteWidth;
end;
function TSVGCustomText.GetFontData: TSVGFontData;
begin
Result.Names := atStringInherit(at_font_family);
Result.Size := atLengthInherit(at_font_size);
Result.Style := atSpecificWord(at_font_style);
Result.Weight := Round(atFontWeight);
Result.Decoration := atSpecificWordSet(at_text_decoration);
end;
function TSVGCustomText.IsInTextPath: Boolean;
var
Obj: TSVGElementObj;
begin
Obj := Self;
while not Obj.IsRoot and (Obj.Element <> el_textPath) do
Obj := Obj.Parent;
Result := Obj.Element = el_textPath;
end;
procedure TSVGCustomText.ReadTextOrigin;
function Sure(A: TSingleDynArray): TSingleDynArray;
begin
if Length(A) = 0 then
begin
SetLength(Result, 1);
Result[0] := 0.0;
end
else
Result := A;
end;
begin
FTextOrigin.X := Sure(atLengthList(at_x));
FTextOrigin.Y := Sure(atLengthList(at_y));
FTextOrigin.DX := Sure(atLengthList(at_dx));
FTextOrigin.DY := Sure(atLengthList(at_dy));
end;
procedure TSVGCustomText.SetSize;
var
Graphics: TSVGCanvas;
Index: Integer;
Previous: TSVGCustomText;
DC: HDC;
Size: TSingleSize;
FontData: TSVGFontData;
i: Integer;
begin
FontData := GetFontData;
DC := GetDC(0);
Graphics := CanvasClass.Create(DC);
try
Size := Graphics.MeasureString(FText, FontData, { out -=> } FFontHeight);
finally
Graphics.Free;
ReleaseDC(0, DC);
end;
FTextWidth := Size.Width;
ReadTextOrigin;
if Assigned(Parent) and (Parent is TSVGCustomText) then
begin
Index := SerialNumber;
Previous := nil;
if (Index > 0) and (Parent[Index - 1] is TSVGCustomText) then
begin
Previous := TSVGCustomText(Parent[Index - 1]);
if atIsDefault(at_x) then
begin
FTextOrigin.X := DynArrayCopy(Previous.FTextOrigin.X);
for i := 0 to High(FTextOrigin.X) do
FTextOrigin.X[i] := Previous.FTextOrigin.X[i] + Previous.GetCompleteWidth;
end;
end
else if (Index = 0) and (Parent is TSVGCustomText) then
begin
Previous := TSVGCustomText(Parent);
if atIsDefault(at_x) then
FTextOrigin.X := DynArrayCopy(Previous.FTextOrigin.X);
end;
if Assigned(Previous) then
begin
if atIsDefault(at_y) then
FTextOrigin.Y := DynArrayCopy(Previous.FTextOrigin.Y);
if atIsDefault(at_dx) then
FTextOrigin.DX := DynArrayCopy(Previous.FTextOrigin.DX);
if atIsDefault(at_dy) then
FTextOrigin.DY := DynArrayCopy(Previous.FTextOrigin.DY);
end;
end;
case atSpecificWord(at_text_anchor) of
svg_middle:
FTextOrigin.DX[0] := FTextOrigin.DX[0] - FTextWidth / 2;
svg_end:
FTextOrigin.DX[0] := FTextOrigin.DX[0] - FTextWidth;
end;
end;
{ Tel_text }
constructor Tel_text.Create;
begin
inherited Create;
ConstructAttributes(el_text);
end;
function Tel_text.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_text.Create(Parent);
end;
{ Tel_tspan }
constructor Tel_tspan.Create;
begin
inherited Create;
ConstructAttributes(el_tspan);
end;
function Tel_tspan.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_tspan.Create(Parent);
end;
{ Tel_clipPath }
procedure Tel_clipPath.ConstructClipPath;
procedure AddPath(SVGBasic: TSVGBaseObj);
var
i: Integer;
begin
if SVGBasic.IsOpt(eoPaint) then
SVGBasic.PaintToPath(FInnerClipPath);
for i := 0 to SVGBasic.Count - 1 do
AddPath(TSVGBaseObj(SVGBasic[i]));
end;
begin
FInnerClipPath := CanvasClass.CreatePath;
AddPath(Self);
end;
constructor Tel_clipPath.Create;
begin
inherited Create;
ConstructAttributes(el_clipPath);
end;
destructor Tel_clipPath.Destroy;
begin
FInnerClipPath.Free;
FTransformedPath.Free;
inherited Destroy;
end;
function Tel_clipPath.GetInnerClipPath(BoundingBox: TSingleBounds; UserMatrix: TSVGTransform): TSVGCanvasPath;
var
Matrix, ScaleMatrix, TranslateMatrix: TSVGTransform;
begin
if FInnerClipPath = nil then
ConstructClipPath;
Matrix := UserMatrix;
if atSpecificWord(at_clipPathUnits) = svg_objectBoundingBox then
begin
TranslateMatrix := tmTranslation(BoundingBox.X, BoundingBox.Y);
ScaleMatrix := tmScaling(BoundingBox.Width, BoundingBox.Height);
Matrix := tmMultiply(TranslateMatrix, Matrix);
Matrix := tmMultiply(ScaleMatrix, Matrix);
end;
if (FTransformedPath = nil) or not IsSameTransform(Matrix, FLastMatrix) then
begin
FTransformedPath.Free;
FTransformedPath := FInnerClipPath.Clone;
FLastMatrix := Matrix;
FTransformedPath.Transform(FLastMatrix);
end;
Result := FTransformedPath;
end;
function Tel_clipPath.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_clipPath.Create(Parent);
end;
{ Tel_textPath }
procedure Tel_textPath.ConstructPath;
function Find(const HRef: string): TSVGElementObj;
begin
Result := nil;
if (HRef <> '') and (HRef[1] = '#') then
Result := FRoot.FindByID(Copy(Href, 2, MaxInt));
end;
var
Obj: TSVGElementObj;
GuidePath: Tel_path;
PT: TSVGCanvasPathText;
Position: Single;
Offset: Single;
procedure RenderTextElement(const CustomText: TSVGCustomText);
var
i: Integer;
FontData: TSVGFontData;
begin
CustomText.ClearDecorations;
FreeAndNil(CustomText.FPath);
if CustomText.FText <> '' then
begin
FontData := CustomText.GetFontData;
PT.AdditionalMatrix := CustomText.atMatrix(at_transform);
CustomText.FPath := CanvasClass.CreatePath;
Position := Position +
PT.AddPathText(CustomText.FPath, Trim(CustomText.FText),
Offset + Position, FontData);
end;
for i := 0 to CustomText.Count - 1 do
if CustomText[i] is TSVGCustomText then
RenderTextElement(TSVGCustomText(CustomText[i]));
end;
begin
Obj := Find(atString(at_href));
if (Obj = nil) or (Obj.Element <> el_path) then
Exit;
GuidePath := Tel_path(Obj);
PT := CanvasClass.CreatePathText(TSVGBaseObj(GuidePath).FPath);
try
Position := 0;
Offset := atLength(at_startOffset, CanvasClass.GetPathLength(GuidePath.FPath));
RenderTextElement(Self);
finally
PT.Free;
end;
end;
constructor Tel_textPath.Create;
begin
inherited Create;
ConstructAttributes(el_textPath);
end;
function Tel_textPath.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_textPath.Create(Parent);
end;
{ Tel_circle }
procedure Tel_circle.ConstructPath;
begin
inherited ConstructPath;
FPath := CanvasClass.CreatePath;
FPath.AddCircle(atLength(at_cx), atLength(at_cy), atLength(at_r));
FObjBounds := FPath.Bounds;
end;
constructor Tel_circle.Create;
begin
inherited Create;
ConstructAttributes(el_circle);
end;
function Tel_circle.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_circle.Create(Parent);
end;
{ Tel_a }
constructor Tel_a.Create;
begin
inherited Create;
ConstructAttributes(el_a);
end;
function Tel_a.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := Tel_a.Create(Parent);
end;
{ TSVGRootObj }
procedure TSVGRootObj.AssignStylesApplyingSequenceTo(Obj: TSVGElementObj);
begin
// Empty
end;
procedure TSVGRootObj.AssignTo(Dest: TPersistent);
begin
inherited AssignTo(Dest);
if Dest is TSVGRootObj then
begin
TSVGRootObj(Dest).StyleList.Assign(FStyleList);
TSVGRootObj(Dest).FSource := FSource;
end;
end;
function TSVGRootObj.CalcAngleMatrix: TSVGTransform;
var
w2, h2: Single;
begin
w2 := atLengthAuto(at_width) / 2.0;
h2 := atLengthAuto(at_height) / 2.0;
Result := tmTranslation(w2, h2);
Result := tmMultiply(tmRotation(FExternalAngle), Result);
Result := tmMultiply(tmTranslation(-w2, -h2), Result);
end;
function TSVGRootObj.CalcCenteredShift(Offset: TSinglePoint): TSinglePoint;
begin
if FExternalCentered or IsSizeless then
Result := ToSinglePoint(
Offset.X * FExternalScale.X,
Offset.Y * FExternalScale.Y
)
else
Result := Offset
end;
function TSVGRootObj.CalcExternalScaleMatrix: TSVGTransform;
begin
Result := tmScaling(FExternalScale.X, FExternalScale.Y);
end;
function TSVGRootObj.CalcExternalTranslateMatrix: TSVGTransform;
begin
Result := tmTranslation(FExternalBounds.X, FExternalBounds.Y)
end;
procedure TSVGRootObj.ClarifyOwnShorthandProperties;
var
i: integer;
begin
inherited ClarifyOwnShorthandProperties;
for i := 0 to FStyleList.Count - 1 do
SplitShorthandProperties(FStyleList.GetStyle(i), css_font);
end;
procedure TSVGRootObj.Clear;
begin
inherited Clear;
FSource := '';
if Assigned(FStyleList) then
FStyleList.Clear;
FIsPrepared := False;
end;
constructor TSVGRootObj.Create(SVGCanvasClass: TSVGCanvasClass);
begin
Create;
FSVGCanvasClass := SVGCanvasClass;
end;
constructor TSVGRootObj.Create;
begin
inherited Create;
FStyleList := TfrxCSSList.Create;
// Default External Values
FExternalOpacity := 1.0;
FExternalAngle := 0.0;
FExternalFontSize := 16.0;
FExternalFontName := 'times';
FExternalBounds := ToSingleBounds(0.0, 0.0, 300.0, 150.0);
FExternalScale := ToSinglePoint(1.0, 1.0);
FExternalCentered := False;
SelfRoot;
FSVGCanvasClass := TSVGGDIPCanvas;
FIsPrepared := False;
end;
destructor TSVGRootObj.Destroy;
begin
FreeAndNil(FStyleList);
inherited Destroy;
end;
function TSVGRootObj.GetCanvasClass: TSVGCanvasClass;
begin
Result := FSVGCanvasClass;
end;
function TSVGRootObj.GetHeight: Single;
begin
atSetLengthAuto(at_height, FExternalBounds.Height);
Result := atLengthAuto(at_height);
end;
function TSVGRootObj.GetHostNormalizedDiagonal: Single;
begin
with FExternalBounds do
Result := Sqrt((Sqr(Width) + Sqr(Height)) / 2.0);
end;
function TSVGRootObj.GetHostViewBoxHeight: Single;
begin
Result := FExternalBounds.Height;
end;
function TSVGRootObj.GetHostViewBoxWidth: Single;
begin
Result := FExternalBounds.Width;
end;
function TSVGRootObj.GetOuterHeight: Single;
begin
Prepare;
if IsAttrDefault(at_height) and IsAttrDefault(at_viewBox) then
Result := CalcUnitedChildrenBounds.Height
else
Result := GetHeight;
end;
function TSVGRootObj.GetOuterWidth: Single;
begin
Prepare;
if IsAttrDefault(at_width) and IsAttrDefault(at_viewBox) then
Result := CalcUnitedChildrenBounds.Width
else
Result := GetWidth;
end;
function TSVGRootObj.GetWidth: Single;
begin
atSetLengthAuto(at_width, FExternalBounds.Width);
Result := atLengthAuto(at_width);
end;
function TSVGRootObj.IsRoot: Boolean;
begin
Result := True;
end;
function TSVGRootObj.IsSizeless: Boolean;
begin
Result := (atIsDefault(at_height) or atIsDefault(at_width)) and
not atIsDefault(at_viewBox);
end;
procedure TSVGRootObj.LoadFromFile(const FileName: string);
var
FS: TFileStream;
begin
//Log('---------------------' + FileName);
FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
try
LoadFromStream(FS);
FFileName := FileName;
finally
FS.Free;
end;
end;
procedure TSVGRootObj.LoadFromStream(Stream: TStream);
var
SL: TStringList;
begin
SL := TStringList.Create;
try
SL.LoadFromStream(Stream{$IFDEF Delphi12}, TEncoding.UTF8{$ENDIF});
LoadFromText(SL.Text);
finally
SL.Free;
end;
end;
procedure TSVGRootObj.LoadFromText(const Text: string);
var
XML: TfrxSVGXMLDocument;
WithoutBOM: string;
begin
Clear;
if (Length(Text) >= 3) and
(Text[1] + Text[2] + Text[3] = #239#187#191) then // BOM
WithoutBOM := Copy(Text, 4, MaxInt)
else
WithoutBOM := Text;
XML := TfrxSVGXMLDocument.Create(nil);
try
FSource := WithoutBOM;
XML.LoadFromXML(WithoutBOM);
if (XML <> nil) and (TfrxSVGXMLItem(XML.Root).nodeName = SVGElement[el_svg].Name) then
ReadIn(TfrxSVGXMLItem(XML.Root))
else
FSource := '';
Prepare;
finally
XML.Free;
end;
end;
function TSVGRootObj.New(Parent: TSVGElementObj): TSVGElementObj;
begin
Result := TSVGRootObj.Create(Parent);
end;
procedure TSVGRootObj.Paint(const Graphics: TSVGCanvas);
procedure PaintItem(const Item: TSVGElementObj);
var
i: Integer;
begin
if Item.IsNeedsPainting then
begin
TSVGBaseObj(Item).PaintToGraphics(Graphics);
for i := 0 to Item.Count - 1 do
PaintItem(Item[i]);
end;
end;
begin
PaintItem(Self);
end;
procedure TSVGRootObj.PaintTo(DC: HDC);
var
Graphics: TSVGCanvas;
begin
Prepare;
Graphics := CanvasClass.Create(DC);
try
CalculateMatrices;
Paint(Graphics);
finally
Graphics.Free;
end;
end;
procedure TSVGRootObj.Prepare;
begin
if not FIsPrepared then
begin
ClarifyShorthandProperties;
ClarifyStyleSequences;
ClarifyUses;
ConstructPathes;
SwitchAll;
ClarifyBounds;
FIsPrepared := True;
end;
end;
procedure TSVGRootObj.ReadStyle(const Node: TfrxSVGXMLItem);
begin
FStyleList.AddText(Node.Text);
end;
procedure TSVGRootObj.SaveToFile(const FileName: string);
var
Stream: TFileStream;
begin
Stream := TFileStream.Create(FileName, fmCreate);
try
SaveToStream(Stream);
finally
Stream.Free;
end;
end;
procedure TSVGRootObj.SaveToStream(Stream: TStream);
var
SL: TStringList;
begin
SL := TStringList.Create;
try
SL.Text := FSource;
SL.SaveToStream(Stream{$IFDEF Delphi12}, TEncoding.UTF8{$ENDIF});
finally
SL.Free;
end;
end;
procedure TSVGRootObj.SetExternalAngle(const Value: Single);
begin
if not IsSameSingle(FExternalAngle, Value) then
begin
FExternalAngle := Value;
FIsPrepared := False;
end;
end;
procedure TSVGRootObj.SetExternalBounds(const Value: TSingleBounds);
begin
if not IsSameBounds(FExternalBounds, Value) then
begin
FExternalBounds := Value;
FIsPrepared := False;
end;
end;
procedure TSVGRootObj.SetExternalCentered(const Value: Boolean);
begin
if FExternalCentered <> Value then
begin
FExternalCentered := Value;
FIsPrepared := False;
end;
end;
procedure TSVGRootObj.SetExternalFontName(const Value: string);
begin
if FExternalFontName <> Value then
begin
FExternalFontName := Value;
FIsPrepared := False;
end;
end;
procedure TSVGRootObj.SetExternalFontSize(const Value: Single);
begin
if not IsSameSingle(FExternalFontSize, Value) then
begin
FExternalFontSize := Value;
FIsPrepared := False;
end;
end;
procedure TSVGRootObj.SetExternalOpacity(const Value: Single);
begin
if not IsSameSingle(FExternalOpacity, Value) then
begin
FExternalOpacity := Value;
FIsPrepared := False;
end;
end;
procedure TSVGRootObj.SetExternalScale(const Value: TSinglePoint);
begin
if not IsSameSinglePoint(FExternalScale, Value) then
begin
FExternalScale := Value;
FIsPrepared := False;
end;
end;
end.