├── .gitattributes
├── .gitignore
├── README.md
├── SVGProjects.groupproj
├── examples
├── Embed.svg
├── InitialCoords.svg
├── Linear Fill 1.svg
├── Lines1.svg
├── PathA.svg
├── PolyLine.svg
├── Polygon.svg
├── Radial Fill.svg
├── Rectangle.svg
├── RoundRect.svg
├── Stroke Fill 1.svg
├── Transform1.svg
├── Transform2.svg
├── TransformMatrix.svg
├── TransformMatrix2.svg
├── TransformMatrix3.svg
├── TransformRotate.svg
├── TransformRotate2.svg
├── Use 1.svg
├── Use 2.svg
├── arcs.svg
├── butterfly.svg
├── circles.svg
├── coords-trans-01-b.svg
├── coords-units-03-b.svg
├── cowboy.svg
├── finland.svg
├── linecap.svg
├── lines2.svg
├── lion.svg
├── longhorn.svg
├── otaniemistreet.svg
├── paths.svg
└── tiger.svg
├── fmx
├── BitmapCodecSVG.pas
├── FMXSVGImage.dpk
├── FMXSVGImage.dproj
├── FMXSVGViewer.dpr
├── FMXSVGViewer.dproj
├── FMXSVGViewer.res
├── FmxSVGViewerUnit.fmx
└── FmxSVGViewerUnit.pas
├── gdip
├── GDIPKerning.pas
├── GDIPOBJ2.pas
├── GDIPPathText.pas
└── GDIPUtils.pas
├── license
├── svg
├── SVG.pas
├── SVGColor.pas
├── SVGCommon.pas
├── SVGPackage.dpk
├── SVGPackage.dproj
├── SVGPackage.res
├── SVGPaint.pas
├── SVGParse.pas
├── SVGPath.pas
├── SVGProperties.pas
├── SVGStyle.pas
└── SVGTypes.pas
└── vcl
├── SvgViewer.dpr
├── SvgViewer.dproj
├── SvgViewer.res
├── SvgViewerUnit.dfm
├── SvgViewerUnit.pas
└── svgimage
├── SVGImage.pas
├── SVGImageList.pas
├── SVGImagePackageD.dpk
├── SVGImagePackageD.dproj
├── SVGImagePackageD.res
├── SVGImagePackageR.dpk
├── SVGImagePackageR.dproj
├── SVGImagePackageR.res
├── SVGImageRegister.pas
├── SVGImgLstEditor.dfm
├── SVGImgLstEditor.pas
└── SVGSpeedButton.pas
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Uncomment these types if you want even more clean repository. But be careful.
2 | # It can make harm to an existing project source. Read explanations below.
3 | #
4 | # Resource files are binaries containing manifest, project icon and version info.
5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files.
6 | #*.res
7 | #
8 | # Type library file (binary). In old Delphi versions it should be stored.
9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored.
10 | #*.tlb
11 | #
12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7.
13 | # Uncomment this if you are not using diagrams or use newer Delphi version.
14 | #*.ddp
15 | #
16 | # Visual LiveBindings file. Added in Delphi XE2.
17 | # Uncomment this if you are not using LiveBindings Designer.
18 | #*.vlb
19 | #
20 | # Deployment Manager configuration file for your project. Added in Delphi XE2.
21 | # Uncomment this if it is not mobile development and you do not use remote debug feature.
22 | #*.deployproj
23 | #
24 | # C++ object files produced when C/C++ Output file generation is configured.
25 | # Uncomment this if you are not using external objects (zlib library for example).
26 | #*.obj
27 | #
28 |
29 | # Delphi compiler-generated binaries (safe to delete)
30 | *.exe
31 | *.dll
32 | *.bpl
33 | *.bpi
34 | *.dcp
35 | *.so
36 | *.apk
37 | *.drc
38 | *.map
39 | *.dres
40 | *.rsm
41 | *.tds
42 | *.dcu
43 | *.lib
44 | *.a
45 | *.o
46 | *.ocx
47 |
48 | # Delphi autogenerated files (duplicated info)
49 | *.cfg
50 | *.hpp
51 | *Resource.rc
52 |
53 | # Delphi local files (user-specific info)
54 | *.local
55 | *.identcache
56 | *.projdata
57 | *.tvsconfig
58 | *.dsk
59 |
60 | # Delphi history and backups
61 | __history/
62 | __recovery/
63 | *.~*
64 |
65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi)
66 | *.stat
67 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DelphiSVG
2 |
3 | Delphi SVG library originally by Martin Walter
4 |
5 | https://development.mwcs.de/svgimage.html
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SVGProjects.groupproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {740F3D4F-3334-44F6-BC92-84B3F3A6AD9E}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | Default.Personality.12
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/examples/Embed.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | ]>
7 |
10 |
--------------------------------------------------------------------------------
/examples/InitialCoords.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
29 |
--------------------------------------------------------------------------------
/examples/Linear Fill 1.svg:
--------------------------------------------------------------------------------
1 |
2 |
26 |
--------------------------------------------------------------------------------
/examples/Lines1.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/PathA.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/PolyLine.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/Polygon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/Radial Fill.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/Rectangle.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/examples/RoundRect.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/Stroke Fill 1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
21 |
--------------------------------------------------------------------------------
/examples/Transform1.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/Transform2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/TransformMatrix.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/TransformMatrix2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/TransformMatrix3.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/TransformRotate.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/TransformRotate2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/Use 1.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 | ]>
5 |
6 |
--------------------------------------------------------------------------------
/examples/Use 2.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 | ]>
5 |
6 |
--------------------------------------------------------------------------------
/examples/arcs.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
24 |
--------------------------------------------------------------------------------
/examples/circles.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/examples/coords-trans-01-b.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
240 |
241 |
--------------------------------------------------------------------------------
/examples/coords-units-03-b.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
129 |
130 |
--------------------------------------------------------------------------------
/examples/linecap.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | ]>
7 |
73 |
--------------------------------------------------------------------------------
/examples/lines2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | ]>
7 |
18 |
--------------------------------------------------------------------------------
/examples/paths.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/fmx/BitmapCodecSVG.pas:
--------------------------------------------------------------------------------
1 | // This unit requires at least Delphi 10.3.2 to work
2 | unit BitmapCodecSVG;
3 |
4 | interface
5 |
6 | implementation
7 |
8 | uses
9 | Winapi.Windows, Winapi.GDIPAPI, Winapi.GDIPOBJ,
10 | System.Types, System.SysUtils, System.Classes,
11 | FMX.Types, FMX.Graphics, FMX.Surfaces,
12 | SVGTypes, SVG;
13 |
14 | type
15 | TBitmapCodecSVG = class(TCustomBitmapCodec)
16 | strict private
17 | function CopyToSurface(const ASVG: TSVG; const Bitmap: TBitmapSurface): Boolean;
18 | class procedure InitGDIPlus;
19 | public
20 | class constructor Create;
21 | class function GetImageSize(const AFileName: string): TPointF; override;
22 | class function IsValid(const AStream: TStream): Boolean; override;
23 | function LoadFromFile(const AFileName: string; const Bitmap: TBitmapSurface;
24 | const MaxSizeLimit: Cardinal): Boolean; override;
25 | function SaveToFile(const AFileName: string; const Bitmap: TBitmapSurface;
26 | const SaveParams: PBitmapCodecSaveParams = nil): Boolean; override;
27 | function LoadThumbnailFromFile(const AFileName: string; const AFitWidth, AFitHeight: Single;
28 | const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; override;
29 | function LoadFromStream(const AStream: TStream; const Bitmap: TBitmapSurface;
30 | const MaxSizeLimit: Cardinal): Boolean; override;
31 | function SaveToStream(const AStream: TStream; const Bitmap: TBitmapSurface; const Extension: string;
32 | const SaveParams: PBitmapCodecSaveParams = nil): Boolean; override;
33 | end;
34 |
35 | const
36 | SSVGImageExtension = '.svg'; // do not localize
37 | SVSVG = 'SVG files';
38 |
39 | procedure DebugGDIPlus(level: DebugEventLevel; message: PChar); stdcall;
40 | begin
41 | OutputDebugString(PChar('DebugGDIPlus ' + message));
42 | end;
43 |
44 |
45 | class procedure TBitmapCodecSVG.InitGDIPlus;
46 | var
47 | Status: TStatus;
48 | begin
49 | // Initialize StartupInput structure
50 | StartupInput.DebugEventCallback := DebugGDIPlus;
51 | StartupInput.SuppressBackgroundThread := False;
52 | StartupInput.SuppressExternalCodecs := False;
53 | StartupInput.GdiplusVersion := 1;
54 |
55 | Status := GdiplusStartup(gdiplusToken, @StartupInput, nil);
56 | OutputDebugString(PChar('InitGDIPlus.Status=' + IntToStr(Ord(Status))));
57 | end;
58 |
59 | class constructor TBitmapCodecSVG.Create;
60 | begin
61 | end;
62 |
63 | class function TBitmapCodecSVG.GetImageSize(const AFileName: string): TPointF;
64 | var
65 | SVG: TSVG;
66 | begin
67 | SVG := TSVG.Create;
68 | try
69 | try
70 | SVG.LoadFromFile(AFileName);
71 | Result := TPointF.Create(SVG.Width, SVG.Height);
72 | finally
73 | SVG.Free;
74 | end;
75 | except
76 | Result := TPointF.Create(0, 0);
77 | end;
78 | end;
79 |
80 | class function TBitmapCodecSVG.IsValid(const AStream: TStream): Boolean;
81 | var
82 | SVG: TSVG;
83 | begin
84 | OutputDebugString('IsValid');
85 | SVG := TSVG.Create;
86 | try
87 | try
88 | SVG.LoadFromStream(AStream);
89 | Result := (SVG.Width > 0) and (SVG.Height > 0);
90 | OutputDebugString(PChar(BoolToStr(Result, True)));
91 | finally
92 | SVG.Free;
93 | end;
94 | except
95 | Result := False;
96 | end;
97 | end;
98 |
99 | function TBitmapCodecSVG.LoadFromFile(const AFileName: string; const Bitmap: TBitmapSurface;
100 | const MaxSizeLimit: Cardinal): Boolean;
101 | var
102 | Stream: TFileStream;
103 | begin
104 | OutputDebugString('LoadFromFile');
105 | Stream := TFileStream.Create(AFileName, fmOpenRead);
106 | try
107 | Result := LoadFromStream(Stream, Bitmap, MaxSizeLimit);
108 | finally
109 | Stream.Free;
110 | end;
111 | end;
112 |
113 | function TBitmapCodecSVG.SaveToFile(const AFileName: string; const Bitmap: TBitmapSurface;
114 | const SaveParams: PBitmapCodecSaveParams = nil): Boolean;
115 | begin
116 | Result := False;
117 | end;
118 |
119 | function TBitmapCodecSVG.LoadThumbnailFromFile(const AFileName: string;
120 | const AFitWidth, AFitHeight: Single; const UseEmbedded: Boolean;
121 | const Bitmap: TBitmapSurface): Boolean;
122 | begin
123 | OutputDebugString('LoadThumbnailFromFile');
124 | Result := False;
125 | end;
126 |
127 | function TBitmapCodecSVG.CopyToSurface(const ASVG: TSVG; const Bitmap: TBitmapSurface): Boolean;
128 | var
129 | GPGraphics: TGPGraphics;
130 | GPBitmap: TGPBitmap;
131 | GPRectF: TGPRectF;
132 | RectArray: TRectarray;
133 | GPRect: TGPRect;
134 | GPBitmapData: Winapi.GDIPAPI.TBitmapData;
135 | Source: PByte;
136 | Dest: PByte;
137 | Y: Integer;
138 | Status: TStatus;
139 | IntWidth: Integer;
140 | IntHeight: Integer;
141 | begin
142 | Result := False;
143 | IntWidth := Trunc(ASVG.Width);
144 | IntHeight := Trunc(ASVG.Height);
145 | OutputDebugString(PChar('CopyToSurface ' + IntToStr(IntWidth) + ' ' + IntToStr(IntHeight)));
146 | GPBitmap := TGPBitmap.Create(IntWidth, IntHeight);
147 | Status := GPBitmap.GetLastStatus;
148 | if Status <> TStatus.Ok then
149 | begin
150 | OutputDebugString(PChar('GPBitmap ' + IntToStr(Ord(Status))));
151 | Exit;
152 | end;
153 |
154 | GPGraphics := TGPGraphics.Create(GPBitmap);
155 | try
156 | Status := GPGraphics.SetSmoothingMode(SmoothingModeAntiAlias);
157 | OutputDebugString(PChar('SetSmoothingMode ' + IntToStr(Ord(Status))));
158 |
159 | GPRectF.X := 0;
160 | GPRectF.Y := 0;
161 | GPRectF.Width := ASVG.Width;
162 | GPRectF.Height := ASVG.Height;
163 |
164 | RectArray := TRectArray.Create(TRect.Create(0, 0, Trunc(ASVG.Width), Trunc(ASVG.Height)));
165 | ASVG.PaintTo(GPGraphics, GPRectF, @RectArray, 1);
166 |
167 | GPRect.X := 0;
168 | GPRect.Y := 0;
169 | GPRect.Width := GPBitmap.GetWidth;
170 | GPRect.Height := GPBitmap.GetHeight;
171 |
172 | OutputDebugString(PChar('CopyToSurface.GPRectWidth '+ IntToStr(GPRect.Width)));
173 | Status := GPBitmap.LockBits(GPRect, ImageLockModeRead, PixelFormat32bppPARGB, GPBitmapData);
174 | if Status = TStatus.Ok then
175 | begin
176 | Bitmap.SetSize(Trunc(ASVG.Width), Trunc(ASVG.Height), TPixelFormat.BGRA);
177 | Source := GPBitmapData.Scan0;
178 | Dest := Bitmap.Bits;
179 | for Y := 0 to GPBitmapData.Height - 1 do
180 | begin
181 | Move(Source^, Dest^, GPBitmapData.Stride);
182 | Source := Source + GPBitmapData.Stride;
183 | Dest := Dest + Bitmap.Pitch;
184 | end;
185 |
186 | GPBitmap.UnlockBits(GPBitmapData);
187 | Result := True;
188 | end
189 | else
190 | begin
191 | OutputDebugString(PChar('CopyToSurface.Lockbits error ' + IntToStr(Ord(Status))));
192 | end;
193 | finally
194 | GPGraphics.Free;
195 | GPBitmap.Free;
196 | end;
197 | end;
198 |
199 | function TBitmapCodecSVG.LoadFromStream(const AStream: TStream; const Bitmap: TBitmapSurface;
200 | const MaxSizeLimit: Cardinal): Boolean;
201 | var
202 | SVG: TSVG;
203 | begin
204 | InitGDIPlus;
205 | OutputDebugString('LoadFromStream');
206 | try
207 | SVG := TSVG.Create;
208 | try
209 | SVG.LoadFromStream(AStream);
210 | Result := CopyToSurface(SVG, Bitmap);
211 | finally
212 | SVG.Free;
213 | end;
214 | except
215 | on E: Exception do
216 | begin
217 | OutputDebugString(PChar('LoadFromStream.E ' + E.Message));
218 | Result := False;
219 | end;
220 | end;
221 | end;
222 |
223 | function TBitmapCodecSVG.SaveToStream(const AStream: TStream; const Bitmap: TBitmapSurface;
224 | const Extension: string; const SaveParams: PBitmapCodecSaveParams = nil): Boolean;
225 | begin
226 | Result := False;
227 | end;
228 |
229 | initialization
230 | TBitmapCodecManager.RegisterBitmapCodecClass(SSVGImageExtension, SVSVG, True,
231 | TBitmapCodecSVG);
232 | finalization
233 | TBitmapCodecManager.UnregisterBitmapCodecClass(SSVGImageExtension);
234 | end.
235 |
--------------------------------------------------------------------------------
/fmx/FMXSVGImage.dpk:
--------------------------------------------------------------------------------
1 | package FMXSVGImage;
2 |
3 | {$R *.res}
4 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
5 | {$ALIGN 8}
6 | {$ASSERTIONS ON}
7 | {$BOOLEVAL OFF}
8 | {$DEBUGINFO OFF}
9 | {$EXTENDEDSYNTAX ON}
10 | {$IMPORTEDDATA ON}
11 | {$IOCHECKS ON}
12 | {$LOCALSYMBOLS ON}
13 | {$LONGSTRINGS ON}
14 | {$OPENSTRINGS ON}
15 | {$OPTIMIZATION OFF}
16 | {$OVERFLOWCHECKS OFF}
17 | {$RANGECHECKS OFF}
18 | {$REFERENCEINFO ON}
19 | {$SAFEDIVIDE OFF}
20 | {$STACKFRAMES ON}
21 | {$TYPEDADDRESS OFF}
22 | {$VARSTRINGCHECKS ON}
23 | {$WRITEABLECONST OFF}
24 | {$MINENUMSIZE 1}
25 | {$IMAGEBASE $400000}
26 | {$DEFINE DEBUG}
27 | {$ENDIF IMPLICITBUILDING}
28 | {$IMPLICITBUILD ON}
29 |
30 | requires
31 | rtl,
32 | xmlrtl,
33 | SVGPackage,
34 | fmx;
35 |
36 | contains
37 | BitmapCodecSVG in 'BitmapCodecSVG.pas';
38 |
39 | end.
40 |
--------------------------------------------------------------------------------
/fmx/FMXSVGViewer.dpr:
--------------------------------------------------------------------------------
1 | program FMXSVGViewer;
2 |
3 | uses
4 | System.StartUpCopy,
5 | FMX.Forms,
6 | FmxSVGViewerUnit in 'FmxSVGViewerUnit.pas' {Form2},
7 | BitmapCodecSVG in 'BitmapCodecSVG.pas';
8 |
9 | {$R *.res}
10 |
11 | begin
12 | Application.Initialize;
13 | Application.CreateForm(TForm2, Form2);
14 | Application.Run;
15 | end.
16 |
--------------------------------------------------------------------------------
/fmx/FMXSVGViewer.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ekot1/DelphiSVG/0c2fc478d221a914c8ca1a15f0e65e282396000a/fmx/FMXSVGViewer.res
--------------------------------------------------------------------------------
/fmx/FmxSVGViewerUnit.fmx:
--------------------------------------------------------------------------------
1 | object Form2: TForm2
2 | Left = 0
3 | Top = 0
4 | Caption = 'FmxSVGViewer'
5 | ClientHeight = 480
6 | ClientWidth = 849
7 | FormFactor.Width = 320
8 | FormFactor.Height = 480
9 | FormFactor.Devices = [Desktop]
10 | OnCreate = FormCreate
11 | OnDestroy = FormDestroy
12 | DesignerMasterStyle = 0
13 | object PaintBox1: TPaintBox
14 | Align = Client
15 | Size.Width = 729.000000000000000000
16 | Size.Height = 480.000000000000000000
17 | Size.PlatformDefault = False
18 | OnPaint = PaintBox1Paint
19 | end
20 | object Panel1: TPanel
21 | Align = Right
22 | Position.X = 729.000000000000000000
23 | Size.Width = 120.000000000000000000
24 | Size.Height = 480.000000000000000000
25 | Size.PlatformDefault = False
26 | TabOrder = 1
27 | object Panel2: TPanel
28 | Align = Top
29 | Size.Width = 120.000000000000000000
30 | Size.Height = 33.000000000000000000
31 | Size.PlatformDefault = False
32 | TabOrder = 0
33 | object Button1: TButton
34 | Position.X = 24.000000000000000000
35 | Position.Y = 3.000000000000000000
36 | TabOrder = 0
37 | Text = 'Open...'
38 | OnClick = Button1Click
39 | end
40 | end
41 | object ListBox1: TListBox
42 | Align = Client
43 | Size.Width = 120.000000000000000000
44 | Size.Height = 447.000000000000000000
45 | Size.PlatformDefault = False
46 | TabOrder = 1
47 | DisableFocusEffect = True
48 | DefaultItemStyles.ItemStyle = ''
49 | DefaultItemStyles.GroupHeaderStyle = ''
50 | DefaultItemStyles.GroupFooterStyle = ''
51 | OnChange = ListBox1Change
52 | Viewport.Width = 116.000000000000000000
53 | Viewport.Height = 443.000000000000000000
54 | end
55 | end
56 | object OpenDialog1: TOpenDialog
57 | DefaultExt = 'svg'
58 | Filter = 'SVG|*.svg'
59 | Left = 632
60 | Top = 64
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/fmx/FmxSVGViewerUnit.pas:
--------------------------------------------------------------------------------
1 | unit FmxSVGViewerUnit;
2 |
3 | interface
4 |
5 | uses
6 | System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
7 | FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation,
8 | FMX.StdCtrls, FMX.Objects, FMX.Layouts, FMX.ListBox,
9 | SVG;
10 |
11 | type
12 | TForm2 = class(TForm)
13 | PaintBox1: TPaintBox;
14 | Panel1: TPanel;
15 | Panel2: TPanel;
16 | ListBox1: TListBox;
17 | Button1: TButton;
18 | OpenDialog1: TOpenDialog;
19 | procedure PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
20 | procedure FormCreate(Sender: TObject);
21 | procedure FormDestroy(Sender: TObject);
22 | procedure ListBox1Change(Sender: TObject);
23 | procedure Button1Click(Sender: TObject);
24 | strict private
25 | { Private declarations }
26 | FSVG: TSVG;
27 | public
28 | { Public declarations }
29 | end;
30 |
31 | var
32 | Form2: TForm2;
33 |
34 | implementation
35 |
36 | uses System.IOUtils, Winapi.GDIPOBJ, Winapi.GDIPUTIL, Winapi.GDIPAPI, SVGTypes;
37 |
38 | {$R *.fmx}
39 | const
40 | CPath = '..\..\..\examples\';
41 |
42 | procedure TForm2.Button1Click(Sender: TObject);
43 | begin
44 | if OpenDialog1.Execute then
45 | begin
46 | FSVG.LoadFromFile(OpenDialog1.FileName);
47 | end;
48 | end;
49 |
50 | procedure TForm2.FormCreate(Sender: TObject);
51 | var
52 | Files: TStringDynArray;
53 | FileName: string;
54 | F: TArray;
55 | begin
56 | FSVG := TSVG.Create;
57 |
58 | Files := TDirectory.GetFiles(CPath, '*.svg');
59 | for FileName in Files do
60 | begin
61 | F := F + [ExtractFileName(FileName)];
62 | end;
63 | ListBox1.Items.AddStrings(F);
64 | end;
65 |
66 | procedure TForm2.FormDestroy(Sender: TObject);
67 | begin
68 | FSVG.Free;
69 | end;
70 |
71 | procedure TForm2.ListBox1Change(Sender: TObject);
72 | begin
73 | FSVG.LoadFromFile(TPath.Combine(CPath, ListBox1.Selected.Text));
74 | PaintBox1.Repaint;
75 | end;
76 |
77 | procedure PaintToCanvas(const ASVG: TSVG; Canvas: TCanvas);
78 | var
79 | GPGraphics: TGPGraphics;
80 | GPBitmap: TGPBitmap;
81 | GPRectF: TGPRectF;
82 | RectArray: TRectarray;
83 | GPRect: TGPRect;
84 | GPBitmapData: Winapi.GDIPAPI.TBitmapData;
85 | BitmapData: FMX.Graphics.TBitmapData;
86 | Bitmap: TBitmap;
87 | Source: PByte;
88 | Dest: PByte;
89 | Y: Integer;
90 | begin
91 | GPBitmap := TGPBitmap.Create(Canvas.Width, Canvas.Height);
92 | GPGraphics := TGPGraphics.Create(GPBitmap);
93 | try
94 | GPGraphics.SetSmoothingMode(SmoothingModeAntiAlias);
95 | GPRectF.X := 0;
96 | GPRectF.Y := 0;
97 | GPRectF.Width := ASVG.Width;
98 | GPRectF.Height := ASVG.Height;
99 |
100 | RectArray := TRectArray.Create(TRect.Create(0, 0, Canvas.Width, Canvas.Height));
101 | ASVG.PaintTo(GPGraphics, GPRectF, @RectArray, 1);
102 |
103 | GPRect.X := 0;
104 | GPRect.Y := 0;
105 | GPRect.Width := GPBitmap.GetWidth;
106 | GPRect.Height := GPBitmap.GetHeight;
107 |
108 | GPBitmap.LockBits(GPRect, ImageLockModeRead, PixelFormat32bppPARGB, GPBitmapData);
109 |
110 | Bitmap := TBitmap.Create(GPRect.Width, GPRect.Height);
111 | try
112 | Bitmap.Map(TMapAccess.Write, BitmapData);
113 |
114 | Source := GPBitmapData.Scan0;
115 | Dest := BitmapData.Data;
116 | for Y := 0 to GPBitmapData.Height - 1 do
117 | begin
118 | Move(Source^, Dest^, GPBitmapData.Stride);
119 | Source := Source + GPBitmapData.Stride;
120 | Dest := Dest + BitmapData.Pitch;
121 | end;
122 |
123 | Bitmap.Unmap(BitmapData);
124 | Canvas.DrawBitmap(Bitmap, TRectF.Create(0, 0, Canvas.Width, Canvas.Height),
125 | TRectF.Create(0, 0, Canvas.Width, Canvas.Height), 100);
126 | finally
127 | Bitmap.Free;
128 | end;
129 |
130 | GPBitmap.UnlockBits(GPBitmapData);
131 | finally
132 | GPGraphics.Free;
133 | GPBitmap.Free;
134 | end;
135 | end;
136 |
137 | procedure TForm2.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
138 | begin
139 | PaintToCanvas(FSVG, Canvas);
140 | end;
141 |
142 | end.
143 |
--------------------------------------------------------------------------------
/gdip/GDIPOBJ2.pas:
--------------------------------------------------------------------------------
1 | { *****************************************************************}
2 | { Added Support for RoundRect (GraphicsPath + TGPGraphics) }
3 | { }
4 | { date : 05-11-2006 }
5 | { }
6 | { email : martin.walter@winningcubed.de }
7 | { }
8 | { *****************************************************************}
9 | unit GDIPOBJ2;
10 |
11 | interface
12 |
13 | uses
14 | Winapi.GDIPAPI, Winapi.GDIPOBJ;
15 |
16 | type
17 | TGPGraphicsPath2 = class(TGPGraphicsPath)
18 | public
19 | function AddRoundRect(Rect: TGPRectF; RX, RY: Single): TStatus; overload;
20 | function AddRoundRect(X, Y, Width, Height, RX, RY: Single): TStatus; overload;
21 | function Clone: TGPGraphicsPath2;
22 | end;
23 |
24 | implementation
25 |
26 | { TGPGraphicsPath2 }
27 |
28 | function TGPGraphicsPath2.AddRoundRect(Rect: TGPRectF; RX, RY: Single): TStatus;
29 | begin
30 | Result := AddRoundRect(Rect.X, Rect.Y, Rect.Width, Rect.Height, RX, RY);
31 | end;
32 |
33 | function TGPGraphicsPath2.AddRoundRect(X, Y, Width, Height, RX, RY: Single) : TStatus;
34 | begin
35 | Result := AddLine(X + RX, Y, X + Width - RX, Y);
36 | if Result <> OK then
37 | Exit;
38 | Result := AddArc(X + Width - 2 * RX, Y, 2 * RX, 2 * RY, 270, 90);
39 | if Result <> OK then
40 | Exit;
41 |
42 | Result := AddLine(X + Width, Y + RY,X + Width, Y + Height - RY);
43 | if Result <> OK then
44 | Exit;
45 | Result := AddArc(X + Width - 2 * RX, Y + Height - 2 * RY, 2 * RX, 2 * RY, 0, 90);
46 | if Result <> OK then
47 | Exit;
48 |
49 | Result := AddLine(X + Width - RX, Y + Height, X + RX, Y + Height);
50 | if Result <> OK then
51 | Exit;
52 | Result := AddArc(X, Y + Height - 2 * RY, 2 * RX, 2 * RY, 90, 90);
53 | if Result <> OK then
54 | Exit;
55 |
56 | Result := AddLine(X, Y + Height - RY, X, Y + RY);
57 | if Result <> OK then
58 | Exit;
59 | Result := AddArc(X, Y, 2 * RX, 2 * RY, 180, 90);
60 | if Result <> OK then
61 | Exit;
62 | Result := CloseFigure;
63 | end;
64 |
65 | function TGPGraphicsPath2.Clone: TGPGraphicsPath2;
66 | var
67 | ClonePath: GpPath;
68 | begin
69 | Clonepath := nil;
70 | SetStatus(GdipClonePath(nativePath, Clonepath));
71 | result := TGPGraphicsPath2.Create(ClonePath);
72 | end;
73 |
74 | end.
75 |
--------------------------------------------------------------------------------
/gdip/GDIPPathText.pas:
--------------------------------------------------------------------------------
1 | {******************************************************************}
2 | { GDIPPathText }
3 | { }
4 | { home page : http://www.mwcs.de }
5 | { email : martin.walter@mwcs.de }
6 | { }
7 | { date : 30-11-2007 }
8 | { }
9 | { version : 1.0 }
10 | { }
11 | { Use of this file is permitted for commercial and non-commercial }
12 | { use, as long as the author is credited. }
13 | { This file (c) 2007 Martin Walter }
14 | { }
15 | { This Software is distributed on an "AS IS" basis, WITHOUT }
16 | { WARRANTY OF ANY KIND, either express or implied. }
17 | { }
18 | { *****************************************************************}
19 |
20 | unit GDIPPathText;
21 |
22 | interface
23 |
24 | uses
25 | Winapi.GDIPAPI, Winapi.GDIPOBJ, GDIPKerning;
26 |
27 | type
28 | TPathPosition = Single;
29 |
30 | TGPPathText = class(TObject)
31 | strict private
32 | FRotation: Single;
33 | FGuidePath: TGPGraphicsPath;
34 |
35 | FFamily: TGPFontFamily;
36 | FStyle: Integer;
37 | FSize: Single;
38 | FFormat: TGPStringFormat;
39 | FDistanceFactor: Single;
40 | FKerningFactor: Single;
41 | FAdditionalMatrix: TGPMatrix;
42 |
43 | function AddGlyphToPath(const Path: TGPGraphicsPath; const Char: WideChar;
44 | const Family: TGPFontFamily; const Style: Integer; const Size: Single;
45 | const Origin: TGPPointF; const Format: TGPStringFormat): TStatus;
46 |
47 | function AddCharacter(const Current, Next: WideChar;
48 | const Path: TGPGraphicsPath; const Position: TPathPosition): TPathPosition;
49 |
50 | function GetPathPoint(const Position: TPathPosition): TGPPointF;
51 | function GetPathPointLength(const Position: TPathPosition): Single;
52 | function GetPathPosition(Indent: Single): TPathPosition;
53 | function FindRightPosition(CenterPos: TPathPosition;
54 | const Radius: Single): TPathPosition;
55 | protected
56 | public
57 | constructor Create(const GuidePath: TGPGraphicsPath;
58 | const Flatness: Single = 10 * FlatnessDefault);
59 |
60 | destructor Destroy; override;
61 |
62 | function AddPathText(const Path: TGPGraphicsPath;
63 | const Text: WideString; const Indent: Single;
64 | const Family: TGPFontFamily; Style: Integer;
65 | const Size: Single; const Format: TGPStringFormat;
66 | const DistanceFactor: Single = 1; const KerningFactor: Single = 1): Single;
67 |
68 | class function GetPathLength(const Path: TGPGraphicsPath): Single;
69 |
70 | property Rotation: Single read FRotation write FRotation;
71 | property AdditionalMatrix: TGPMatrix read FAdditionalMatrix write FAdditionalMatrix;
72 | end;
73 |
74 | implementation
75 |
76 | uses
77 | System.Math, System.SysUtils;
78 |
79 | function GetPoint(P: PGPPointF; Index: Integer): TGPPointF;
80 | begin
81 | Inc(P, Index);
82 | Result := P^;
83 | end;
84 |
85 | function AddPoint(A, B: TGPPointF): TGPPointF;
86 | begin
87 | Result.X := A.X + B.X;
88 | Result.Y := A.Y + B.Y;
89 | end;
90 |
91 | function SubPoint(A, B: TGPPointF): TGPPointF;
92 | begin
93 | Result.X := A.X - B.X;
94 | Result.Y := A.Y - B.Y;
95 | end;
96 |
97 | function GetIntersectionFromCircle(PtA, PtB, Center: TGPPointF;
98 | const R2: Single): Single;
99 | var
100 | Diff: TGPPointF;
101 | rA2, rB2: Single;
102 | A, B, C, D, T, T1, T2, SqrtD: Single;
103 | begin
104 | PtA := SubPoint(PtA, Center);
105 | PtB := SubPoint(PtB, Center);
106 |
107 | rA2 := Sqr(PtA.X) + Sqr(PtA.Y);
108 | rB2 := Sqr(PtB.X) + Sqr(PtB.Y);
109 |
110 | if (rA2 > R2) and (rB2 > R2) then
111 | begin
112 | Result := -1;
113 | Exit;
114 | end;
115 |
116 | if (rA2 < R2) and (rB2 < R2) then
117 | begin
118 | Result := -1;
119 | Exit;
120 | end;
121 |
122 | Diff := SubPoint(PtB, PtA);
123 |
124 | A := Sqr(Diff.X) + Sqr(Diff.Y);
125 | B := 2 * (PtA.X * Diff.X + PtA.Y * Diff.Y);
126 | C := rA2 - R2;
127 | D := Sqr(B) - 4 * A * C;
128 |
129 | T := -1;
130 |
131 | A := 2 * A;
132 | if (D = 0) then
133 | T := -B / A
134 | else
135 | if (D > 0) then
136 | begin
137 | SqrtD := Sqrt(D);
138 | T1 := (-B + SqrtD) / A;
139 | T2 := (-B - SqrtD) / A;
140 |
141 | if (T1 >= 0) and (T1 <= 1) then
142 | begin
143 | if (T2 > 0) and (T2 < T1) then
144 | T := T2
145 | else
146 | T := T1;
147 | end
148 | else
149 | if (T2 >= 0) and (T2 <= 1) then
150 | T := T2;
151 | end;
152 | Result := T;
153 | end;
154 |
155 |
156 | { TPathText }
157 |
158 | function TGPPathText.AddCharacter(const Current, Next: WideChar;
159 | const Path: TGPGraphicsPath; const Position: TPathPosition): TPathPosition;
160 | var
161 | CharWidth: Single;
162 | GlyphPath: TGPGraphicsPath;
163 | Left, Right, Diff: TGPPointF;
164 | SinAngle, CosAngle: Single;
165 | Matrix: TGPMatrix;
166 | PosRight: TPathPosition;
167 | begin
168 | GlyphPath := TGPGraphicsPath.Create;
169 | try
170 | CharWidth := KerningText.GetCellWidth(Word(Current), Word(Next),
171 | FDistanceFactor, FKerningFactor);
172 |
173 | if (CharWidth = 0) then
174 | begin
175 | Result := -1;
176 | Exit;
177 | end;
178 |
179 | PosRight := FindRightPosition(Position, CharWidth);
180 | if (PosRight < 0) then
181 | begin
182 | Result := PosRight;
183 | Exit;
184 | end;
185 |
186 | Left := GetPathPoint(Position);
187 | Right := GetPathPoint(PosRight);
188 |
189 | Diff := SubPoint(Right, Left);
190 |
191 | CosAngle := Diff.X / CharWidth;
192 | SinAngle := Diff.Y / CharWidth;
193 |
194 | AddGlyphToPath(GlyphPath, Current, FFamily, FStyle, FSize,
195 | MakePoint(0, -FSize), FFormat);
196 |
197 | if Assigned(FAdditionalMatrix) then
198 | GlyphPath.Transform(FAdditionalMatrix);
199 |
200 | Matrix := TGPMatrix.Create(CosAngle, SinAngle,
201 | - Rotation * SinAngle, 1 + Rotation * (CosAngle - 1),
202 | Left.X, Left.Y);
203 | try
204 | GlyphPath.Transform(Matrix);
205 | finally
206 | Matrix.Free;
207 | end;
208 |
209 | Path.AddPath(GlyphPath, False);
210 | Result := PosRight;
211 | finally
212 | GlyphPath.Free;
213 | end;
214 | end;
215 |
216 | function TGPPathText.AddGlyphToPath(const Path: TGPGraphicsPath;
217 | const Char: WideChar; const Family: TGPFontFamily; const Style: Integer;
218 | const Size: Single; const Origin: TGPPointF;
219 | const Format: TGPStringFormat): TStatus;
220 | begin
221 | Result := Path.AddString(Char, -1, Family, Style, Size, Origin, Format);
222 | end;
223 |
224 | function TGPPathText.AddPathText(const Path: TGPGraphicsPath;
225 | const Text: WideString; const Indent: Single;
226 | const Family: TGPFontFamily; Style: Integer;
227 | const Size: Single; const Format: TGPStringFormat;
228 | const DistanceFactor: Single = 1; const KerningFactor: Single = 1): Single;
229 | var
230 | IndentPosition, Position: TPathPosition;
231 | Current, Next: PWideChar;
232 | begin
233 | Result := 0;
234 | Path.SetFillMode(FillModeWinding);
235 |
236 | IndentPosition := GetPathPosition(Indent);
237 | Position := IndentPosition;
238 |
239 | Current := PWideChar(Text);
240 |
241 | FFamily := Family;
242 | FStyle := Style;
243 | FSize := Size;
244 | FFormat := Format;
245 | FDistanceFactor := DistanceFactor;
246 | FKerningFactor := KerningFactor;
247 |
248 | KerningText.Prepare(FFamily, FStyle, FSize, FFormat);
249 | try
250 | while (Current^ <> #0) and (Position >= 0) do
251 | begin
252 | Next := Current + 1;
253 | Position := AddCharacter(Current^, Next^, Path, Position);
254 | if Position >= 0 then
255 | Result := Position;
256 | Inc(Current);
257 | end;
258 | finally
259 | KerningText.Unprepare;
260 | end;
261 | if Result > 0 then
262 | Result := GetPathPointLength(Result - IndentPosition);
263 | end;
264 |
265 | constructor TGPPathText.Create(const GuidePath: TGPGraphicsPath;
266 | const Flatness: Single);
267 | begin
268 | if not Assigned(GuidePath) then
269 | Exception.Create('Path is invalid');
270 |
271 | inherited Create;
272 |
273 | FGuidePath := GuidePath.Clone;
274 | FRotation := 1;
275 | FGuidePath.Flatten(nil, Flatness);
276 | end;
277 |
278 | destructor TGPPathText.Destroy;
279 | begin
280 | FGuidePath.Free;
281 | inherited;
282 | end;
283 |
284 | function TGPPathText.FindRightPosition(CenterPos: TPathPosition;
285 | const Radius: Single): TPathPosition;
286 | var
287 | StartSegment: Integer;
288 | PD: TPathData;
289 | DistLeft, DistRight: Single;
290 | Start: TGPPointF;
291 | Diff: TGPPointF;
292 | P1, P2: TGPPointF;
293 | C, PointCount: Integer;
294 | Intersection: Single;
295 | begin
296 | if (CenterPos < 0) then
297 | begin
298 | Result := -1;
299 | Exit;
300 | end;
301 |
302 | StartSegment := Floor(CenterPos);
303 |
304 | PointCount := FGuidePath.GetPointCount;
305 | if (StartSegment >= PointCount - 1) then
306 | begin
307 | Result := -1;
308 | Exit;
309 | end;
310 |
311 | PD := TPathData.Create;
312 | try
313 | if (FGuidePath.GetPathData(PD) = Ok) then
314 | begin
315 | Start := GetPathPoint(CenterPos);
316 |
317 | P1 := GetPoint(PD.Points, StartSegment + 1);
318 |
319 | Diff := SubPoint(Start, P1);
320 | DistRight := Sqrt(Sqr(Diff.X) + Sqr(Diff.Y));
321 |
322 | if (Radius < DistRight) then
323 | begin
324 | Diff := SubPoint(Start, GetPoint(PD.Points, StartSegment));
325 | DistLeft := Sqrt(Sqr(Diff.X) + Sqr(Diff.Y));
326 |
327 | Result := StartSegment + 1 - (DistRight - Radius) / (DistRight + DistLeft);
328 | Exit;
329 | end;
330 |
331 | for C := StartSegment + 1 to PointCount - 2 do
332 | begin
333 | P2 := GetPoint(PD.Points, C + 1);
334 | Intersection := GetIntersectionFromCircle(P1, P2, Start, Sqr(Radius));
335 | P1 := P2;
336 |
337 | if (Intersection >= 0) then
338 | begin
339 | Result := C + Intersection;
340 | Exit;
341 | end;
342 | end;
343 | end;
344 | Result := -1;
345 | finally
346 | PD.Free;
347 | end;
348 | end;
349 |
350 | class function TGPPathText.GetPathLength(const Path: TGPGraphicsPath): Single;
351 | var
352 | P: TGPGraphicsPath;
353 | Count, C: Integer;
354 | PD: TPathData;
355 | P1, P2: TGPPointF;
356 | begin
357 | Result := 0;
358 | P := Path.Clone;
359 | try
360 | P.Flatten(nil, 10 * FlatnessDefault);
361 |
362 | Count := P.GetPointCount;
363 | if Count > 0 then
364 | begin
365 | PD := TPathData.Create;
366 | try
367 | if (P.GetPathData(PD) = Ok) then
368 | begin
369 | P1 := GetPoint(PD.Points, 0);
370 | for C := 0 to Count - 2 do
371 | begin
372 | P2 := GetPoint(PD.Points, C + 1);
373 | P1 := SubPoint(P2, P1);
374 | Result := Result + Sqrt(Sqr(P1.X) + Sqr(P1.Y));
375 | P1 := P2;
376 | end;
377 | end;
378 | finally
379 | PD.Free;
380 | end;
381 | end;
382 | finally
383 | P.Free;
384 | end;
385 | end;
386 |
387 | function TGPPathText.GetPathPoint(const Position: TPathPosition): TGPPointF;
388 | var
389 | R: TGPPointF;
390 | Segment, Count: Integer;
391 | PD: TPathData;
392 | Diff: TGPPointF;
393 | T: Single;
394 | begin
395 | R := MakePoint(0.0, 0);
396 |
397 | if Position < 0 then
398 | begin
399 | Result := R;
400 | Exit;
401 | end;
402 |
403 | Segment := Floor(Position);
404 |
405 | Count := FGuidePath.GetPointCount;
406 | if (Segment < Count - 1) then
407 | begin
408 | PD := TPathData.Create;
409 | if (FGuidePath.GetPathData(PD) = Ok) then
410 | begin
411 | R := GetPoint(PD.Points, Segment);
412 | Diff := GetPoint(PD.Points, Segment + 1);
413 |
414 | Diff := SubPoint(Diff, R);
415 |
416 | T := Frac(Position);
417 |
418 | R.X := R.X + T * Diff.X;
419 | R.Y := R.Y + T * Diff.Y;
420 | end;
421 | PD.Free;
422 | end;
423 |
424 | Result := R;
425 | end;
426 |
427 | function TGPPathText.GetPathPointLength(const Position: TPathPosition): Single;
428 | var
429 | P1, P2: TGPPointF;
430 | Diff: TGPPointF;
431 | C, Segment, Count: Integer;
432 | PD: TPathData;
433 | begin
434 | if Position < 0 then
435 | begin
436 | Result := 0;
437 | Exit;
438 | end;
439 |
440 | Segment := Floor(Position);
441 |
442 | Result := 0;
443 | Count := FGuidePath.GetPointCount;
444 | if (Segment < Count - 1) then
445 | begin
446 | PD := TPathData.Create;
447 | try
448 | if (FGuidePath.GetPathData(PD) = Ok) then
449 | begin
450 | P1 := GetPoint(PD.Points, 0);
451 | for C := 0 to Segment - 1 do
452 | begin
453 | P2 := GetPoint(PD.Points, C + 1);
454 | Diff := SubPoint(P2, P1);
455 | Result := Result + Sqrt(Sqr(Diff.X) + Sqr(Diff.Y));
456 | P1 := P2;
457 | end;
458 |
459 | P2 := GetPoint(PD.Points, Segment + 1);
460 | Diff := SubPoint(P2, P1);
461 |
462 | Result := Result + Sqrt(Sqr(Diff.X) + Sqr(Diff.Y)) * Frac(Position);
463 | end;
464 | finally
465 | PD.Free;
466 | end;
467 | end;
468 | end;
469 |
470 | function TGPPathText.GetPathPosition(Indent: Single): TPathPosition;
471 | var
472 | PD: TPathData;
473 | C, Count: Integer;
474 | A, B: TGPPointF;
475 | Distance: Single;
476 | begin
477 | PD := TPathData.Create;
478 | try
479 | if (FGuidePath.GetPathData(PD) = Ok) then
480 | begin
481 | Count := FGuidePath.GetPointCount;
482 | A := GetPoint(PD.Points, 0);
483 | for C := 0 to Count - 2 do
484 | begin
485 | B := GetPoint(PD.Points, C + 1);
486 |
487 | Distance := Sqrt(Sqr(B.X - A.X) + Sqr(B.Y - A.Y));
488 | A := B;
489 |
490 | if (Indent < Distance) then
491 | begin
492 | Result := C + Indent / Distance;
493 | Exit;
494 | end;
495 |
496 | Indent := Indent - Distance;
497 | end;
498 | end;
499 | finally
500 | PD.Free;
501 | end;
502 | Result := -1;
503 | end;
504 |
505 | end.
506 |
--------------------------------------------------------------------------------
/gdip/GDIPUtils.pas:
--------------------------------------------------------------------------------
1 | unit GDIPUtils;
2 |
3 | interface
4 |
5 | uses
6 | System.Math.Vectors,
7 | Winapi.GDIPAPI, Winapi.GDIPOBJ;
8 |
9 | type
10 | TBoxAlignment = (baTopLeft, baTopCenter, baTopRight,
11 | baCenterLeft, baCenterCenter, baCenterRight,
12 | baBottomLeft, baBottomCenter, baBottomRight);
13 |
14 | function CalcRect(const Bounds: TGPRectF; const Width, Height: Double;
15 | const Alignment: TBoxAlignment): TGPRectF;
16 |
17 | function GetGPMatrix(const Matrix: TMatrix): TGPMatrix;
18 |
19 | implementation
20 |
21 | function CalcRect(const Bounds: TGPRectF; const Width, Height: Double;
22 | const Alignment: TBoxAlignment): TGPRectF;
23 | var
24 | R: Double;
25 | begin
26 | if Height > 0 then
27 | R := Width / Height
28 | else
29 | R := 1;
30 |
31 | if (Bounds.Height <> 0) and
32 | (Bounds.Width / Bounds.Height > R) then
33 | begin
34 | Result.Width := Bounds.Height * R;
35 | Result.Height := Bounds.Height;
36 | end else
37 | begin
38 | Result.Width := Bounds.Width;
39 | Result.Height := Bounds.Width / R;
40 | end;
41 |
42 | case Alignment of
43 | baTopCenter, baCenterCenter, baBottomCenter:
44 | Result.X := (Bounds.Width - Result.Width) / 2;
45 | baTopRight, baCenterRight, baBottomRight:
46 | Result.X := Bounds.Width - Result.Width;
47 | else
48 | Result.X := 0;
49 | end;
50 |
51 | case Alignment of
52 | baCenterLeft, baCenterCenter, baCenterRight:
53 | Result.Y := (Bounds.Height - Result.Height) / 2;
54 | baBottomLeft, baBottomCenter, baBottomRight:
55 | Result.Y := Bounds.Height - Result.Height;
56 | else
57 | Result.Y := 0;
58 | end;
59 |
60 | Result.X := Result.X + Bounds.X;
61 | Result.Y := Result.Y + Bounds.Y;
62 | end;
63 |
64 | function GetGPMatrix(const Matrix: TMatrix): TGPMatrix;
65 | begin
66 | Result := TGPMatrix.Create(Matrix.m11, Matrix.m12, Matrix.m21, Matrix.m22, Matrix.m31,
67 | Matrix.m32);
68 | end;
69 |
70 | end.
71 |
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | { Use of this library is permitted for commercial and non-commercial }
2 | { use, as long as the author is credited. }
3 | { This file (c) 2005, 2008 Martin Walter }
4 | { This Software is distributed on an "AS IS" basis, WITHOUT }
5 | { WARRANTY OF ANY KIND, either express or implied. }
6 |
--------------------------------------------------------------------------------
/svg/SVG.pas:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ekot1/DelphiSVG/0c2fc478d221a914c8ca1a15f0e65e282396000a/svg/SVG.pas
--------------------------------------------------------------------------------
/svg/SVGCommon.pas:
--------------------------------------------------------------------------------
1 | {******************************************************************}
2 | { SVG common }
3 | { }
4 | { home page : http://www.mwcs.de }
5 | { email : martin.walter@mwcs.de }
6 | { }
7 | { date : 05-04-2008 }
8 | { }
9 | { Use of this file is permitted for commercial and non-commercial }
10 | { use, as long as the author is credited. }
11 | { This file (c) 2005, 2008 Martin Walter }
12 | { }
13 | { This Software is distributed on an "AS IS" basis, WITHOUT }
14 | { WARRANTY OF ANY KIND, either express or implied. }
15 | { }
16 | { *****************************************************************}
17 |
18 | unit SVGCommon;
19 |
20 | interface
21 |
22 | uses
23 | SVGTypes;
24 |
25 | function TryStrToTFloat(const S: string; out Value: TFloat): Boolean;
26 |
27 | function StrToTFloat(const S: string): TFloat;
28 |
29 | implementation
30 |
31 | uses
32 | System.SysUtils;
33 |
34 | function TryStrToTFloat(const S: string; out Value: TFloat): Boolean;
35 | var
36 | S1: string;
37 | begin
38 | S1 := StringReplace(S, ',', FormatSettings.DecimalSeparator, [rfReplaceAll]);
39 | S1 := StringReplace(S1, '.', FormatSettings.DecimalSeparator, [rfReplaceAll]);
40 | Result := TryStrToFloat(S1, Value);
41 | if not Result then
42 | Value := 0;
43 | end;
44 |
45 | function StrToTFloat(const S: string): TFloat;
46 | begin
47 | TryStrToTFloat(S, Result);
48 | end;
49 |
50 | end.
51 |
--------------------------------------------------------------------------------
/svg/SVGPackage.dpk:
--------------------------------------------------------------------------------
1 | package SVGPackage;
2 |
3 | {$R *.res}
4 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
5 | {$ALIGN 8}
6 | {$ASSERTIONS ON}
7 | {$BOOLEVAL OFF}
8 | {$DEBUGINFO OFF}
9 | {$EXTENDEDSYNTAX ON}
10 | {$IMPORTEDDATA ON}
11 | {$IOCHECKS ON}
12 | {$LOCALSYMBOLS ON}
13 | {$LONGSTRINGS ON}
14 | {$OPENSTRINGS ON}
15 | {$OPTIMIZATION OFF}
16 | {$OVERFLOWCHECKS OFF}
17 | {$RANGECHECKS OFF}
18 | {$REFERENCEINFO ON}
19 | {$SAFEDIVIDE OFF}
20 | {$STACKFRAMES ON}
21 | {$TYPEDADDRESS OFF}
22 | {$VARSTRINGCHECKS ON}
23 | {$WRITEABLECONST OFF}
24 | {$MINENUMSIZE 1}
25 | {$IMAGEBASE $400000}
26 | {$DEFINE DEBUG}
27 | {$ENDIF IMPLICITBUILDING}
28 | {$DESCRIPTION 'MWK - SVG'}
29 | {$IMPLICITBUILD OFF}
30 |
31 | requires
32 | rtl,
33 | xmlrtl;
34 |
35 | contains
36 | SVG in 'SVG.pas',
37 | SVGColor in 'SVGColor.pas',
38 | SVGPaint in 'SVGPaint.pas',
39 | SVGParse in 'SVGParse.pas',
40 | SVGPath in 'SVGPath.pas',
41 | SVGProperties in 'SVGProperties.pas',
42 | SVGStyle in 'SVGStyle.pas',
43 | SVGTypes in 'SVGTypes.pas',
44 | SVGCommon in 'SVGCommon.pas',
45 | GDIPKerning in '..\gdip\GDIPKerning.pas',
46 | GDIPOBJ2 in '..\gdip\GDIPOBJ2.pas',
47 | GDIPPathText in '..\gdip\GDIPPathText.pas',
48 | GDIPUtils in '..\gdip\GDIPUtils.pas';
49 |
50 | end.
51 |
--------------------------------------------------------------------------------
/svg/SVGPackage.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ekot1/DelphiSVG/0c2fc478d221a914c8ca1a15f0e65e282396000a/svg/SVGPackage.res
--------------------------------------------------------------------------------
/svg/SVGPaint.pas:
--------------------------------------------------------------------------------
1 | {******************************************************************}
2 | { SVG fill classes }
3 | { }
4 | { home page : http://www.mwcs.de }
5 | { email : martin.walter@mwcs.de }
6 | { }
7 | { date : 05-04-2008 }
8 | { }
9 | { Use of this file is permitted for commercial and non-commercial }
10 | { use, as long as the author is credited. }
11 | { This file (c) 2005, 2008 Martin Walter }
12 | { }
13 | { This Software is distributed on an "AS IS" basis, WITHOUT }
14 | { WARRANTY OF ANY KIND, either express or implied. }
15 | { }
16 | { *****************************************************************}
17 |
18 | unit SVGPaint;
19 |
20 | interface
21 |
22 | uses
23 | Winapi.Windows, Winapi.GDIPOBJ, Winapi.GDIPAPI,
24 | System.UITypes, System.Classes,
25 | Xml.XmlIntf,
26 | SVGTypes, SVG;
27 |
28 | type
29 | TColors = record
30 | Colors: packed array of ARGB;
31 | Positions: packed array of Single;
32 | Count: Integer;
33 | end;
34 |
35 | TSVGStop = class(TSVGObject)
36 | strict private
37 | FStop: TFloat;
38 | FStopColor: TColor;
39 | FOpacity: TFloat;
40 |
41 | protected
42 | function New(Parent: TSVGObject): TSVGObject; override;
43 | procedure AssignTo(Dest: TPersistent); override;
44 | public
45 | procedure ReadIn(const Node: IXMLNode); override;
46 | procedure PaintToGraphics(Graphics: TGPGraphics); override;
47 | procedure PaintToPath(Path: TGPGraphicsPath); override;
48 |
49 | property Stop: TFloat read FStop write FStop;
50 | property StopColor: TColor read FStopColor write FStopColor;
51 |
52 | property Opacity: TFloat read FOpacity write FOpacity;
53 | end;
54 |
55 | TSVGFiller = class(TSVGMatrix)
56 | private
57 | protected
58 | function New(Parent: TSVGObject): TSVGObject; override;
59 | public
60 | procedure ReadIn(const Node: IXMLNode); override;
61 | function GetBrush(Alpha: Byte; const DestObject: TSVGBasic): TGPBrush; virtual; abstract;
62 | procedure PaintToGraphics(Graphics: TGPGraphics); override;
63 | procedure PaintToPath(Path: TGPGraphicsPath); override;
64 | end;
65 |
66 | TSVGGradient = class(TSVGFiller)
67 | private
68 | FURI: string;
69 | FGradientUnits: TGradientUnits;
70 | protected
71 | function GetColors(Alpha: Byte): TColors; virtual;
72 | public
73 | procedure ReadIn(const Node: IXMLNode); override;
74 | end;
75 |
76 | TSVGLinearGradient = class(TSVGGradient)
77 | private
78 | FX1: TFloat;
79 | FY1: TFloat;
80 | FX2: TFloat;
81 | FY2: TFloat;
82 | protected
83 | function New(Parent: TSVGObject): TSVGObject; override;
84 | procedure AssignTo(Dest: TPersistent); override;
85 | public
86 | procedure ReadIn(const Node: IXMLNode); override;
87 | function GetBrush(Alpha: Byte; const DestObject: TSVGBasic): TGPBrush; override;
88 |
89 | property X1: TFloat read FX1 write FX1;
90 | property Y1: TFloat read FY1 write FY1;
91 | property X2: TFloat read FX2 write FX2;
92 | property Y2: TFloat read FY2 write FY2;
93 | end;
94 |
95 | TSVGRadialGradient = class(TSVGGradient)
96 | private
97 | FCX: TFloat;
98 | FCY: TFloat;
99 | FR: TFloat;
100 | FFX: TFloat;
101 | FFY: TFloat;
102 | protected
103 | function New(Parent: TSVGObject): TSVGObject; override;
104 | procedure AssignTo(Dest: TPersistent); override;
105 | public
106 | procedure Clear; override;
107 | procedure ReadIn(const Node: IXMLNode); override;
108 | function GetBrush(Alpha: Byte; const DestObject: TSVGBasic): TGPBrush; override;
109 |
110 | property CX: TFloat read FCX write FCX;
111 | property CY: TFloat read FCY write FCY;
112 | property R: TFloat read FR write FR;
113 | property FX: TFloat read FFX write FFX;
114 | property FY: TFloat read FFY write FFY;
115 | end;
116 |
117 |
118 | implementation
119 |
120 | uses
121 | System.SysUtils, System.Math.Vectors,
122 | SVGParse, SVGStyle, SVGProperties, SVGColor,
123 | GDIPUtils;
124 |
125 | // TSVGStop
126 |
127 | procedure TSVGStop.PaintToPath(Path: TGPGraphicsPath);
128 | begin
129 | end;
130 |
131 | procedure TSVGStop.ReadIn(const Node: IXMLNode);
132 | var
133 | S: string;
134 | begin
135 | inherited;
136 | LoadPercent(Node, 'offset', FStop);
137 |
138 | LoadString(Node, 'stop-color', S);
139 | FStopColor := GetColor(S);
140 |
141 | if FStopColor = INHERIT then
142 | begin
143 | S := Style['stop-color'];
144 | FStopColor := GetColor(S);
145 | end;
146 |
147 | S := Style['stop-opacity'];
148 | if (S <> '') then
149 | FOpacity := ParsePercent(S)
150 | else
151 | FOpacity := 1;
152 |
153 | if (FOpacity < 0) then
154 | FOpacity := 0;
155 |
156 | if (FOpacity > 1) then
157 | FOpacity := 1;
158 | end;
159 |
160 | procedure TSVGStop.AssignTo(Dest: TPersistent);
161 | begin
162 | inherited;
163 | if Dest is TSVGStop then
164 | begin
165 | TSVGStop(Dest).FStop := FStop;
166 | TSVGStop(Dest).FStopColor := FStopColor;
167 | end;
168 | end;
169 |
170 | function TSVGStop.New(Parent: TSVGObject): TSVGObject;
171 | begin
172 | Result := TSVGStop.Create(Parent);
173 | end;
174 |
175 | procedure TSVGStop.PaintToGraphics(Graphics: TGPGraphics);
176 | begin
177 | end;
178 |
179 | // TSVGFiller
180 |
181 | procedure TSVGFiller.PaintToPath(Path: TGPGraphicsPath);
182 | begin
183 | end;
184 |
185 | procedure TSVGFiller.ReadIn(const Node: IXMLNode);
186 | begin
187 | inherited;
188 | Display := 0;
189 | end;
190 |
191 | function TSVGFiller.New(Parent: TSVGObject): TSVGObject;
192 | begin
193 | Result := nil;
194 | end;
195 |
196 | procedure TSVGFiller.PaintToGraphics(Graphics: TGPGraphics);
197 | begin
198 | end;
199 |
200 | // TSVGGradient
201 |
202 | procedure TSVGGradient.ReadIn(const Node: IXMLNode);
203 | var
204 | C: Integer;
205 | Stop: TSVGStop;
206 | begin
207 | inherited;
208 |
209 | LoadGradientUnits(Node, FGradientUnits);
210 |
211 | for C := 0 to Node.childNodes.count - 1 do
212 | if Node.childNodes[C].nodeName = 'stop' then
213 | begin
214 | Stop := TSVGStop.Create(Self);
215 | Stop.ReadIn(Node.childNodes[C]);
216 | end;
217 |
218 | FURI := Style['xlink:href'];
219 | if FURI <> '' then
220 | begin
221 | FURI := Trim(FURI);
222 | if (FURI <> '') and (FURI[1] = '#') then
223 | FURI := Copy(FURI, 2, MaxInt);
224 | end;
225 | end;
226 |
227 | // TSVGLinearGradient
228 |
229 | function TSVGLinearGradient.New(Parent: TSVGObject): TSVGObject;
230 | begin
231 | Result := TSVGLinearGradient.Create(Parent);
232 | end;
233 |
234 | procedure TSVGLinearGradient.ReadIn(const Node: IXMLNode);
235 | var
236 | Matrix: TMatrix;
237 | begin
238 | inherited;
239 | LoadLength(Node, 'x1', FX1);
240 | LoadLength(Node, 'y1', FY1);
241 | LoadLength(Node, 'x2', FX2);
242 | LoadLength(Node, 'y2', FY2);
243 |
244 | FillChar(Matrix, SizeOf(Matrix), 0);
245 | LoadTransform(Node, 'gradientTransform', Matrix);
246 | PureMatrix := Matrix;
247 | end;
248 |
249 | procedure TSVGLinearGradient.AssignTo(Dest: TPersistent);
250 | begin
251 | inherited;
252 | if Dest is TSVGLinearGradient then
253 | begin
254 | TSVGLinearGradient(Dest).FX1 := FX1;
255 | TSVGLinearGradient(Dest).FY1 := FY1;
256 | TSVGLinearGradient(Dest).FX2 := FX2;
257 | TSVGLinearGradient(Dest).FY2 := FY2;
258 | end;
259 | end;
260 |
261 | function TSVGLinearGradient.GetBrush(Alpha: Byte; const DestObject: TSVGBasic): TGPBrush;
262 | var
263 | Brush: TGPLinearGradientBrush;
264 | TGP: TGPMatrix;
265 | Colors: TColors;
266 | begin
267 | if Assigned(DestObject) and (FGradientUnits = guObjectBoundingBox) then
268 | Brush := TGPLinearGradientBrush.Create(MakePoint(DestObject.X, DestObject.Y),
269 | MakePoint(DestObject.X + DestObject.Width, DestObject.Y + DestObject.Height), 0, 0)
270 | else
271 | Brush := TGPLinearGradientBrush.Create(MakePoint(FX1, FY1), MakePoint(FX2, FY2), 0, 0);
272 |
273 | Colors := GetColors(Alpha);
274 |
275 | Brush.SetInterpolationColors(PGPColor(Colors.Colors),
276 | PSingle(Colors.Positions), Colors.Count);
277 |
278 | Finalize(Colors);
279 |
280 | if PureMatrix.m33 = 1 then
281 | begin
282 | TGP := GetGPMatrix(PureMatrix);
283 | Brush.SetTransform(TGP);
284 | TGP.Free;
285 | end;
286 |
287 | Result := Brush;
288 | end;
289 |
290 | // TSVGRadialGradient
291 |
292 | procedure TSVGRadialGradient.AssignTo(Dest: TPersistent);
293 | begin
294 | inherited;
295 | if Dest is TSVGRadialGradient then
296 | begin
297 | TSVGRadialGradient(Dest).FCX := FCX;
298 | TSVGRadialGradient(Dest).FCY := FCY;
299 | TSVGRadialGradient(Dest).FFX := FFX;
300 | TSVGRadialGradient(Dest).FFY := FFY;
301 | TSVGRadialGradient(Dest).FR := FR;
302 | end;
303 | end;
304 |
305 | procedure TSVGRadialGradient.Clear;
306 | begin
307 | inherited;
308 |
309 | FCX := 0.5;
310 | FCY := 0.5;
311 | FR := 0.5;
312 | FFX := FCX;
313 | FFY := FCY;
314 | end;
315 |
316 | procedure TSVGRadialGradient.ReadIn(const Node: IXMLNode);
317 | begin
318 | inherited;
319 |
320 | LoadLength(Node, 'cx', FCX);
321 | LoadLength(Node, 'cy', FCY);
322 | LoadLength(Node, 'r', FR);
323 | LoadLength(Node, 'fx', FFX);
324 | LoadLength(Node, 'fy', FFY);
325 | end;
326 |
327 | function TSVGRadialGradient.GetBrush(Alpha: Byte; const DestObject: TSVGBasic): TGPBrush;
328 | var
329 | Brush: TGPPathGradientBrush;
330 | Path: TGPGraphicsPath;
331 | TGP: TGPMatrix;
332 | Colors: TColors;
333 | begin
334 | Path := TGPGraphicsPath.Create;
335 |
336 | if Assigned(DestObject) and (FGradientUnits = guObjectBoundingBox) then
337 | Path.AddEllipse(DestObject.X, DestObject.Y, DestObject.Width, DestObject.Height)
338 | else
339 | Path.AddEllipse(FCX - FR, FCY - FR, 2 * FR, 2 * FR);
340 |
341 | Brush := TGPPathGradientBrush.Create(Path);
342 | Path.Free;
343 |
344 | Colors := GetColors(Alpha);
345 | Brush.SetInterpolationColors(PARGB(Colors.Colors), PSingle(Colors.Positions), Colors.Count);
346 |
347 | Finalize(Colors);
348 |
349 | Brush.SetCenterPoint(MakePoint(FFX, FFY));
350 |
351 | if PureMatrix.m33 = 1 then
352 | begin
353 | TGP := GetGPMatrix(PureMatrix);
354 | Brush.SetTransform(TGP);
355 | TGP.Free;
356 | end;
357 |
358 | Result := Brush;
359 | end;
360 |
361 | function TSVGRadialGradient.New(Parent: TSVGObject): TSVGObject;
362 | begin
363 | Result := TSVGRadialGradient.Create(Parent);
364 | end;
365 |
366 | function TSVGGradient.GetColors(Alpha: Byte): TColors;
367 | var
368 | C, Start, ColorCount: Integer;
369 | Stop: TSVGStop;
370 | Item: TSVGGradient;
371 | begin
372 | Result.Count := 0;
373 | if FURI = '' then
374 | Item := Self
375 | else
376 | begin
377 | Item := TSVGGradient(GetRoot.FindByID(FURI));
378 | if not (Item is TSVGGradient) then
379 | Exit;
380 | end;
381 |
382 | Start := 0;
383 | ColorCount := Item.Count;
384 |
385 | if Item.Count = 0 then
386 | Exit;
387 |
388 | if TSVGStop(Item.Items[ColorCount - 1]).Stop < 1 then
389 | Inc(ColorCount);
390 |
391 | if TSVGStop(Item.Items[0]).Stop > 0 then
392 | begin
393 | Inc(ColorCount);
394 | Inc(Start);
395 | end;
396 |
397 | SetLength(Result.Colors, ColorCount);
398 | SetLength(Result.Positions, ColorCount);
399 |
400 | if Start > 0 then
401 | begin
402 | Stop := TSVGStop(Item.Items[0]);
403 | Result.Colors[0] := ConvertColor(Stop.StopColor, Round(Alpha * Stop.Opacity));
404 | Result.Positions[0] := 0;
405 | end;
406 |
407 | for C := 0 to Item.Count - 1 do
408 | begin
409 | Stop := TSVGStop(Item.Items[C]);
410 | Result.Colors[C + Start] := ConvertColor(Stop.StopColor, Round(Alpha * Stop.Opacity));
411 | Result.Positions[C + Start] := Stop.Stop;
412 | end;
413 |
414 | if (ColorCount - Start) > Item.Count then
415 | begin
416 | Stop := TSVGStop(Item.Items[Item.Count - 1]);
417 | Result.Colors[ColorCount - 1] := ConvertColor(Stop.StopColor, Round(Alpha * Stop.Opacity));
418 | Result.Positions[ColorCount - 1] := 1;
419 | end;
420 |
421 | Result.Count := ColorCount;
422 | end;
423 |
424 | end.
425 |
--------------------------------------------------------------------------------
/svg/SVGParse.pas:
--------------------------------------------------------------------------------
1 | {******************************************************************}
2 | { Parse of SVG property values }
3 | { }
4 | { home page : http://www.mwcs.de }
5 | { email : martin.walter@mwcs.de }
6 | { }
7 | { date : 05-04-2008 }
8 | { }
9 | { Use of this file is permitted for commercial and non-commercial }
10 | { use, as long as the author is credited. }
11 | { This file (c) 2005, 2008 Martin Walter }
12 | { }
13 | { This Software is distributed on an "AS IS" basis, WITHOUT }
14 | { WARRANTY OF ANY KIND, either express or implied. }
15 | { }
16 | { *****************************************************************}
17 |
18 | unit SVGParse;
19 |
20 | interface
21 |
22 | uses
23 | System.Types, System.Classes, System.Math.Vectors,
24 | SVGTypes;
25 |
26 | function ParseAngle(const Angle: string): TFloat;
27 |
28 | function ParsePercent(const S: string): TFloat;
29 |
30 | function ParseInteger(const S: string): Integer;
31 |
32 | function ParseLength(const S: string): TFloat;
33 |
34 | function ParseUnit(const S: string): TSVGUnit;
35 |
36 | function ParseDRect(const S: string): TRectF;
37 |
38 | function ParseURI(const URI: string): string;
39 |
40 | function ParseTransform(const ATransform: string): TMatrix;
41 |
42 | implementation
43 |
44 | uses
45 | System.SysUtils, System.Math, System.StrUtils,
46 | SVGCommon;
47 |
48 | function ParseAngle(const Angle: string): TFloat;
49 | var
50 | D: TFloat;
51 | C: Integer;
52 | S: string;
53 | begin
54 | if Angle <> '' then
55 | begin
56 | S := Angle;
57 | C := Pos('deg', S);
58 | if C <> 0 then
59 | begin
60 | S := LeftStr(S, C - 1);
61 | if TryStrToTFloat(S, D) then
62 | Result := DegToRad(D)
63 | else
64 | Result := 0;
65 | Exit;
66 | end;
67 |
68 | C := Pos('rad', S);
69 | if C <> 0 then
70 | begin
71 | TryStrToTFloat(S, Result);
72 | Exit;
73 | end;
74 |
75 | C := Pos('grad', S);
76 | if C <> 0 then
77 | begin
78 | S := LeftStr(S, C - 1);
79 | if TryStrToTFloat(S, D) then
80 | Result := GradToRad(D)
81 | else
82 | Result := 0;
83 | Exit;
84 | end;
85 |
86 | if TryStrToTFloat(S, D) then
87 | Result := DegToRad(D)
88 | else
89 | Result := 0;
90 | end else
91 | Result := 0;
92 | end;
93 |
94 | function ParsePercent(const S: string): TFloat;
95 | begin
96 | if Length(S) = 0 then
97 | begin
98 | Result := -1;
99 | end
100 | else
101 | begin
102 | if S[Length(S)] = '%' then
103 | Result := StrToTFloat(LeftStr(S, Length(S) - 1)) / 100
104 | else
105 | Result := StrToTFloat(S);
106 | end;
107 | end;
108 |
109 | function ParseInteger(const S: string): Integer;
110 | begin
111 | Result := StrToInt(S);
112 | end;
113 |
114 | const
115 | CFactors: array [TSVGUnit] of TFloat =
116 | (1, // suNone
117 | 1, // suPX
118 | 1.33, // suPT
119 | 12*1.33, // suPC
120 | 96/25.4, // suMM
121 | 96/2.54, // suCM
122 | 96, // suIN
123 | 16, // suEM
124 | 1, // suEX
125 | 1); // suPercent
126 |
127 | function ParseLength(const S: string): TFloat;
128 | var
129 | SVGUnit: TSVGUnit;
130 | Number: TFloat;
131 | N: string;
132 | begin
133 | SVGUnit := ParseUnit(S);
134 | case SVGUnit of
135 | suPercent: N := Copy(S, 1, Length(S) - 1);
136 | suNone : N := S;
137 | else
138 | N := Copy(S, 1, Length(S) - 2);
139 | end;
140 | Number := StrToTFloat(N);
141 |
142 | Result := Number * CFactors[SVGUnit];
143 | end;
144 |
145 | function ParseUnit(const S: string): TSVGUnit;
146 | var
147 | Suffix: string;
148 | begin
149 | Suffix := RightStr(S, 2);
150 | if Suffix = 'px' then
151 | begin
152 | Result := suPx;
153 | end
154 | else if Suffix = 'pt' then
155 | begin
156 | Result := suPt;
157 | end
158 | else if Suffix = 'pc' then
159 | begin
160 | Result := suPC;
161 | end
162 | else if Suffix = 'mm' then
163 | begin
164 | Result := suMM;
165 | end
166 | else if Suffix = 'cm' then
167 | begin
168 | Result := suCM;
169 | end
170 | else if Suffix = 'in' then
171 | begin
172 | Result := suIN;
173 | end
174 | else if Suffix = 'em' then
175 | begin
176 | Result := suEM;
177 | end
178 | else if Suffix = 'ex' then
179 | begin
180 | Result := suEX;
181 | end
182 | else if Suffix.EndsWith('%') then
183 | begin
184 | Result := suPercent;
185 | end
186 | else
187 | begin
188 | Result := suNone;
189 | end;
190 | end;
191 |
192 | function GetValues(const S: string; const Delimiter: Char): TStrings;
193 | var
194 | C: Integer;
195 | begin
196 | Result := TStringList.Create;
197 | Result.Delimiter := Delimiter;
198 | Result.DelimitedText := S;
199 |
200 | for C := Result.Count - 1 downto 0 do
201 | begin
202 | if Result[C] = '' then
203 | begin
204 | Result.Delete(C);
205 | end;
206 | end;
207 | end;
208 |
209 | function ParseDRect(const S: string): TRectF;
210 | var
211 | SL: TStrings;
212 | begin
213 | FillChar(Result, SizeOf(Result), 0);
214 |
215 | SL := GetValues(Trim(S), ' ');
216 |
217 | try
218 | if SL.Count = 4 then
219 | begin
220 | Result.Left := ParseLength(SL[0]);
221 | Result.Top := ParseLength(SL[1]);
222 | Result.Width := ParseLength(SL[2]);
223 | Result.Height := ParseLength(SL[3]);
224 | end;
225 | finally
226 | SL.Free;
227 | end;
228 | end;
229 |
230 | function ParseURI(const URI: string): string;
231 | const
232 | CUriPrefix = 'url(#';
233 | CUriPrefixLength = Length(CUriPrefix);
234 | var
235 | S: string;
236 | begin
237 | if Length(URI) = 0 then
238 | begin
239 | Result := '';
240 | end
241 | else
242 | begin
243 | S := Trim(URI);
244 |
245 | if S.StartsWith(CUriPrefix) and S.EndsWith(')') then
246 | begin
247 | Result := Copy(S, CUriPrefixLength + 1, Length(S) - (CUriPrefixLength + 1));
248 | end
249 | else
250 | begin
251 | Result := '';
252 | end;
253 | end;
254 | end;
255 |
256 | function GetMatrix(const S: string): TMatrix;
257 | var
258 | SL: TStrings;
259 | begin
260 | Result := TMatrix.Identity;
261 | SL := GetValues(S, ',');
262 | try
263 | if SL.Count = 6 then
264 | begin
265 | Result.m11 := StrToTFloat(SL[0]);
266 | Result.m12 := StrToTFloat(SL[1]);
267 | Result.m21 := StrToTFloat(SL[2]);
268 | Result.m22 := StrToTFloat(SL[3]);
269 | Result.m31 := StrToTFloat(SL[4]);
270 | Result.m32 := StrToTFloat(SL[5]);
271 | end;
272 | finally
273 | SL.Free;
274 | end;
275 | end;
276 |
277 | function GetTranslate(const S: string): TMatrix;
278 | var
279 | SL: TStrings;
280 | begin
281 | FillChar(Result, SizeOf(Result), 0);
282 | SL := GetValues(S, ',');
283 | try
284 | if SL.Count = 1 then
285 | SL.Add('0');
286 |
287 | if SL.Count = 2 then
288 | begin
289 | Result := TMatrix.CreateTranslation(StrToTFloat(SL[0]), StrToTFloat(SL[1]));
290 | end;
291 | finally
292 | SL.Free;
293 | end;
294 | end;
295 |
296 | function GetScale(const S: string): TMatrix;
297 | var
298 | SL: TStrings;
299 | begin
300 | FillChar(Result, SizeOf(Result), 0);
301 | SL := GetValues(S, ',');
302 | try
303 | if SL.Count = 1 then
304 | SL.Add(SL[0]);
305 | if SL.Count = 2 then
306 | begin
307 | Result := TMatrix.CreateScaling(StrToTFloat(SL[0]), StrToTFloat(SL[1]));
308 | end;
309 | finally
310 | SL.Free;
311 | end;
312 | end;
313 |
314 | function GetRotation(const S: string): TMatrix;
315 | var
316 | SL: TStrings;
317 | X, Y, Angle: TFloat;
318 | begin
319 | SL := GetValues(S, ',');
320 | try
321 | Angle := ParseAngle(SL[0]);
322 |
323 | if SL.Count = 3 then
324 | begin
325 | X := StrToTFloat(SL[1]);
326 | Y := StrToTFloat(SL[2]);
327 | end else
328 | begin
329 | X := 0;
330 | Y := 0;
331 | end;
332 | finally
333 | SL.Free;
334 | end;
335 |
336 | Result := TMatrix.CreateTranslation(X, Y);
337 | Result := TMatrix.CreateRotation(Angle) * Result;
338 | Result := TMatrix.CreateTranslation(-X, -Y) * Result;
339 | end;
340 |
341 | function GetSkewX(const S: string): TMatrix;
342 | var
343 | SL: TStrings;
344 | Angle: TFloat;
345 | begin
346 | FillChar(Result, SizeOf(Result), 0);
347 |
348 | SL := GetValues(S, ',');
349 | try
350 | if SL.Count = 1 then
351 | begin
352 | Result := TMatrix.Identity;
353 | Angle := ParseAngle(SL[0]);
354 | Result.m21 := Tan(Angle);
355 | end;
356 | finally
357 | SL.Free;
358 | end;
359 | end;
360 |
361 | function GetSkewY(const S: string): TMatrix;
362 | var
363 | SL: TStrings;
364 | Angle: TFloat;
365 | begin
366 | FillChar(Result, SizeOf(Result), 0);
367 |
368 | SL := GetValues(S, ',');
369 | try
370 | if SL.Count = 1 then
371 | begin
372 | Result := TMatrix.Identity;
373 | Angle := ParseAngle(SL[0]);
374 | Result.m12 := Tan(Angle);
375 | end;
376 | finally
377 | SL.Free;
378 | end;
379 | end;
380 |
381 | function ParseTransform(const ATransform: string): TMatrix;
382 | var
383 | Start: Integer;
384 | Stop: Integer;
385 | TType: string;
386 | Values: string;
387 | S: string;
388 | M: TMatrix;
389 | begin
390 | FillChar(Result, SizeOf(Result), 0);
391 |
392 | S := Trim(ATransform);
393 |
394 | while S <> '' do
395 | begin
396 | Start := Pos('(', S);
397 | Stop := Pos(')', S);
398 | if (Start = 0) or (Stop = 0) then
399 | Exit;
400 | TType := Copy(S, 1, Start - 1);
401 | Values := Trim(Copy(S, Start + 1, Stop - Start - 1));
402 | Values := StringReplace(Values, ' ', ',', [rfReplaceAll]);
403 | M.m33 := 0;
404 |
405 | if TType = 'matrix' then
406 | begin
407 | M := GetMatrix(Values);
408 | end
409 | else if TType = 'translate' then
410 | begin
411 | M := GetTranslate(Values);
412 | end
413 | else if TType = 'scale' then
414 | begin
415 | M := GetScale(Values);
416 | end
417 | else if TType = 'rotate' then
418 | begin
419 | M := GetRotation(Values);
420 | end
421 | else if TType = 'skewX' then
422 | begin
423 | M := GetSkewX(Values);
424 | end
425 | else if TType = 'skewY' then
426 | begin
427 | M := GetSkewY(Values);
428 | end;
429 |
430 | if M.m33 = 1 then
431 | begin
432 | if Result.m33 = 0 then
433 | Result := M
434 | else
435 | Result := M * Result;
436 | end;
437 |
438 | S := Trim(Copy(S, Stop + 1, Length(S)));
439 | end;
440 | end;
441 |
442 | end.
443 |
--------------------------------------------------------------------------------
/svg/SVGProperties.pas:
--------------------------------------------------------------------------------
1 | {******************************************************************}
2 | { Parse of SVG properties }
3 | { }
4 | { home page : http://www.mwcs.de }
5 | { email : martin.walter@mwcs.de }
6 | { }
7 | { date : 05-04-2008 }
8 | { }
9 | { Use of this file is permitted for commercial and non-commercial }
10 | { use, as long as the author is credited. }
11 | { This file (c) 2005, 2008 Martin Walter }
12 | { }
13 | { This Software is distributed on an "AS IS" basis, WITHOUT }
14 | { WARRANTY OF ANY KIND, either express or implied. }
15 | { }
16 | { *****************************************************************}
17 |
18 | unit SVGProperties;
19 |
20 | interface
21 |
22 | uses
23 | System.Math.Vectors,
24 | Xml.XmlIntf,
25 | SVGTypes;
26 |
27 | procedure LoadLength(const Node: IXMLNode; const S: string; var X: TFloat);
28 |
29 | procedure LoadTFloat(const Node: IXMLNode; const S: string; var X: TFloat);
30 |
31 | function LoadString(const Node: IXMLNode; const S: string): string; overload;
32 | procedure LoadString(const Node: IXMLNode; const S: string; var X: string); overload;
33 |
34 | procedure LoadTransform(const Node: IXMLNode; const S: string; var Matrix: TMatrix);
35 |
36 | procedure LoadPercent(const Node: IXMLNode; const S: string; var X: TFloat); overload;
37 | procedure LoadPercent(const Node: IXMLNode; const S: string; Max: Integer; var X: TFloat); overload;
38 | procedure LoadBytePercent(const Node: IXMLNode; const S: string; var X: Integer);
39 |
40 | procedure LoadBoolean(const Node: IXMLNode; const S: string; var X: Boolean);
41 |
42 | procedure LoadDisplay(const Node: IXMLNode; var X: Integer);
43 |
44 | procedure LoadVisible(const Node: IXMLNode; var X: Integer);
45 |
46 | procedure LoadGradientUnits(const Node: IXMLNode; var Units: TGradientUnits);
47 |
48 | implementation
49 |
50 | uses
51 | SVGCommon, SVGParse;
52 |
53 | procedure LoadLength(const Node: IXMLNode; const S: string; var X: TFloat);
54 | var
55 | Attribute: IXMLNode;
56 | begin
57 | Attribute := Node.AttributeNodes.FindNode(S);
58 | if Assigned(Attribute) then
59 | begin
60 | X := ParseLength(Attribute.nodeValue);
61 | end;
62 | end;
63 |
64 | procedure LoadTFloat(const Node: IXMLNode; const S: string; var X: TFloat);
65 | var
66 | Attribute: IXMLNode;
67 | begin
68 | Attribute := Node.AttributeNodes.FindNode(S);
69 | if Assigned(Attribute) then
70 | begin
71 | X := StrToTFloat(Attribute.nodeValue);
72 | end;
73 | end;
74 |
75 | function LoadString(const Node: IXMLNode; const S: string): string;
76 | var
77 | Attribute: IXMLNode;
78 | begin
79 | Attribute := Node.AttributeNodes.FindNode(S);
80 | if Assigned(Attribute) then
81 | begin
82 | Result := Attribute.text;
83 | end
84 | else
85 | begin
86 | Result := '';
87 | end;
88 | end;
89 |
90 | procedure LoadString(const Node: IXMLNode; const S: string; var X: string);
91 | var
92 | Attribute: IXMLNode;
93 | begin
94 | Attribute := Node.AttributeNodes.FindNode(S);
95 | if Assigned(Attribute) then
96 | begin
97 | X := Attribute.text;
98 | end;
99 | end;
100 |
101 | procedure LoadTransform(const Node: IXMLNode; const S: string;
102 | var Matrix: TMatrix);
103 | var
104 | Attribute: IXMLNode;
105 | begin
106 | Attribute := Node.AttributeNodes.FindNode(S);
107 | if Assigned(Attribute) then
108 | begin
109 | Matrix := ParseTransform(Attribute.nodeValue);
110 | end;
111 | end;
112 |
113 | procedure LoadPercent(const Node: IXMLNode; const S: string;
114 | var X: TFloat);
115 | var
116 | Attribute: IXMLNode;
117 | begin
118 | Attribute := Node.AttributeNodes.FindNode(S);
119 | if Assigned(Attribute) then
120 | X := ParsePercent(Attribute.nodeValue);
121 | end;
122 |
123 | procedure LoadPercent(const Node: IXMLNode; const S: string;
124 | Max: Integer; var X: TFloat);
125 | var
126 | Attribute: IXMLNode;
127 | begin
128 | Attribute := Node.AttributeNodes.FindNode(S);
129 | if Assigned(Attribute) then
130 | begin
131 | X := Max * ParsePercent(Attribute.nodeValue);
132 | end;
133 | end;
134 |
135 | procedure LoadBytePercent(const Node: IXMLNode; const S: string;
136 | var X: Integer);
137 | var
138 | Attribute: IXMLNode;
139 | begin
140 | Attribute := Node.AttributeNodes.FindNode(S);
141 | if Assigned(Attribute) then
142 | begin
143 | X := Round(255 * ParsePercent(Attribute.nodeValue));
144 | end;
145 | end;
146 |
147 | procedure LoadBoolean(const Node: IXMLNode; const S: string;
148 | var X: Boolean);
149 | var
150 | Attribute: IXMLNode;
151 | begin
152 | Attribute := Node.AttributeNodes.FindNode(S);
153 | if Assigned(Attribute) then
154 | X := Boolean(ParseInteger(Attribute.nodeValue));
155 | end;
156 |
157 | procedure LoadDisplay(const Node: IXMLNode; var X: Integer);
158 | var
159 | S: string;
160 | Attribute: IXMLNode;
161 | begin
162 | Attribute := Node.AttributeNodes.FindNode('display');
163 | if Assigned(Attribute) then
164 | begin
165 | S := Attribute.nodeValue;
166 | if S = 'inherit' then
167 | X := -1
168 | else
169 | if S = 'none' then
170 | X := 0
171 | else
172 | X := 1;
173 | end;
174 | end;
175 |
176 | procedure LoadVisible(const Node: IXMLNode; var X: Integer);
177 | var
178 | S: string;
179 | Attribute: IXMLNode;
180 | begin
181 | Attribute := Node.AttributeNodes.FindNode('visibility');
182 | if Assigned(Attribute) then
183 | begin
184 | S := Attribute.nodeValue;
185 | if S = 'inherit' then
186 | X := -1
187 | else
188 | if S = 'visible' then
189 | X := 1
190 | else
191 | X := 0;
192 | end;
193 | end;
194 |
195 | procedure LoadGradientUnits(const Node: IXMLNode; var Units: TGradientUnits);
196 | var
197 | S: string;
198 | Attribute: IXMLNode;
199 | begin
200 | Units := guObjectBoundingBox;
201 | Attribute := Node.AttributeNodes.FindNode('gradientUnits');
202 | if Assigned(Attribute) then
203 | begin
204 | S := Attribute.nodeValue;
205 | if S = 'userSpaceOnUse' then
206 | Units := guUserSpaceOnUse
207 | else
208 | if S = 'objectBoundingBox' then
209 | Units := guObjectBoundingBox;
210 | end;
211 | end;
212 |
213 | end.
214 |
--------------------------------------------------------------------------------
/svg/SVGStyle.pas:
--------------------------------------------------------------------------------
1 | {******************************************************************}
2 | { SVG style class }
3 | { }
4 | { home page : http://www.mwcs.de }
5 | { email : martin.walter@mwcs.de }
6 | { }
7 | { date : 26-04-2005 }
8 | { }
9 | { Use of this file is permitted for commercial and non-commercial }
10 | { use, as long as the author is credited. }
11 | { This file (c) 2005 Martin Walter }
12 | { }
13 | { This Software is distributed on an "AS IS" basis, WITHOUT }
14 | { WARRANTY OF ANY KIND, either express or implied. }
15 | { }
16 | { *****************************************************************}
17 |
18 | unit SVGStyle;
19 |
20 | interface
21 |
22 | uses
23 | System.Classes, System.Contnrs;
24 |
25 | type
26 | TStyle = class(TObject)
27 | strict private
28 | FValues: TStrings;
29 | function GetCount: Integer;
30 | procedure PutValues(const Key: string; const Value: string);
31 | function GetValues(const Key: string): string;
32 |
33 | procedure PutValuesByNum(const Index: Integer; const Value: string);
34 | function GetValuesByNum(const Index: Integer): string;
35 |
36 | procedure PutKey(const Index: Integer; const Key: string);
37 | function GetKey(const Index: Integer): string;
38 |
39 | function Dequote(const Value: string): string;
40 | private
41 | FName: string;
42 | strict private
43 | FOnChange: TNotifyEvent;
44 | procedure DoOnChange;
45 | public
46 | constructor Create;
47 | destructor Destroy; override;
48 | procedure Clear;
49 | function Clone: TStyle;
50 | procedure SetValues(const Values: string);
51 |
52 | function AddStyle(const Key, Value: string): Integer;
53 | function IndexOf(const Key: string): Integer;
54 | procedure Delete(Index: Integer);
55 | function Remove(const Key: string): Integer;
56 |
57 | property Count: Integer read GetCount;
58 | property Values[const Key: string]: string read GetValues write PutValues; default;
59 | property ValuesByNum[const Index: Integer]: string read GetValuesByNum write PutValuesByNum;
60 | property Keys[const Index: Integer]: string read GetKey write PutKey;
61 | property OnChange: TNotifyEvent read FOnChange write FOnChange;
62 | end;
63 |
64 | TStyleList = class(TObject)
65 | strict private
66 | FList: TObjectList;
67 |
68 | function GetCount: Integer;
69 | function GetStyle(const Index: Integer): TStyle;
70 | procedure PutStyle(const Index: Integer; Style: TStyle);
71 | public
72 | constructor Create;
73 | destructor Destroy; override;
74 | procedure Clear;
75 | function Clone: TStyleList;
76 |
77 | procedure Delete(Index: Integer);
78 | function Remove(const Style: TStyle): Integer;
79 | function Add(const AStyle: TStyle): Integer; overload;
80 | function Add(const Name, Values: string): Integer; overload;
81 | function Add(const AStyle: string): Integer; overload;
82 |
83 | procedure Insert(Index: Integer; Style: TStyle); overload;
84 | procedure Insert(Index: Integer; const Name, Values: string); overload;
85 | procedure Exchange(Index1, Index2: Integer);
86 | procedure Move(CurIndex, NewIndex: Integer);
87 | function IndexOf(Style: TStyle): Integer;
88 | function GetStyleByName(const Name: string): TStyle;
89 |
90 | property Style[const Index: Integer]: TStyle read GetStyle write PutStyle; default;
91 | property Count: Integer read GetCount;
92 | end;
93 |
94 | implementation
95 |
96 | uses
97 | System.SysUtils, System.StrUtils;
98 |
99 | {$REGION 'TStyle'}
100 |
101 | constructor TStyle.Create;
102 | begin
103 | inherited;
104 | FValues := TStringList.Create;
105 | FValues.NameValueSeparator := '"';
106 | end;
107 |
108 | destructor TStyle.Destroy;
109 | begin
110 | FreeAndNil(FValues);
111 | inherited;
112 | end;
113 |
114 | procedure TStyle.Clear;
115 | begin
116 | if FValues <> nil then
117 | begin
118 | FValues.Clear;
119 | end;
120 | end;
121 |
122 | function TStyle.Clone: TStyle;
123 | begin
124 | Result := TStyle.Create;
125 | Result.FName := FName;
126 | Result.FValues.Assign(FValues);
127 | end;
128 |
129 | function TStyle.GetCount: Integer;
130 | begin
131 | Result := FValues.Count;
132 | end;
133 |
134 | procedure TStyle.DoOnChange;
135 | begin
136 | if Assigned(FOnChange) then
137 | begin
138 | FOnChange(Self);
139 | end;
140 | end;
141 |
142 | procedure TStyle.PutValues(const Key: string; const Value: string);
143 | var
144 | Index: Integer;
145 | begin
146 | Index := IndexOf(Key);
147 | if Index > 0 then
148 | PutValuesByNum(Index, Value)
149 | else
150 | AddStyle(Key, Value);
151 | end;
152 |
153 | function TStyle.GetValues(const Key: string): string;
154 | begin
155 | Result := GetValuesByNum(IndexOf(Key));
156 | end;
157 |
158 | procedure TStyle.PutValuesByNum(const Index: Integer; const Value: string);
159 | begin
160 | if (Index >= 0) and (Index < FValues.Count) then
161 | FValues.ValueFromIndex[Index] := DeQuote(Value);
162 | end;
163 |
164 | function TStyle.GetValuesByNum(const Index: Integer): string;
165 | begin
166 | if (Index >= 0) and (Index < FValues.Count) then
167 | Result := FValues.ValueFromIndex[Index]
168 | else
169 | Result := '';
170 | end;
171 |
172 | procedure TStyle.PutKey(const Index: Integer; const Key: string);
173 | begin
174 | if (Index >= 0) and (Index < FValues.Count) then
175 | FValues[Index] := Key + FValues.NameValueSeparator + FValues.ValueFromIndex[Index];
176 | end;
177 |
178 | function TStyle.GetKey(const Index: Integer): string;
179 | begin
180 | if (Index >= 0) and (Index < FValues.Count) then
181 | Result := FValues.Names[Index]
182 | else
183 | Result := '';
184 | end;
185 |
186 | function TStyle.Dequote(const Value: string): string;
187 | begin
188 | if Value <> '' then
189 | begin
190 | if (Value[1] = '''') and (Value[Length(Value)] = '''') then
191 | Result := Copy(Value, 2, Length(Value) - 2)
192 | else
193 | if (Value[1] = '"') and (Value[Length(Value)] = '"') then
194 | Result := Copy(Value, 2, Length(Value) - 2)
195 | else
196 | Result := Value;
197 | end else
198 | Result := Value;
199 | end;
200 |
201 | procedure TStyle.SetValues(const Values: string);
202 | var
203 | C: Integer;
204 | Key: string;
205 | Value: string;
206 | Help: string;
207 | begin
208 | Help := Trim(Values);
209 |
210 | while Help <> '' do
211 | begin
212 | C := Pos(';', Help);
213 | if C = 0 then
214 | C := Length(Help) + 1;
215 | Key := Copy(Help, 1, C - 1);
216 | Help := Trim(Copy(Help, C + 1, MaxInt));
217 | C := Pos(':', Key);
218 | if C <> 0 then
219 | begin
220 | Value := Trim(Copy(Key, C + 1, MaxInt));
221 | Key := Trim(Copy(Key, 1, C - 1));
222 |
223 | C := IndexOf(Key);
224 | if C = -1 then
225 | FValues.Add(Key + FValues.NameValueSeparator + DeQuote(Value))
226 | else
227 | PutValuesByNum(C, Value);
228 | end;
229 | end;
230 | end;
231 |
232 | function TStyle.AddStyle(const Key, Value: string): Integer;
233 | begin
234 | Result := IndexOf(Key);
235 | if Result = -1 then
236 | Result := FValues.Add(Key + FValues.NameValueSeparator + DeQuote(Value))
237 | else
238 | PutValuesByNum(Result, Value);
239 | DoOnChange;
240 | end;
241 |
242 | function TStyle.IndexOf(const Key: string): Integer;
243 | begin
244 | for Result := 0 to FValues.Count - 1 do
245 | begin
246 | if FValues.Names[Result] = Key then
247 | Exit;
248 | end;
249 | Result := -1;
250 | end;
251 |
252 | procedure TStyle.Delete(Index: Integer);
253 | begin
254 | if (Index >= 0) and (Index < FValues.Count) then
255 | begin
256 | FValues.Delete(Index);
257 | end;
258 | end;
259 |
260 | function TStyle.Remove(const Key: string): Integer;
261 | begin
262 | Result := IndexOf(Key);
263 | Delete(Result);
264 | end;
265 | {$ENDREGION}
266 |
267 | {$REGION 'TStyleList'}
268 | constructor TStyleList.Create;
269 | begin
270 | inherited;
271 | FList := TObjectList.Create(False);
272 | end;
273 |
274 | destructor TStyleList.Destroy;
275 | begin
276 | Clear;
277 | FList.Free;
278 | inherited;
279 | end;
280 |
281 | procedure TStyleList.Clear;
282 | begin
283 | while FList.Count > 0 do
284 | begin
285 | TStyle(FList[0]).Free;
286 | FList.Delete(0);
287 | end;
288 | end;
289 |
290 | function TStyleList.Clone: TStyleList;
291 | var
292 | C: Integer;
293 | begin
294 | Result := TStyleList.Create;
295 | for C := 0 to FList.Count - 1 do
296 | Result.Add(GetStyle(C).Clone);
297 | end;
298 |
299 | function TStyleList.GetCount: Integer;
300 | begin
301 | Result := FList.Count;
302 | end;
303 |
304 | function TStyleList.GetStyle(const Index: Integer): TStyle;
305 | begin
306 | if (Index >= 0) and (Index < FList.Count) then
307 | Result := TStyle(FList[Index])
308 | else
309 | Result := nil;
310 | end;
311 |
312 | procedure TStyleList.PutStyle(const Index: Integer; Style: TStyle);
313 | begin
314 | if (Index >= 0) and (Index < FList.Count) then
315 | begin
316 | FList[Index].Free;
317 | FList[Index] := Style;
318 | end;
319 | end;
320 |
321 | procedure TStyleList.Delete(Index: Integer);
322 | begin
323 | if (Index >= 0) and (Index < FList.Count) then
324 | begin
325 | FList[Index].Free;
326 | FList.Delete(Index);
327 | end;
328 | end;
329 |
330 | function TStyleList.Remove(const Style: TStyle): Integer;
331 | begin
332 | Result := IndexOf(Style);
333 | Delete(Result);
334 | end;
335 |
336 | function TStyleList.Add(const AStyle: TStyle): Integer;
337 | begin
338 | Result := FList.Add(AStyle);
339 | end;
340 |
341 | function TStyleList.Add(const Name, Values: string): Integer;
342 | var
343 | S: TStyle;
344 | begin
345 | S := TStyle.Create;
346 | S.FName := Name;
347 | S.SetValues(Values);
348 | Result := Add(S);
349 | end;
350 |
351 | function TStyleList.Add(const AStyle: string): Integer;
352 | var
353 | Name: string;
354 | StyleStr: string;
355 | Values: string;
356 | C: Integer;
357 | D: Integer;
358 | begin
359 | Result := -1;
360 | StyleStr := Trim(AStyle);
361 | for C := Low(StyleStr) to High(StyleStr) do
362 | begin
363 | if StyleStr[C] = '{' then
364 | begin
365 | for D := High(StyleStr) downto C + 1 do
366 | begin
367 | if StyleStr[D] = '}' then
368 | begin
369 | Name := Trim(Copy(StyleStr, 1, C - 1));
370 |
371 | Values := Copy(StyleStr, C + 1, D - C - 1);
372 | Result := Add(Name, Values);
373 | end;
374 | end;
375 | end;
376 | end;
377 | end;
378 |
379 | procedure TStyleList.Insert(Index: Integer; Style: TStyle);
380 | begin
381 | if (Index >= 0) and (Index < FList.Count) then
382 | FList.Insert(Index, Style);
383 | end;
384 |
385 | procedure TStyleList.Insert(Index: Integer; const Name, Values: string);
386 | var
387 | S: TStyle;
388 | begin
389 | if (Index >= 0) and (Index < FList.Count) then
390 | begin
391 | S := TStyle.Create;
392 | S.FName := Name;
393 | S.SetValues(Values);
394 | Insert(Index, S);
395 | end;
396 | end;
397 |
398 | procedure TStyleList.Exchange(Index1, Index2: Integer);
399 | begin
400 | if (Index1 >= 0) and (Index1 < FList.Count) and
401 | (Index2 >= 0) and (Index2 < FList.Count) then
402 | FList.Exchange(Index1, Index2);
403 | end;
404 |
405 | procedure TStyleList.Move(CurIndex, NewIndex: Integer);
406 | begin
407 | if (CurIndex >= 0) and (CurIndex < FList.Count) and
408 | (NewIndex >= 0) and (NewIndex < FList.Count) then
409 | FList.Move(CurIndex, NewIndex);
410 | end;
411 |
412 | function TStyleList.IndexOf(Style: TStyle): Integer;
413 | begin
414 | Result := FList.IndexOf(Style);
415 | end;
416 |
417 | function TStyleList.GetStyleByName(const Name: string): TStyle;
418 | var
419 | C: Integer;
420 | begin
421 | for C := 0 to FList.Count - 1 do
422 | begin
423 | Result := TStyle(FList[C]);
424 | if Result.FName = Name then
425 | Exit;
426 | end;
427 |
428 | Result := nil;
429 | end;
430 | {$ENDREGION}
431 |
432 | end.
433 |
--------------------------------------------------------------------------------
/svg/SVGTypes.pas:
--------------------------------------------------------------------------------
1 | {******************************************************************}
2 | { SVG types }
3 | { }
4 | { home page : http://www.mwcs.de }
5 | { email : martin.walter@mwcs.de }
6 | { }
7 | { date : 05-04-2008 }
8 | { }
9 | { Use of this file is permitted for commercial and non-commercial }
10 | { use, as long as the author is credited. }
11 | { This file (c) 2005, 2008 Martin Walter }
12 | { }
13 | { This Software is distributed on an "AS IS" basis, WITHOUT }
14 | { WARRANTY OF ANY KIND, either express or implied. }
15 | { }
16 | { *****************************************************************}
17 |
18 | unit SVGTypes;
19 |
20 | interface
21 |
22 | uses
23 | System.Math, System.Types,
24 | Winapi.Windows, Winapi.GDIPAPI;
25 |
26 | const
27 | INHERIT = -1;
28 |
29 | FontNormal = 0;
30 | FontItalic = 1;
31 |
32 | MaxTFloat = MaxSingle;
33 |
34 | type
35 | TFloat = single;
36 |
37 | TListOfPoints = array of TPointF;
38 |
39 | TRectarray = packed array of TRect;
40 | PRectArray = ^TRectArray;
41 |
42 | TTextDecoration = set of (tdInherit, tdUnderLine, tdOverLine, tdStrikeOut);
43 |
44 | TTextPathMethod = (tpmAlign, tpmStretch);
45 |
46 | TTextPathSpacing = (tpsAuto, tpsExact);
47 |
48 | TSVGUnit = (suNone, suPX, suPT, suPC, suMM, suCM, suIN, suEM, suEX, suPercent);
49 |
50 | TGradientUnits = (guObjectBoundingBox, guUserSpaceOnUse);
51 |
52 | TBounds = record
53 | TopLeft: TPointF;
54 | TopRight: TPointF;
55 | BottomLeft: TPointF;
56 | BottomRight: TPointF;
57 | end;
58 |
59 | function ToGPPoint(const Point: TPointF): TGPPointF;
60 |
61 | function Intersect(const Bounds: TBounds; const Rect: TRect): Boolean;
62 |
63 | implementation
64 |
65 | function ToGPPoint(const Point: TPointF): TGPPointF;
66 | begin
67 | Result := MakePoint(Point.X, Point.Y);
68 | end;
69 |
70 | function Intersect(const Bounds: TBounds; const Rect: TRect): Boolean;
71 | var
72 | R1, R2: THandle;
73 | P: array[0..3] of TPoint;
74 | begin
75 | P[0].X := Round(Bounds.TopLeft.X);
76 | P[0].Y := Round(Bounds.TopLeft.Y);
77 |
78 | P[1].X := Round(Bounds.TopRight.X);
79 | P[1].Y := Round(Bounds.TopRight.Y);
80 |
81 | P[2].X := Round(Bounds.BottomRight.X);
82 | P[2].Y := Round(Bounds.BottomRight.Y);
83 |
84 | P[3].X := Round(Bounds.BottomLeft.X);
85 | P[3].Y := Round(Bounds.BottomLeft.Y);
86 |
87 | R1 := CreatePolygonRgn(P, 4, ALTERNATE);
88 | R2 := CreateRectRgn(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
89 |
90 | Result := CombineRgn(R1, R1, R2, RGN_AND) <> NULLREGION;
91 |
92 | DeleteObject(R1);
93 | DeleteObject(R2);
94 | end;
95 |
96 | end.
97 |
--------------------------------------------------------------------------------
/vcl/SvgViewer.dpr:
--------------------------------------------------------------------------------
1 | program SvgViewer;
2 |
3 | uses
4 | Vcl.Forms,
5 | SvgViewerUnit in 'SvgViewerUnit.pas' {Form1};
6 |
7 | {$R *.res}
8 |
9 | begin
10 | Application.Initialize;
11 | Application.MainFormOnTaskbar := True;
12 | Application.CreateForm(TForm1, Form1);
13 | Application.Run;
14 | end.
15 |
--------------------------------------------------------------------------------
/vcl/SvgViewer.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ekot1/DelphiSVG/0c2fc478d221a914c8ca1a15f0e65e282396000a/vcl/SvgViewer.res
--------------------------------------------------------------------------------
/vcl/SvgViewerUnit.dfm:
--------------------------------------------------------------------------------
1 | object Form1: TForm1
2 | Left = 0
3 | Top = 0
4 | Caption = 'Form1'
5 | ClientHeight = 629
6 | ClientWidth = 805
7 | Color = clBtnFace
8 | DoubleBuffered = True
9 | Font.Charset = DEFAULT_CHARSET
10 | Font.Color = clWindowText
11 | Font.Height = -11
12 | Font.Name = 'Tahoma'
13 | Font.Style = []
14 | OldCreateOrder = False
15 | OnCreate = FormCreate
16 | OnDestroy = FormDestroy
17 | PixelsPerInch = 96
18 | TextHeight = 13
19 | object PaintBox1: TPaintBox
20 | Left = 0
21 | Top = 0
22 | Width = 707
23 | Height = 629
24 | Align = alClient
25 | OnPaint = PaintBox1Paint
26 | ExplicitLeft = -6
27 | ExplicitTop = -1
28 | end
29 | object Panel1: TPanel
30 | Left = 707
31 | Top = 0
32 | Width = 98
33 | Height = 629
34 | Align = alRight
35 | TabOrder = 0
36 | object ListBox1: TListBox
37 | Left = 1
38 | Top = 42
39 | Width = 96
40 | Height = 586
41 | Align = alClient
42 | ItemHeight = 13
43 | TabOrder = 0
44 | OnClick = ListBox1Click
45 | end
46 | object Panel2: TPanel
47 | Left = 1
48 | Top = 1
49 | Width = 96
50 | Height = 41
51 | Align = alTop
52 | BevelOuter = bvNone
53 | Caption = 'Panel2'
54 | TabOrder = 1
55 | object Button1: TButton
56 | Left = 13
57 | Top = 9
58 | Width = 75
59 | Height = 25
60 | Caption = 'Open...'
61 | TabOrder = 0
62 | OnClick = Button1Click
63 | end
64 | end
65 | end
66 | object OpenDialog1: TOpenDialog
67 | Left = 456
68 | Top = 120
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/vcl/SvgViewerUnit.pas:
--------------------------------------------------------------------------------
1 | unit SvgViewerUnit;
2 |
3 | interface
4 |
5 | uses
6 | Winapi.Windows, Winapi.Messages,
7 | System.SysUtils, System.Variants, System.Classes,
8 | Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.ComCtrls,
9 | SVG;
10 |
11 | type
12 | TForm1 = class(TForm)
13 | PaintBox1: TPaintBox;
14 | OpenDialog1: TOpenDialog;
15 | Panel1: TPanel;
16 | ListBox1: TListBox;
17 | Panel2: TPanel;
18 | Button1: TButton;
19 | procedure PaintBox1Paint(Sender: TObject);
20 | procedure FormCreate(Sender: TObject);
21 | procedure FormDestroy(Sender: TObject);
22 | procedure Button1Click(Sender: TObject);
23 | procedure ListBox1Click(Sender: TObject);
24 | private
25 | { Private declarations }
26 | FSVG: TSVG;
27 | public
28 | { Public declarations }
29 | end;
30 |
31 | var
32 | Form1: TForm1;
33 |
34 | implementation
35 |
36 | {$R *.dfm}
37 |
38 | uses
39 | Winapi.GDIPAPI, System.IOUtils, System.Types,
40 | SVGImage;
41 |
42 | const
43 | CPath = '..\..\..\examples';
44 |
45 | procedure TForm1.Button1Click(Sender: TObject);
46 | begin
47 | if OpenDialog1.Execute(Handle) then
48 | begin
49 | FSVG.LoadFromFile(OpenDialog1.FileName);
50 | end;
51 | PaintBox1.Invalidate;
52 | end;
53 |
54 | procedure TForm1.FormCreate(Sender: TObject);
55 | var
56 | Files: TStringDynArray;
57 | F: TArray;
58 | FileName: string;
59 | begin
60 | FSVG := TSVG.Create;
61 | Files := TDirectory.GetFiles(CPath, '*.svg');
62 | for FileName in Files do
63 | begin
64 | F := F + [ExtractFileName(FileName)];
65 | end;
66 |
67 | ListBox1.Items.AddStrings(F);
68 | end;
69 |
70 | procedure TForm1.FormDestroy(Sender: TObject);
71 | begin
72 | FSVG.Free;
73 | end;
74 |
75 | procedure TForm1.ListBox1Click(Sender: TObject);
76 | begin
77 | FSVG.LoadFromFile(TPath.Combine(CPath, ListBox1.Items[ListBox1.ItemIndex]));
78 | PaintBox1.Invalidate;
79 | end;
80 |
81 | procedure TForm1.PaintBox1Paint(Sender: TObject);
82 | begin
83 | if FSVG.Count > 0 then
84 | begin
85 | FSVG.PaintTo(PaintBox1.Canvas.Handle,
86 | MakeRect(0.0, 0.0, FSVG.Width, FSVG.Height), nil, 0);
87 | end;
88 | end;
89 |
90 | end.
91 |
--------------------------------------------------------------------------------
/vcl/svgimage/SVGImage.pas:
--------------------------------------------------------------------------------
1 | {******************************************************************}
2 | { SVG Image in TPicture }
3 | { }
4 | { home page: http://www.mwcs.de }
5 | { email : martin.walter@mwcs.de }
6 | { }
7 | { date : 05-04-2008 }
8 | { }
9 | { Use of this file is permitted for commercial and non-commercial }
10 | { use, as long as the author is credited. }
11 | { This file (c) 2005, 2008 Martin Walter }
12 | { }
13 | { Thanks to: }
14 | { Elias Zurschmiede (imagelist error) }
15 | { }
16 | { This Software is distributed on an "AS IS" basis, WITHOUT }
17 | { WARRANTY OF ANY KIND, either express or implied. }
18 | { }
19 | { *****************************************************************}
20 |
21 | unit SVGImage;
22 |
23 | interface
24 |
25 | uses
26 | Winapi.Windows, Winapi.GDIPOBJ,
27 | System.SysUtils, System.Classes, Vcl.Controls, Vcl.Graphics,
28 | SVG, SVGImageList;
29 |
30 | type
31 | TSVGImage = class(TGraphicControl)
32 | strict private
33 | FSVGImage: TSVG;
34 | FStream: TMemoryStream;
35 |
36 | FCenter: Boolean;
37 | FProportional: Boolean;
38 | FStretch: Boolean;
39 | FAutoSize: Boolean;
40 | FScale: Double;
41 |
42 | FOpacity: Byte;
43 | FFileName: TFileName;
44 | FImageList: TSVGImageList;
45 | FImageIndex: Integer;
46 |
47 | procedure SetCenter(Value: Boolean);
48 | procedure SetProportional(Value: Boolean);
49 | procedure SetOpacity(Value: Byte);
50 | procedure SetFileName(const Value: TFileName);
51 | procedure ReadData(Stream: TStream);
52 | procedure WriteData(Stream: TStream);
53 | procedure SetImageIndex(const Value: Integer);
54 | procedure SetStretch(const Value: Boolean);
55 | procedure SetScale(const Value: Double);
56 | procedure SetAutoSizeImage(const Value: Boolean);
57 | protected
58 | procedure DefineProperties(Filer: TFiler); override;
59 | procedure Notification(AComponent: TComponent; Operation: TOperation); override;
60 | procedure CheckAutoSize;
61 | public
62 | constructor Create(AOwner: TComponent); override;
63 | destructor Destroy; override;
64 | procedure Clear;
65 | function Empty: Boolean;
66 | procedure Paint; override;
67 | procedure LoadFromFile(const FileName: string);
68 | procedure LoadFromStream(Stream: TStream);
69 | procedure Assign(Source: TPersistent); override;
70 | property SVG: TSVG read FSVGImage;
71 | published
72 | property AutoSize: Boolean read FAutoSize write SetAutoSizeImage;
73 | property Center: Boolean read FCenter write SetCenter;
74 | property Proportional: Boolean read FProportional write SetProportional;
75 | property Stretch: Boolean read FStretch write SetStretch;
76 | property Opacity: Byte read FOpacity write SetOpacity;
77 | property Scale: Double read FScale write SetScale;
78 | property FileName: TFileName read FFileName write SetFileName;
79 | property ImageList: TSVGImageList read FImageList write FImageList;
80 | property ImageIndex: Integer read FImageIndex write SetImageIndex;
81 | property Enabled;
82 | property Visible;
83 | property Constraints;
84 | property Anchors;
85 | property Align;
86 |
87 | property OnClick;
88 | property OnDblClick;
89 | property OnMouseDown;
90 | property OnMouseMove;
91 | property OnMouseUp;
92 | end;
93 |
94 |
95 | TSVGGraphic = class(TGraphic)
96 | strict private
97 | FSVGImage: TSVG;
98 | FStream: TMemoryStream;
99 |
100 | FOpacity: Byte;
101 | FFileName: TFileName;
102 |
103 | procedure SetOpacity(Value: Byte);
104 | procedure SetFileName(const Value: TFileName);
105 | protected
106 | procedure DefineProperties(Filer: TFiler); override;
107 |
108 | procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
109 |
110 | function GetEmpty: Boolean; override;
111 | function GetWidth: Integer; override;
112 | function GetHeight: Integer; override;
113 | procedure SetHeight(Value: Integer); override;
114 | procedure SetWidth(Value: Integer); override;
115 |
116 | procedure ReadData(Stream: TStream); override;
117 | procedure WriteData(Stream: TStream); override;
118 | public
119 | constructor Create; override;
120 | destructor Destroy; override;
121 | procedure Clear;
122 |
123 | procedure Assign(Source: TPersistent); override;
124 | procedure AssignTo(Dest: TPersistent); override;
125 |
126 | procedure AssignSVG(SVG: TSVG);
127 |
128 | procedure LoadFromFile(const Filename: String); override;
129 | procedure LoadFromStream(Stream: TStream); override;
130 |
131 | procedure SaveToStream(Stream: TStream); override;
132 |
133 | procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
134 | APalette: HPALETTE); override;
135 | procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
136 | var APalette: HPALETTE); override;
137 |
138 | property Opacity: Byte read FOpacity write SetOpacity;
139 | property FileName: TFileName read FFileName write SetFileName;
140 | end;
141 |
142 | function TGPImageToBitmap(Image: TGPImage): TBitmap;
143 |
144 | implementation
145 |
146 | uses
147 | Vcl.Dialogs,
148 | Winapi.GDIPAPI;
149 |
150 | function TGPImageToBitmap(Image: TGPImage): TBitmap;
151 | var
152 | Graphics: TGPGraphics;
153 | Bitmap: TBitmap;
154 | P: Pointer;
155 | W, H: Cardinal;
156 | begin
157 | Bitmap := nil;
158 | if Assigned(Image) then
159 | begin
160 | W := Image.GetWidth;
161 | H := Image.GetHeight;
162 | if (W > 0) and (H > 0) then
163 | begin
164 | Bitmap := TBitmap.Create;
165 | Bitmap.PixelFormat := pf32Bit;
166 | Bitmap.Width := W;
167 | Bitmap.Height := H;
168 | P := Bitmap.ScanLine[H - 1];
169 | FillChar(P^, (W * H) shl 2, 0);
170 | Graphics := TGPGraphics.Create(Bitmap.Canvas.Handle);
171 | try
172 | Graphics.DrawImage(Image, 0, 0);
173 | finally
174 | Graphics.Free;
175 | end;
176 | end;
177 | end;
178 | Result := Bitmap;
179 | end;
180 |
181 | constructor TSVGImage.Create(AOwner: TComponent);
182 | begin
183 | inherited;
184 | FSVGImage := TSVG.Create;
185 | FProportional := False;
186 | FCenter := True;
187 | FStretch := True;
188 | FOpacity := 255;
189 | FScale := 1;
190 | FImageIndex := -1;
191 | FStream := TMemoryStream.Create;
192 | end;
193 |
194 | destructor TSVGImage.Destroy;
195 | begin
196 | FSVGImage.Free;
197 | FStream.Free;
198 | inherited;
199 | end;
200 |
201 | procedure TSVGImage.CheckAutoSize;
202 | begin
203 | if FAutoSize and (FSVGImage.Width > 0) and (FSVGImage.Height > 0) then
204 | begin
205 | SetBounds(Left, Top, Round(FSVGImage.Width), Round(FSVGImage.Height));
206 | end;
207 | end;
208 |
209 | procedure TSVGImage.Clear;
210 | begin
211 | FSVGImage.Clear;
212 | FFileName := '';
213 | Repaint;
214 | end;
215 |
216 | function TSVGImage.Empty: Boolean;
217 | begin
218 | Empty := FSVGImage.Count = 0;
219 | end;
220 |
221 | procedure TSVGImage.DefineProperties(Filer: TFiler);
222 | begin
223 | Filer.DefineBinaryProperty('Data', ReadData, WriteData, True);
224 | end;
225 |
226 | procedure TSVGImage.Paint;
227 | var
228 | Bounds: TGPRectF;
229 |
230 | procedure CalcWidth(const ImageWidth, ImageHeight: Double);
231 | var
232 | R: Double;
233 | begin
234 | Bounds.Width := ImageWidth * FScale;
235 | Bounds.Height := ImageHeight * FScale;
236 |
237 | if FProportional then
238 | begin
239 | if ImageHeight > 0 then
240 | R := ImageWidth / ImageHeight
241 | else
242 | R := 1;
243 |
244 | if Width / Height > R then
245 | begin
246 | Bounds.Width := Height * R;
247 | Bounds.Height := Height;
248 | end else
249 | begin
250 | Bounds.Width := Width;
251 | Bounds.Height := Width / R;
252 | end;
253 | Exit;
254 | end;
255 |
256 | if FStretch then
257 | begin
258 | Bounds := MakeRect(0.0, 0, Width, Height);
259 | Exit;
260 | end;
261 | end;
262 |
263 | procedure CalcOffset;
264 | begin
265 | Bounds.X := 0;
266 | Bounds.Y := 0;
267 | if FCenter then
268 | begin
269 | Bounds.X := (Width - Bounds.Width) / 2;
270 | Bounds.Y := (Height - Bounds.Height) / 2;
271 | end;
272 | end;
273 |
274 | var
275 | SVG: TSVG;
276 | begin
277 | if Assigned(FImageList) and (FImageIndex >= 0) and
278 | (FImageIndex < FImagelist.Count) then
279 | SVG := FImageList.Images[FImageIndex]
280 | else
281 | SVG := FSVGImage;
282 |
283 | if SVG.Count > 0 then
284 | begin
285 | CalcWidth(SVG.Width, SVG.Height);
286 | CalcOffset;
287 |
288 | SVG.SVGOpacity := FOpacity / 255;
289 | SVG.PaintTo(Canvas.Handle, Bounds, nil, 0);
290 | SVG.SVGOpacity := 1;
291 | end;
292 |
293 | if csDesigning in ComponentState then
294 | begin
295 | Canvas.Brush.Style := bsClear;
296 | Canvas.Pen.Style := psDash;
297 | Canvas.Pen.Color := clBlack;
298 | Canvas.Rectangle(0, 0, Width, Height);
299 | end;
300 | end;
301 |
302 | procedure TSVGImage.LoadFromFile(const FileName: string);
303 | begin
304 | if csLoading in ComponentState then
305 | Exit;
306 | try
307 | FStream.Clear;
308 | FStream.LoadFromFile(FileName);
309 | FSVGImage.LoadFromStream(FStream);
310 | FFileName := FileName;
311 | except
312 | Clear;
313 | end;
314 | CheckAutoSize;
315 | Repaint;
316 | end;
317 |
318 | procedure TSVGImage.LoadFromStream(Stream: TStream);
319 | begin
320 | try
321 | FFileName := '';
322 | FStream.Clear;
323 | FStream.LoadFromStream(Stream);
324 | FSVGImage.LoadFromStream(FStream);
325 | except
326 | end;
327 | CheckAutoSize;
328 | Repaint;
329 | end;
330 |
331 | procedure TSVGImage.Notification(AComponent: TComponent; Operation: TOperation);
332 | begin
333 | inherited;
334 | if (Operation = opRemove) and (AComponent = FImageList) then
335 | FImageList := nil;
336 | end;
337 |
338 | procedure TSVGImage.Assign(Source: TPersistent);
339 | var
340 | SVG: TSVG;
341 | begin
342 | if (Source is TSVGImage) then
343 | begin
344 | SVG := (Source as TSVGImage).FSVGImage;
345 | FSVGImage.LoadFromText(SVG.Source);
346 | FImageIndex := -1;
347 | CheckAutoSize;
348 | end;
349 |
350 | if (Source.ClassType = TSVG) then
351 | begin
352 | SVG := TSVG(Source);
353 | FSVGImage.LoadFromText(SVG.Source);
354 | FImageIndex := -1;
355 | end;
356 |
357 | Repaint;
358 | end;
359 |
360 | procedure TSVGImage.SetAutoSizeImage(const Value: Boolean);
361 | begin
362 | if (Value = FAutoSize) then
363 | Exit;
364 | FAutoSize := Value;
365 |
366 | CheckAutoSize;
367 | end;
368 |
369 | procedure TSVGImage.SetCenter(Value: Boolean);
370 | begin
371 | if Value = FCenter then
372 | Exit;
373 |
374 | FCenter := Value;
375 | Repaint;
376 | end;
377 |
378 | procedure TSVGImage.SetProportional(Value: Boolean);
379 | begin
380 | if Value = FProportional then
381 | Exit;
382 |
383 | FProportional := Value;
384 | Repaint;
385 | end;
386 |
387 | procedure TSVGImage.SetScale(const Value: Double);
388 | begin
389 | if Value = FScale then
390 | Exit;
391 | FScale := Value;
392 | FAutoSize := False;
393 | Repaint;
394 | end;
395 |
396 | procedure TSVGImage.SetStretch(const Value: Boolean);
397 | begin
398 | if Value = FStretch then
399 | Exit;
400 |
401 | FStretch := Value;
402 | if FStretch then
403 | FAutoSize := False;
404 | Repaint;
405 | end;
406 |
407 | procedure TSVGImage.SetOpacity(Value: Byte);
408 | begin
409 | if Value = FOpacity then
410 | Exit;
411 |
412 | FOpacity := Value;
413 | Repaint;
414 | end;
415 |
416 | procedure TSVGImage.SetFileName(const Value: TFileName);
417 | begin
418 | if Value = FFileName then
419 | Exit;
420 |
421 | LoadFromFile(Value);
422 | end;
423 |
424 | procedure TSVGImage.ReadData(Stream: TStream);
425 | var
426 | Size: LongInt;
427 | begin
428 | Stream.Read(Size, SizeOf(Size));
429 | FStream.Clear;
430 | if Size > 0 then
431 | begin
432 | FStream.CopyFrom(Stream, Size);
433 | FSVGImage.LoadFromStream(FStream);
434 | end else
435 | FSVGImage.Clear;
436 | end;
437 |
438 | procedure TSVGImage.WriteData(Stream: TStream);
439 | var
440 | Size: LongInt;
441 | begin
442 | Size := FStream.Size;
443 | Stream.Write(Size, SizeOf(Size));
444 | FStream.Position := 0;
445 | if FStream.Size > 0 then
446 | FStream.SaveToStream(Stream);
447 | end;
448 |
449 |
450 | constructor TSVGGraphic.Create;
451 | begin
452 | inherited;
453 | FSVGImage := TSVG.Create;
454 | FOpacity := 255;
455 | FStream := TMemoryStream.Create;
456 | end;
457 |
458 | destructor TSVGGraphic.Destroy;
459 | begin
460 | FSVGImage.Free;
461 | FStream.Free;
462 | inherited;
463 | end;
464 |
465 | procedure TSVGGraphic.Clear;
466 | begin
467 | FSVGImage.Clear;
468 | FFileName := '';
469 | Changed(Self);
470 | end;
471 |
472 | procedure TSVGGraphic.Assign(Source: TPersistent);
473 | begin
474 | if (Source is TSVGGraphic) then
475 | begin
476 | try
477 | FSVGImage.Free;
478 | FSVGImage := TSVG(TSVGGraphic(Source).FSVGImage.Clone(nil));
479 | FStream.Clear;
480 | FStream.LoadFromStream(TSVGGraphic(Source).FStream);
481 | except
482 | end;
483 | Changed(Self);
484 | end;
485 | end;
486 |
487 | procedure TSVGGraphic.AssignSVG(SVG: TSVG);
488 | begin
489 | FSVGImage.LoadFromText(SVG.Source);
490 | Changed(Self);
491 | end;
492 |
493 | procedure TSVGGraphic.AssignTo(Dest: TPersistent);
494 | begin
495 | if Dest is TSVGGraphic then
496 | TSVGGraphic(Dest).Assign(Self);
497 | end;
498 |
499 | procedure TSVGGraphic.SetOpacity(Value: Byte);
500 | begin
501 | if Value = FOpacity then
502 | Exit;
503 |
504 | FOpacity := Value;
505 | Changed(Self);
506 | end;
507 |
508 | procedure TSVGGraphic.SetWidth(Value: Integer);
509 | begin
510 | inherited;
511 |
512 | end;
513 |
514 | procedure TSVGGraphic.SetFileName(const Value: TFileName);
515 | begin
516 | if Value = FFileName then
517 | Exit;
518 |
519 | LoadFromFile(Value);
520 | end;
521 |
522 | procedure TSVGGraphic.SetHeight(Value: Integer);
523 | begin
524 | inherited;
525 |
526 | end;
527 |
528 | procedure TSVGGraphic.ReadData(Stream: TStream);
529 | var
530 | Size: LongInt;
531 | begin
532 | Stream.Read(Size, SizeOf(Size));
533 | FStream.Clear;
534 | FStream.CopyFrom(Stream, Size);
535 | FSVGImage.LoadFromStream(FStream);
536 | end;
537 |
538 | procedure TSVGGraphic.WriteData(Stream: TStream);
539 | var
540 | Size: LongInt;
541 | begin
542 | Size := FStream.Size;
543 | Stream.Write(Size, SizeOf(Size));
544 | FStream.Position := 0;
545 | FStream.SaveToStream(Stream);
546 | end;
547 |
548 | procedure TSVGGraphic.DefineProperties(Filer: TFiler);
549 | begin
550 | Filer.DefineBinaryProperty('Data', ReadData, WriteData, True);
551 | end;
552 |
553 | procedure TSVGGraphic.Draw(ACanvas: TCanvas; const Rect: TRect);
554 | var
555 | Bounds: TGPRectF;
556 | begin
557 | if Empty then
558 | Exit;
559 |
560 | Bounds := MakeRect(Rect.Left + 0.0, Rect.Top,
561 | Rect.Right - Rect.Left, Rect.Bottom - Rect.Top);
562 |
563 | FSVGImage.SVGOpacity := FOpacity / 255;
564 | FSVGImage.PaintTo(ACanvas.Handle, Bounds, nil, 0);
565 | end;
566 |
567 |
568 | function TSVGGraphic.GetEmpty: Boolean;
569 | begin
570 | Result := FSVGImage.Count = 0;
571 | end;
572 |
573 | function TSVGGraphic.GetWidth: Integer;
574 | begin
575 | Result := Round(FSVGImage.Width);
576 | end;
577 |
578 | function TSVGGraphic.GetHeight: Integer;
579 | begin
580 | Result := Round(FSVGImage.Height);
581 | end;
582 |
583 | procedure TSVGGraphic.LoadFromClipboardFormat(AFormat: Word; AData: THandle;
584 | APalette: HPALETTE);
585 | begin
586 | inherited;
587 |
588 | end;
589 |
590 | procedure TSVGGraphic.LoadFromFile(const Filename: String);
591 | begin
592 | FStream.Clear;
593 | FStream.LoadFromFile(FileName);
594 | FSVGImage.LoadFromStream(FStream);
595 | Changed(Self);
596 | end;
597 |
598 | procedure TSVGGraphic.LoadFromStream(Stream: TStream);
599 | begin
600 | try
601 | FFileName := '';
602 | FStream.LoadFromStream(Stream);
603 | FSVGImage.LoadFromStream(FStream);
604 | except
605 | end;
606 | Changed(Self);
607 | end;
608 |
609 | procedure TSVGGraphic.SaveToClipboardFormat(var AFormat: Word;
610 | var AData: THandle; var APalette: HPALETTE);
611 | begin
612 | inherited;
613 |
614 | end;
615 |
616 | procedure TSVGGraphic.SaveToStream(Stream: TStream);
617 | begin
618 | FStream.Position := 0;
619 | FStream.SaveToStream(Stream);
620 | end;
621 |
622 |
623 | procedure TSVGImage.SetImageIndex(const Value: Integer);
624 | begin
625 | if FImageIndex = Value then
626 | Exit;
627 | FImageIndex := Value;
628 | CheckAutoSize;
629 | Repaint;
630 | end;
631 |
632 | initialization
633 | TPicture.RegisterFileFormat('SVG', 'Scalable Vector Graphics', TSVGGraphic);
634 |
635 | finalization
636 | TPicture.UnregisterGraphicClass(TSVGGraphic);
637 | end.
638 |
--------------------------------------------------------------------------------
/vcl/svgimage/SVGImageList.pas:
--------------------------------------------------------------------------------
1 | unit SVGImageList;
2 |
3 | interface
4 |
5 | uses
6 | Winapi.Windows, System.SysUtils, System.Classes,
7 | Vcl.Controls, Vcl.Graphics, Vcl.ImgList,
8 | SVG;
9 |
10 | type
11 | TSVGCollectionItem = class(TCollectionItem)
12 | strict private
13 | FName: string;
14 | FSVG: TSVG;
15 | procedure SetName(const Value: string);
16 | procedure SetSVG(const Value: TSVG);
17 | protected
18 | procedure AssignTo(Dest: TPersistent); override;
19 | public
20 | constructor Create(Collection: TCollection); override;
21 | destructor Destroy; override;
22 | property SVG: TSVG read FSVG write SetSVG;
23 | property Name: string read FName write SetName;
24 | end;
25 |
26 | TSVGCollectionItems = class(TCollection)
27 | strict private
28 | FOwner: TPersistent;
29 | function GetItem(Index: Integer): TSVGCollectionItem;
30 | procedure SetItem(Index: Integer; const Value: TSVGCollectionItem);
31 | protected
32 | procedure Update(Item: TCollectionItem); override;
33 | procedure Notify(Item: TCollectionItem; Action: TCollectionNotification); override;
34 | function GetOwner: TPersistent; override;
35 | public
36 | constructor Create(AOwner: TPersistent);
37 | function Add: TSVGCollectionItem;
38 | procedure Assign(Source: TPersistent); override;
39 | property Items[index: Integer]: TSVGCollectionItem read GetItem write SetItem; default;
40 | end;
41 |
42 | TSVGImageList = class(TCustomImageList)
43 | strict private
44 | FImages: TSVGCollectionItems;
45 | FOpacity: Byte;
46 | FUpdating: Boolean;
47 | function GetImages(Index: Integer): TSVG;
48 | function GetNames(Index: Integer): string;
49 | procedure SetImages(Index: Integer; const Value: TSVG);
50 | procedure SetNames(Index: Integer; const Value: string);
51 | function GetHeight: Integer;
52 | function GetWidth: Integer;
53 | procedure SetHeight(const Value: Integer);
54 | procedure SetWidth(const Value: Integer);
55 | procedure SetOpacity(const Value: Byte);
56 | function SVGToIcon(const SVG: TSVG): HICON;
57 | procedure ReadLeft(Reader: TReader);
58 | procedure ReadTop(Reader: TReader);
59 | procedure WriteLeft(Writer: TWriter);
60 | procedure WriteTop(Writer: TWriter);
61 | procedure ReadImageData(Stream: TStream);
62 | procedure WriteImageData(Stream: TStream);
63 | private
64 | procedure RecreateBitmaps;
65 | protected
66 | procedure DefineProperties(Filer: TFiler); override;
67 | procedure AssignTo(Dest: TPersistent); override;
68 | procedure DoDraw(Index: Integer; Canvas: TCanvas; X, Y: Integer;
69 | Style: Cardinal; Enabled: Boolean = True); override;
70 | function GetCount: Integer; override;
71 | public
72 | constructor Create(AOwner: TComponent); override;
73 | destructor Destroy; override;
74 | function Add(const SVG: TSVG; const Name: string): Integer;
75 | procedure Delete(const Index: Integer);
76 | procedure Remove(const Name: string);
77 | function IndexOf(const Name: string): Integer;
78 | procedure Clear;
79 | procedure PaintTo(const DC: HDC; const Index: Integer;
80 | const X, Y, Width, Height: Double); overload;
81 | procedure PaintTo(const DC: HDC; const Name: string;
82 | const X, Y, Width, Height: Double); overload;
83 | property Images[Index: Integer]: TSVG read GetImages write SetImages;
84 | property Names[Index: Integer]: string read GetNames write SetNames;
85 | property Count: Integer read GetCount;
86 | published
87 | property Items: TSVGCollectionItems read FImages;
88 | property Opacity: Byte read FOpacity write SetOpacity;
89 | property Width: Integer read GetWidth write SetWidth default 16;
90 | property Height: Integer read GetHeight write SetHeight default 16;
91 | end;
92 |
93 | implementation
94 |
95 | uses
96 | Winapi.CommCtrl, Winapi.GDIPAPI, Winapi.GDIPOBJ,
97 | Vcl.ComCtrls,
98 | GDIPUtils, SVGTypes;
99 |
100 | { TSVGImageList }
101 |
102 | function TSVGImageList.Add(const SVG: TSVG;
103 | const Name: string): Integer;
104 | var
105 | Item: TSVGCollectionItem;
106 | Updating: Boolean;
107 | begin
108 | Updating := FUpdating;
109 | try
110 | FUpdating := True;
111 | Item := FImages.Add;
112 |
113 | Item.SVG := SVG;
114 | Item.Name := Name;
115 | finally
116 | FUpdating := Updating;
117 | RecreateBitmaps;
118 | end;
119 | Result := FImages.Count - 1;
120 | end;
121 |
122 | procedure TSVGImageList.AssignTo(Dest: TPersistent);
123 | begin
124 | Clear;
125 | inherited;
126 | if Dest is TSVGImageList then
127 | begin
128 | TSVGImageList(Dest).FOpacity := FOpacity;
129 | TSVGImageList(Dest).Width := Width;
130 | TSVGImageList(Dest).Height := Height;
131 | FImages.AssignTo(TSVGImageList(Dest).FImages);
132 | end;
133 | end;
134 |
135 | procedure TSVGImageList.Clear;
136 | begin
137 | inherited Clear;
138 | FUpdating := True;
139 | try
140 | FImages.Clear;
141 | finally
142 | FUpdating := False;
143 | RecreateBitmaps;
144 | end;
145 | end;
146 |
147 | constructor TSVGImageList.Create(AOwner: TComponent);
148 | begin
149 | inherited;
150 | FImages := TSVGCollectionItems.Create(Self);
151 | FOpacity := 255;
152 | end;
153 |
154 | procedure TSVGImageList.DefineProperties(Filer: TFiler);
155 | var
156 | Ancestor: TComponent;
157 | Info: Longint;
158 | begin
159 | Info := 0;
160 | Ancestor := TComponent(Filer.Ancestor);
161 | if Ancestor <> nil then
162 | Info := Ancestor.DesignInfo;
163 | Filer.DefineProperty('Left', ReadLeft, WriteLeft, LongRec(DesignInfo).Lo <> LongRec(Info).Lo);
164 | Filer.DefineProperty('Top', ReadTop, WriteTop, LongRec(DesignInfo).Hi <> LongRec(Info).Hi);
165 | Filer.DefineBinaryProperty('Images', ReadImageData, WriteImageData, True);
166 | end;
167 |
168 | procedure TSVGImageList.Delete(const Index: Integer);
169 | begin
170 | if (Index >= 0) and (Index < FImages.Count) then
171 | FImages.Delete(Index);
172 | end;
173 |
174 | destructor TSVGImageList.Destroy;
175 | begin
176 | Clear;
177 | FImages.Free;
178 | inherited;
179 | end;
180 |
181 | procedure TSVGImageList.DoDraw(Index: Integer; Canvas: TCanvas; X, Y: Integer;
182 | Style: Cardinal; Enabled: Boolean);
183 | begin
184 | PaintTo(Canvas.Handle, Index, X, Y, Width, Height);
185 | end;
186 |
187 | function TSVGImageList.GetCount: Integer;
188 | begin
189 | Result := FImages.Count;
190 | end;
191 |
192 | function TSVGImageList.GetHeight: Integer;
193 | begin
194 | Result := inherited Height;
195 | end;
196 |
197 | function TSVGImageList.GetImages(Index: Integer): TSVG;
198 | begin
199 | if (Index >= 0) and (Index < FImages.Count) then
200 | Result := FImages[Index].SVG
201 | else
202 | Result := nil;
203 | end;
204 |
205 | function TSVGImageList.GetNames(Index: Integer): string;
206 | begin
207 | if (Index >= 0) and (Index < FImages.Count) then
208 | Result := FImages[Index].Name
209 | else
210 | Result := '';
211 | end;
212 |
213 | function TSVGImageList.GetWidth: Integer;
214 | begin
215 | Result := inherited Width;
216 | end;
217 |
218 | function TSVGImageList.IndexOf(const Name: string): Integer;
219 | begin
220 | for Result := 0 to FImages.Count - 1 do
221 | if FImages[Result].Name = Name then
222 | Exit;
223 | Result := -1;
224 | end;
225 |
226 | procedure TSVGImageList.PaintTo(const DC: HDC; const Index: Integer;
227 | const X, Y, Width, Height: Double);
228 | var
229 | R: TGPRectF;
230 | SVG: TSVG;
231 | begin
232 | if (Index >= 0) and (Index < FImages.Count) then
233 | begin
234 | SVG := FImages[Index].SVG;
235 | SVG.SVGOpacity := FOpacity / 255;
236 | R := CalcRect(MakeRect(X, Y, Width, Height), SVG.Width, SVG.Height, baCenterCenter);
237 | SVG.PaintTo(DC, R, nil, 0);
238 | SVG.SVGOpacity := 1;
239 | end;
240 | end;
241 |
242 | procedure TSVGImageList.PaintTo(const DC: HDC; const Name: string;
243 | const X, Y, Width, Height: Double);
244 | var
245 | Index: Integer;
246 | begin
247 | Index := IndexOf(Name);
248 | PaintTo(DC, Index, X, Y, Width, Height);
249 | end;
250 |
251 |
252 | procedure TSVGImageList.ReadImageData(Stream: TStream);
253 | var
254 | FStream: TMemoryStream;
255 | Count, Size: Integer;
256 | SVG: TSVG;
257 | Name: string;
258 | C: Integer;
259 | begin
260 | try
261 | FUpdating := True;
262 | FStream := TMemoryStream.Create;
263 | Stream.Read(Count, SizeOf(Integer));
264 | SVG := TSVG.Create(nil);
265 | for C := 0 to Count - 1 do
266 | begin
267 | Stream.Read(Size, SizeOf(Integer));
268 | SetLength(Name, Size);
269 | Stream.Read(PChar(Name)^, Size * SizeOf(Char));
270 |
271 | Stream.Read(Size, SizeOf(Integer));
272 | FStream.CopyFrom(Stream, Size);
273 | SVG.LoadFromStream(FStream);
274 | FStream.Clear;
275 | Add(SVG, Name);
276 | end;
277 | FStream.Free;
278 | SVG.Free;
279 | finally
280 | FUpdating := False;
281 | RecreateBitmaps;
282 | end;
283 | end;
284 |
285 | procedure TSVGImageList.ReadLeft(Reader: TReader);
286 | var
287 | FDesignInfo: LongInt;
288 | begin
289 | FDesignInfo := DesignInfo;
290 | LongRec(FDesignInfo).Lo := Reader.ReadInteger;
291 | DesignInfo := FDesignInfo;
292 | end;
293 |
294 | procedure TSVGImageList.ReadTop(Reader: TReader);
295 | var
296 | FDesignInfo: LongInt;
297 | begin
298 | FDesignInfo := DesignInfo;
299 | LongRec(FDesignInfo).Hi := Reader.ReadInteger;
300 | DesignInfo := FDesignInfo;
301 | end;
302 |
303 | procedure TSVGImageList.RecreateBitmaps;
304 | var
305 | C: Integer;
306 | SVG: TSVG;
307 | Icon: HIcon;
308 | begin
309 | if not FUpdating then
310 | begin
311 | ImageList_Remove(Handle, -1);
312 | Handle := ImageList_Create(Width, Height,
313 | ILC_COLOR32 or (Integer(Masked) * ILC_MASK), 0, AllocBy);
314 |
315 | for C := 0 to FImages.Count - 1 do
316 | begin
317 | SVG := FImages[C].SVG;
318 | if Assigned(SVG) then
319 | begin
320 | Icon := SVGToIcon(SVG);
321 | ImageList_AddIcon(Handle, Icon);
322 | DestroyIcon(Icon);
323 | end;
324 | end;
325 | end;
326 | end;
327 |
328 | procedure TSVGImageList.Remove(const Name: string);
329 | begin
330 | Delete(IndexOf(Name));
331 | end;
332 |
333 | procedure TSVGImageList.SetHeight(const Value: Integer);
334 | begin
335 | inherited Height := Value;
336 | RecreateBitmaps;
337 | end;
338 |
339 | procedure TSVGImageList.SetImages(Index: Integer; const Value: TSVG);
340 | begin
341 | if (Index >= 0) and (Index < FImages.Count) then
342 | begin
343 | if FImages[Index].SVG <> Value then
344 | FImages[Index].SVG := Value;
345 | end;
346 | end;
347 |
348 | procedure TSVGImageList.SetNames(Index: Integer; const Value: string);
349 | begin
350 | if (Index >= 0) and (Index < FImages.Count) then
351 | FImages[Index].Name := Value;
352 | end;
353 |
354 | procedure TSVGImageList.SetOpacity(const Value: Byte);
355 | begin
356 | FOpacity := Value;
357 | RecreateBitmaps;
358 | end;
359 |
360 | procedure TSVGImageList.SetWidth(const Value: Integer);
361 | begin
362 | inherited Width := Value;
363 | RecreateBitmaps;
364 | end;
365 |
366 | procedure PaintToBitmap(SVG: TSVG; Bitmap: TBitmap; Bounds: TGPRectF;
367 | Rects: PRectArray; RectCount: Integer);
368 | var
369 | Graphics: TGPGraphics;
370 | begin
371 | Graphics := TGPGraphics.Create(Bitmap.Canvas.Handle);
372 | try
373 | Graphics.SetSmoothingMode(SmoothingModeAntiAlias);
374 | SVG.PaintTo(Graphics, Bounds, Rects, RectCount);
375 | finally
376 | Graphics.Free;
377 | end;
378 | end;
379 |
380 | function TSVGImageList.SVGToIcon(const SVG: TSVG): HICON;
381 | var
382 | R: TGPRectF;
383 |
384 | function SVGToIcon24(SVG: TSVG): HIcon;
385 | var
386 | ColorBitmap, MaskBitmap: TBitmap;
387 | X: Integer;
388 | Y: Integer;
389 | Bits: PRGBQuad;
390 | IconInfo: TIconInfo;
391 | TransparentBitmap: TBitmap;
392 | BF: TBlendFunction;
393 | DC: THandle;
394 | begin
395 | ColorBitmap := TBitmap.Create;
396 | MaskBitmap := TBitmap.Create;
397 | TransparentBitmap := TBitmap.Create;
398 | try
399 | TransparentBitmap.PixelFormat := pf32bit;
400 | TransparentBitmap.Width := Width;
401 | TransparentBitmap.Height := Height;
402 | FillChar(TransparentBitmap.Scanline[Height - 1]^, Width * Height * 4, 0);
403 |
404 | PaintToBitmap(SVG, TransparentBitmap, R, nil, 0);
405 |
406 |
407 | ColorBitmap.PixelFormat := pf32bit;
408 | ColorBitmap.Width := Width;
409 | ColorBitmap.Height := Height;
410 | MaskBitmap.PixelFormat := pf32bit;
411 | MaskBitmap.Width := Width;
412 | MaskBitmap.Height := Height;
413 |
414 |
415 | ColorBitmap.Canvas.Brush.Color := BkColor;
416 | ColorBitmap.Canvas.FillRect(Rect(0, 0, Width, Height));
417 |
418 | BF.BlendOp := AC_SRC_OVER;
419 | BF.BlendFlags := 0;
420 | BF.SourceConstantAlpha := 255;
421 | BF.AlphaFormat := AC_SRC_ALPHA;
422 | AlphaBlend(ColorBitmap.Canvas.Handle, 0, 0, Width, Height,
423 | TransparentBitmap.Canvas.Handle, 0, 0, Width, Height, BF);
424 |
425 | DC := MaskBitmap.Canvas.Handle;
426 | for Y := 0 to Height - 1 do
427 | begin
428 | Bits := TransparentBitmap.ScanLine[Y];
429 | for X := 0 to Width - 1 do
430 | begin
431 | if Bits.rgbReserved = 0 then
432 | SetPixelV(DC, X, Y, clWhite)
433 | else
434 | SetPixelV(DC, X, Y, clBlack);
435 | Inc(Bits);
436 | end;
437 | end;
438 |
439 | IconInfo.fIcon := True;
440 | IconInfo.hbmColor := ColorBitmap.Handle;
441 | IconInfo.hbmMask := MaskBitmap.Handle;
442 | Result := CreateIconIndirect(IconInfo);
443 | finally
444 | TransparentBitmap.Free;
445 | ColorBitmap.Free;
446 | MaskBitmap.Free;
447 | end;
448 | end;
449 |
450 | function SVGToIcon32(SVG: TSVG): HICON;
451 | var
452 | Bitmap: TGPBitmap;
453 | Graphics: TGPGraphics;
454 | begin
455 | Bitmap := TGPBitmap.Create(Width, Height);
456 | Graphics := TGPGraphics.Create(Bitmap);
457 | Graphics.SetSmoothingMode(SmoothingModeAntiAlias);
458 | SVG.PaintTo(Graphics, R, nil, 0);
459 | Graphics.Free;
460 |
461 | Bitmap.GetHICON(Result);
462 | Bitmap.Free;
463 | end;
464 |
465 | begin
466 | SVG.SVGOpacity := FOpacity / 255;
467 | R := CalcRect(MakeRect(0.0, 0, Width, Height), SVG.Width, SVG.Height, baCenterCenter);
468 |
469 | if GetFileVersion(comctl32) >= ComCtlVersionIE6 then
470 | Result := SVGToIcon32(SVG)
471 | else
472 | Result := SVGToIcon24(SVG);
473 |
474 | SVG.SVGOpacity := 1;
475 | end;
476 |
477 | procedure TSVGImageList.WriteImageData(Stream: TStream);
478 | var
479 | Count, Size: Integer;
480 | SVG: TSVG;
481 | Name: string;
482 | C: Integer;
483 | SVGStream: TMemoryStream;
484 | begin
485 | Count := FImages.Count;
486 | Stream.Write(Count, SizeOf(Integer));
487 |
488 | SVGStream := TMemoryStream.Create;
489 | for C := 0 to Count - 1 do
490 | begin
491 | Name := FImages[C].Name;
492 | SVG := FImages[C].SVG;
493 | Size := Length(Name);
494 | Stream.Write(Size, SizeOf(Integer));
495 | Stream.WriteBuffer(PChar(Name)^, Size * SizeOf(Char));
496 |
497 | SVG.SaveToStream(SVGStream);
498 | Size := SVGStream.Size;
499 | Stream.Write(Size, SizeOf(Integer));
500 | SVGStream.Position := 0;
501 | Stream.CopyFrom(SVGStream, Size);
502 | SVGStream.Clear;
503 | end;
504 | SVGStream.Free;
505 | end;
506 |
507 | procedure TSVGImageList.WriteLeft(Writer: TWriter);
508 | begin
509 | Writer.WriteInteger(LongRec(DesignInfo).Lo);
510 | end;
511 |
512 | procedure TSVGImageList.WriteTop(Writer: TWriter);
513 | begin
514 | Writer.WriteInteger(LongRec(DesignInfo).Hi);
515 | end;
516 |
517 | { TSVGImageCollectionItem }
518 |
519 | procedure TSVGCollectionItem.AssignTo(Dest: TPersistent);
520 | begin
521 | if Dest is TSVGCollectionItem then
522 | begin
523 | TSVGCollectionItem(Dest).FName := FName;
524 | TSVGCollectionItem(Dest).FSVG.LoadFromText(FSVG.Source);
525 | end;
526 | end;
527 |
528 | constructor TSVGCollectionItem.Create(Collection: TCollection);
529 | begin
530 | inherited Create(Collection);
531 | FSVG := TSVG.Create;
532 | end;
533 |
534 | destructor TSVGCollectionItem.Destroy;
535 | begin
536 | FreeAndNil(FSVG);
537 | inherited;
538 | end;
539 |
540 | procedure TSVGCollectionItem.SetName(const Value: string);
541 | begin
542 | FName := Value;
543 | TSVGCollectionItems(Collection).Update(Self);
544 | end;
545 |
546 | procedure TSVGCollectionItem.SetSVG(const Value: TSVG);
547 | begin
548 | FSVG.LoadFromText(Value.Source);
549 | TSVGCollectionItems(Collection).Update(Self);
550 | end;
551 |
552 | { TSVGImageListCollection }
553 |
554 | function TSVGCollectionItems.Add: TSVGCollectionItem;
555 | begin
556 | Result := TSVGCollectionItem(inherited Add);
557 | end;
558 |
559 | procedure TSVGCollectionItems.Assign(Source: TPersistent);
560 | var
561 | C: Integer;
562 | Item: TSVGCollectionItem;
563 | begin
564 | inherited;
565 | if Source is TSVGCollectionItems then
566 | try
567 | BeginUpdate;
568 | Clear;
569 | for C := 0 to TSVGCollectionItems(Source).Count - 1 do
570 | begin
571 | Item := Add;
572 | TSVGCollectionItems(Source)[C].AssignTo(Item);
573 | end;
574 | finally
575 | EndUpdate;
576 | Update(nil);
577 | end;
578 | end;
579 |
580 | constructor TSVGCollectionItems.Create(AOwner: TPersistent);
581 | begin
582 | inherited Create(TSVGCollectionItem);
583 | FOwner := AOwner;
584 | end;
585 |
586 | function TSVGCollectionItems.GetItem(
587 | Index: Integer): TSVGCollectionItem;
588 | begin
589 | Result := TSVGCollectionItem(inherited GetItem(Index));
590 | end;
591 |
592 | function TSVGCollectionItems.GetOwner: TPersistent;
593 | begin
594 | Result := FOwner;
595 | end;
596 |
597 | procedure TSVGCollectionItems.Notify(Item: TCollectionItem;
598 | Action: TCollectionNotification);
599 | begin
600 | inherited;
601 | if FOwner is TSVGImageList then
602 | TSVGImageList(FOwner).RecreateBitmaps;
603 | end;
604 |
605 | procedure TSVGCollectionItems.SetItem(Index: Integer;
606 | const Value: TSVGCollectionItem);
607 | begin
608 | inherited SetItem(Index, Value);
609 | end;
610 |
611 | procedure TSVGCollectionItems.Update(Item: TCollectionItem);
612 | begin
613 | inherited;
614 | if FOwner is TSVGImageList then
615 | TSVGImageList(FOwner).RecreateBitmaps;
616 | end;
617 |
618 | end.
619 |
--------------------------------------------------------------------------------
/vcl/svgimage/SVGImagePackageD.dpk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ekot1/DelphiSVG/0c2fc478d221a914c8ca1a15f0e65e282396000a/vcl/svgimage/SVGImagePackageD.dpk
--------------------------------------------------------------------------------
/vcl/svgimage/SVGImagePackageD.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {B4EF66C9-50A0-473B-A3E2-04371F0E8F45}
4 | SVGImagePackageD.dpk
5 | True
6 | Debug
7 | 1
8 | Package
9 | VCL
10 | 18.8
11 | Win32
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | true
28 | Base
29 | true
30 |
31 |
32 | true
33 | Cfg_1
34 | true
35 | true
36 |
37 |
38 | true
39 | Base
40 | true
41 |
42 |
43 | true
44 | Cfg_2
45 | true
46 | true
47 |
48 |
49 | .\Output
50 | false
51 | false
52 | MWK - SVG-Unterstьtzung Designtime
53 | true
54 | SVGImagePackageD
55 | true
56 | false
57 | 1049
58 | false
59 | 00400000
60 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=
61 | false
62 | true
63 |
64 |
65 | xmlrtl;rtl;vclactnband;vclx;vcl;SVGPackage;SVGImagePackageR;$(DCC_UsePackage)
66 | 1033
67 | CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName)
68 | true
69 |
70 |
71 | xmlrtl;rtl;vclactnband;vclx;vcl;$(DCC_UsePackage)
72 |
73 |
74 | 0
75 | false
76 | 0
77 | RELEASE;$(DCC_Define)
78 |
79 |
80 | 1033
81 | CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName)
82 | true
83 |
84 |
85 | DEBUG;$(DCC_Define)
86 | false
87 | true
88 |
89 |
90 | 1033
91 | CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName)
92 | true
93 |
94 |
95 |
96 | MainSource
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 | Cfg_2
112 | Base
113 |
114 |
115 | Base
116 |
117 |
118 | Cfg_1
119 | Base
120 |
121 |
122 |
123 | Delphi.Personality.12
124 | Package
125 |
126 |
127 |
128 | SVGImagePackageD.dpk
129 |
130 |
131 | Microsoft Office 2000 Sample Automation Server Wrapper Components
132 | Microsoft Office XP Sample Automation Server Wrapper Components
133 |
134 |
135 |
136 | True
137 | False
138 |
139 |
140 | 12
141 |
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/vcl/svgimage/SVGImagePackageD.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ekot1/DelphiSVG/0c2fc478d221a914c8ca1a15f0e65e282396000a/vcl/svgimage/SVGImagePackageD.res
--------------------------------------------------------------------------------
/vcl/svgimage/SVGImagePackageR.dpk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ekot1/DelphiSVG/0c2fc478d221a914c8ca1a15f0e65e282396000a/vcl/svgimage/SVGImagePackageR.dpk
--------------------------------------------------------------------------------
/vcl/svgimage/SVGImagePackageR.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ekot1/DelphiSVG/0c2fc478d221a914c8ca1a15f0e65e282396000a/vcl/svgimage/SVGImagePackageR.res
--------------------------------------------------------------------------------
/vcl/svgimage/SVGImageRegister.pas:
--------------------------------------------------------------------------------
1 | unit SVGImageRegister;
2 |
3 | interface
4 |
5 | procedure Register;
6 |
7 | implementation
8 |
9 | uses
10 | System.Classes,
11 | SVGImage, SVGImageList, SVGSpeedButton;
12 |
13 | procedure Register;
14 | begin
15 | RegisterComponents('MWK', [TSVGImage, TSVGImageList, TSVGSpeedButton]);
16 | end;
17 |
18 |
19 | end.
20 |
--------------------------------------------------------------------------------
/vcl/svgimage/SVGImgLstEditor.dfm:
--------------------------------------------------------------------------------
1 | object ImageListEditor: TImageListEditor
2 | Left = 392
3 | Top = 450
4 | ActiveControl = OkButton
5 | Caption = 'ImageListEditor'
6 | ClientHeight = 316
7 | ClientWidth = 547
8 | Color = clBtnFace
9 | Font.Charset = DEFAULT_CHARSET
10 | Font.Color = clWindowText
11 | Font.Height = -11
12 | Font.Name = 'MS Sans Serif'
13 | Font.Style = []
14 | OldCreateOrder = True
15 | Position = poScreenCenter
16 | OnCreate = FormCreate
17 | OnDestroy = FormDestroy
18 | OnShow = FormShow
19 | DesignSize = (
20 | 547
21 | 316)
22 | PixelsPerInch = 96
23 | TextHeight = 13
24 | object GroupBox1: TGroupBox
25 | Left = 8
26 | Top = 8
27 | Width = 435
28 | Height = 129
29 | Anchors = [akLeft, akTop, akRight]
30 | Caption = 'Graphic'
31 | TabOrder = 0
32 | DesignSize = (
33 | 435
34 | 129)
35 | object Label1: TLabel
36 | Left = 128
37 | Top = 24
38 | Width = 31
39 | Height = 13
40 | Caption = 'Name:'
41 | end
42 | object ImagePanel: TPanel
43 | Left = 8
44 | Top = 16
45 | Width = 105
46 | Height = 105
47 | BevelOuter = bvNone
48 | BorderStyle = bsSingle
49 | Color = clWhite
50 | ParentBackground = False
51 | TabOrder = 0
52 | object ActImage: TImage
53 | Left = 7
54 | Top = 7
55 | Width = 89
56 | Height = 89
57 | Center = True
58 | Proportional = True
59 | Stretch = True
60 | Transparent = True
61 | end
62 | end
63 | object ImageName: TEdit
64 | Left = 128
65 | Top = 40
66 | Width = 291
67 | Height = 21
68 | Anchors = [akLeft, akTop, akRight]
69 | TabOrder = 1
70 | OnExit = ImageNameExit
71 | end
72 | end
73 | object GroupBox2: TGroupBox
74 | Left = 8
75 | Top = 144
76 | Width = 531
77 | Height = 129
78 | Anchors = [akLeft, akTop, akRight, akBottom]
79 | Caption = 'Pictures list'
80 | TabOrder = 1
81 | DesignSize = (
82 | 531
83 | 129)
84 | object ListView1: TListView
85 | Left = 8
86 | Top = 16
87 | Width = 515
88 | Height = 105
89 | Anchors = [akLeft, akTop, akRight, akBottom]
90 | Columns = <>
91 | DragMode = dmAutomatic
92 | FullDrag = True
93 | HideSelection = False
94 | IconOptions.Arrangement = iaLeft
95 | IconOptions.AutoArrange = True
96 | LargeImages = SVGImageList1
97 | MultiSelect = True
98 | ReadOnly = True
99 | ShowWorkAreas = True
100 | TabOrder = 0
101 | OnDragDrop = ListView1DragDrop
102 | OnDragOver = ListView1DragOver
103 | OnSelectItem = ListView1SelectItem
104 | end
105 | end
106 | object OkButton: TButton
107 | Left = 450
108 | Top = 16
109 | Width = 89
110 | Height = 25
111 | Anchors = [akTop, akRight]
112 | Caption = '&Ok'
113 | Default = True
114 | ModalResult = 1
115 | TabOrder = 2
116 | OnClick = OkButtonClick
117 | end
118 | object CancelButton: TButton
119 | Left = 450
120 | Top = 48
121 | Width = 89
122 | Height = 25
123 | Anchors = [akTop, akRight]
124 | Cancel = True
125 | Caption = '&Cancel'
126 | ModalResult = 2
127 | TabOrder = 3
128 | end
129 | object ApplyButton: TButton
130 | Left = 450
131 | Top = 80
132 | Width = 89
133 | Height = 25
134 | Anchors = [akTop, akRight]
135 | Caption = 'A&pply'
136 | TabOrder = 4
137 | OnClick = ApplyButtonClick
138 | end
139 | object AddButton: TButton
140 | Left = 20
141 | Top = 279
142 | Width = 81
143 | Height = 25
144 | Anchors = [akBottom]
145 | Caption = '&Add...'
146 | TabOrder = 5
147 | OnClick = AddButtonClick
148 | end
149 | object DeleteButton: TButton
150 | Left = 231
151 | Top = 279
152 | Width = 81
153 | Height = 25
154 | Anchors = [akBottom]
155 | Caption = '&Delete'
156 | TabOrder = 6
157 | OnClick = DeleteButtonClick
158 | end
159 | object ClearButton: TButton
160 | Left = 336
161 | Top = 279
162 | Width = 81
163 | Height = 25
164 | Anchors = [akBottom]
165 | Caption = 'Empty'
166 | TabOrder = 7
167 | OnClick = ClearButtonClick
168 | end
169 | object ExportButton: TButton
170 | Left = 441
171 | Top = 279
172 | Width = 81
173 | Height = 25
174 | Anchors = [akBottom]
175 | Caption = '&Export...'
176 | TabOrder = 8
177 | OnClick = ExportButtonClick
178 | end
179 | object ReplaceButton: TButton
180 | Left = 125
181 | Top = 279
182 | Width = 81
183 | Height = 25
184 | Anchors = [akBottom]
185 | Caption = '&Replace...'
186 | TabOrder = 9
187 | OnClick = ReplaceButtonClick
188 | end
189 | object OpenPictureDialog1: TOpenPictureDialog
190 | Filter = 'Scalable Vector Graphics (*.svg)|*.svg'
191 | Options = [ofHideReadOnly, ofAllowMultiSelect, ofPathMustExist, ofFileMustExist, ofEnableSizing]
192 | Left = 24
193 | Top = 168
194 | end
195 | object SavePictureDialog1: TSavePictureDialog
196 | DefaultExt = 'svg'
197 | Filter = 'Scalable Vector Graphics (*.svg)|*.svg'
198 | Options = [ofHideReadOnly, ofPathMustExist, ofEnableSizing]
199 | Left = 56
200 | Top = 168
201 | end
202 | object SVGImageList1: TSVGImageList
203 | Opacity = 255
204 | Width = 32
205 | Height = 32
206 | Left = 96
207 | Top = 168
208 | Images = {00000000}
209 | end
210 | end
211 |
--------------------------------------------------------------------------------
/vcl/svgimage/SVGImgLstEditor.pas:
--------------------------------------------------------------------------------
1 | unit SVGImgLstEditor;
2 |
3 | interface
4 |
5 | uses
6 | Winapi.Windows, Winapi.Messages,
7 | System.SysUtils, System.Variants, System.Classes, System.ImageList,
8 | Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls, Vcl.ImgList,
9 | Vcl.ExtDlgs, Vcl.ExtCtrls,
10 | DesignIntf, DesignEditors,
11 | SVGImageList;
12 |
13 | type
14 | TImageListEditor = class(TForm)
15 | GroupBox1: TGroupBox;
16 | GroupBox2: TGroupBox;
17 | ImagePanel: TPanel;
18 | Label1: TLabel;
19 | ImageName: TEdit;
20 | OkButton: TButton;
21 | CancelButton: TButton;
22 | ApplyButton: TButton;
23 | ListView1: TListView;
24 | AddButton: TButton;
25 | DeleteButton: TButton;
26 | ClearButton: TButton;
27 | ExportButton: TButton;
28 | OpenPictureDialog1: TOpenPictureDialog;
29 | SavePictureDialog1: TSavePictureDialog;
30 | ActImage: TImage;
31 | SVGImageList1: TSVGImageList;
32 | ReplaceButton: TButton;
33 | procedure FormCreate(Sender: TObject);
34 | procedure ApplyButtonClick(Sender: TObject);
35 | procedure ClearButtonClick(Sender: TObject);
36 | procedure AddButtonClick(Sender: TObject);
37 | procedure DeleteButtonClick(Sender: TObject);
38 | procedure ListView1SelectItem(Sender: TObject; Item: TListItem;
39 | Selected: Boolean);
40 | procedure ExportButtonClick(Sender: TObject);
41 | procedure OkButtonClick(Sender: TObject);
42 | procedure FormDestroy(Sender: TObject);
43 | procedure ImageNameExit(Sender: TObject);
44 | procedure FormShow(Sender: TObject);
45 | procedure ListView1DragOver(Sender, Source: TObject; X, Y: Integer;
46 | State: TDragState; var Accept: Boolean);
47 | procedure ListView1DragDrop(Sender, Source: TObject; X, Y: Integer);
48 | procedure ReplaceButtonClick(Sender: TObject);
49 | private
50 | FImages: TSVGCollectionItems;
51 | FComponentList: TSVGImageList;
52 | FChanged: Boolean;
53 | FModified: Boolean;
54 |
55 | procedure BuildList(Selected: Integer);
56 | procedure PaintActive;
57 | procedure Apply;
58 | public
59 | constructor CreateImgListEditor(AOwner: TComponent;
60 | ASVGImgList: TSVGImageList);
61 | property Modified: Boolean read FModified;
62 | end;
63 |
64 | TSVGImageListProperty = class(TClassProperty)
65 | public
66 | procedure Edit; override;
67 | function GetAttributes: TPropertyAttributes; override;
68 | function GetValue: string; override;
69 | end;
70 |
71 | TSVGImageListEditor = class(TDefaultEditor)
72 | protected
73 | procedure EditProperty(const PropertyEditor: IProperty;
74 | var Continue: Boolean); override;
75 | end;
76 |
77 | procedure Register;
78 |
79 | implementation
80 |
81 | {$R *.dfm}
82 |
83 | uses
84 | Winapi.GDIPAPI,
85 | SVG, SVGImage;
86 |
87 | procedure Register;
88 | begin
89 | RegisterComponentEditor(TSVGImageList, TSVGImageListEditor);
90 |
91 | RegisterPropertyEditor(TypeInfo(TSVGCollectionItems), TSVGImageList, 'Items', TSVGImageListProperty);
92 | end;
93 |
94 | procedure TImageListEditor.FormCreate(Sender: TObject);
95 | begin
96 | ApplyButton.Enabled := False;
97 | ImageName.Enabled := False;
98 | DeleteButton.Enabled := False;
99 | ExportButton.Enabled := False;
100 | FImages := TSVGCollectionItems.Create(Self);
101 | FChanged := False;
102 | FModified := False;
103 | end;
104 |
105 | procedure TImageListEditor.ApplyButtonClick(Sender: TObject);
106 | begin
107 | ApplyButton.Enabled := False;
108 | Apply;
109 | end;
110 |
111 | procedure TImageListEditor.ClearButtonClick(Sender: TObject);
112 | begin
113 | ApplyButton.Enabled := True;
114 | ImageName.Enabled := False;
115 | ImageName.Text := '';
116 | ListView1.Clear;
117 | ActImage.Picture := nil;
118 | SVGImageList1.Clear;
119 | FChanged := True;
120 | FImages.Clear;
121 | ClearButton.Enabled := FImages.Count > 0;
122 | end;
123 |
124 | procedure TImageListEditor.AddButtonClick(Sender: TObject);
125 | var
126 | C: Integer;
127 | SVG: TSVG;
128 | FileName: string;
129 | Item: TSVGCollectionItem;
130 | begin
131 | if OpenPictureDialog1.Execute then
132 | begin
133 | ApplyButton.Enabled := True;
134 | SVG := TSVG.Create;
135 | for C := 0 to OpenPictureDialog1.Files.Count - 1 do
136 | begin
137 | FileName := ChangeFileExt(ExtractFileName(OpenPictureDialog1.Files[C]), '');
138 | try
139 | SVG.LoadFromFile(OpenPictureDialog1.Files[C]);
140 | Item := FImages.Add;
141 | Item.Name := FileName;
142 | Item.SVG := SVG;
143 | FChanged := True;
144 | finally
145 | end;
146 | end;
147 | SVG.Free;
148 | BuildList(MaxInt);
149 | ClearButton.Enabled := FImages.Count > 0;
150 | end;
151 | end;
152 |
153 | procedure TImageListEditor.DeleteButtonClick(Sender: TObject);
154 | var
155 | C: Integer;
156 | Selected: Integer;
157 | begin
158 | ApplyButton.Enabled := True;
159 | DeleteButton.Enabled := ListView1.SelCount > 0;
160 | ReplaceButton.Enabled := ListView1.SelCount = 1;
161 |
162 | Selected := ListView1.ItemIndex;
163 | for C := ListView1.Items.Count - 1 downto 0 do
164 | if ListView1.Items[C].Selected then
165 | FImages.Delete(C);
166 |
167 | FChanged := True;
168 | BuildList(Selected);
169 | ClearButton.Enabled := FImages.Count > 0;
170 | PaintActive;
171 | end;
172 |
173 | procedure TImageListEditor.ListView1DragDrop(Sender, Source: TObject; X,
174 | Y: Integer);
175 | var
176 | Target: TListItem;
177 | Item: TCollectionItem;
178 | SIndex, DIndex: Integer;
179 | begin
180 | SIndex := ListView1.ItemIndex;
181 | Target := ListView1.GetItemAt(X, Y);
182 | if Target = nil then
183 | Target := ListView1.GetNearestItem(Point(X, Y), sdRight);
184 |
185 | if Assigned(Target) then
186 | DIndex := ListView1.Items.IndexOf(Target)
187 | else
188 | DIndex := ListView1.Items.Count - 1;
189 |
190 | Item := FImages[SIndex];
191 | Item.Index := DIndex;
192 | BuildList(Item.Index);
193 | if SIndex <> DIndex then
194 | begin
195 | FChanged := True;
196 | ApplyButton.Enabled := True;
197 | end;
198 | end;
199 |
200 | procedure TImageListEditor.ListView1DragOver(Sender, Source: TObject; X,
201 | Y: Integer; State: TDragState; var Accept: Boolean);
202 | begin
203 | Accept := Source = Sender;
204 | end;
205 |
206 | procedure TImageListEditor.ListView1SelectItem(Sender: TObject;
207 | Item: TListItem; Selected: Boolean);
208 | begin
209 | DeleteButton.Enabled := ListView1.SelCount > 0;
210 | ReplaceButton.Enabled := ListView1.SelCount = 1;
211 | PaintActive;
212 | ExportButton.Enabled := ListView1.SelCount = 1;
213 | end;
214 |
215 | procedure TImageListEditor.PaintActive;
216 | var
217 | SVGGraphic: TSVGGraphic;
218 | begin
219 | if ListView1.SelCount <> 1 then
220 | begin
221 | ActImage.Picture := nil;
222 | ImageName.Text := '';
223 | ImageName.Enabled := False;
224 | Exit;
225 | end;
226 |
227 | ActImage.Picture := nil;
228 | ActImage.Repaint;
229 | ImageName.Text := ListView1.Selected.Caption;
230 | ImageName.Enabled := True;
231 | SVGGraphic := TSVGGraphic.Create;
232 | SVGGraphic.AssignSVG(FImages[ListView1.ItemIndex].SVG);
233 | ActImage.Picture.Assign(SVGGraphic);
234 | SVGGraphic.Free;
235 | end;
236 |
237 | procedure TImageListEditor.ReplaceButtonClick(Sender: TObject);
238 | var
239 | C: Integer;
240 | SVG: TSVG;
241 | FileName: string;
242 | Item: TSVGCollectionItem;
243 | begin
244 | if OpenPictureDialog1.Execute then
245 | begin
246 | ApplyButton.Enabled := True;
247 | SVG := TSVG.Create;
248 | for C := 0 to OpenPictureDialog1.Files.Count - 1 do
249 | begin
250 | FileName := ChangeFileExt(ExtractFileName(OpenPictureDialog1.Files[C]), '');
251 | try
252 | SVG.LoadFromFile(OpenPictureDialog1.Files[C]);
253 | Item := FImages[ListView1.ItemIndex];
254 | Item.Name := FileName;
255 | Item.SVG := SVG;
256 | FChanged := True;
257 | finally
258 | end;
259 | end;
260 | SVG.Free;
261 | BuildList(MaxInt);
262 | ClearButton.Enabled := FImages.Count > 0;
263 | end;
264 | end;
265 |
266 | procedure TImageListEditor.BuildList(Selected: Integer);
267 | var
268 | C: Integer;
269 | LI: TListItem;
270 | begin
271 | ListView1.Clear;
272 | SVGImageList1.Clear;
273 | for C := 0 to FImages.Count - 1 do
274 | begin
275 | SVGImageList1.Add(FImages[C].SVG, FImages[C].Name);
276 | LI := ListView1.Items.Add;
277 | LI.ImageIndex := C;
278 | LI.Caption := FImages[C].Name;
279 | end;
280 |
281 | if Selected < -1 then
282 | Selected := -1;
283 | if Selected >= FImages.Count then
284 | Selected := FImages.Count - 1;
285 |
286 | ListView1.ItemIndex := Selected;
287 | end;
288 |
289 | procedure TImageListEditor.ExportButtonClick(Sender: TObject);
290 | begin
291 | if SavePictureDialog1.Execute then
292 | FImages[ListView1.ItemIndex].SVG.SaveToFile(SavePictureDialog1.FileName);
293 | end;
294 |
295 | procedure TImageListEditor.Apply;
296 | begin
297 | if not FChanged then
298 | Exit;
299 | FComponentList.Items.Assign(FImages);
300 | FChanged := False;
301 | FModified := True;
302 | end;
303 |
304 | constructor TImageListEditor.CreateImgListEditor(AOwner: TComponent;
305 | ASVGImgList: TSVGImageList);
306 | begin
307 | inherited Create(AOwner);
308 | FComponentList := ASVGImgList;
309 | end;
310 |
311 | procedure TImageListEditor.OkButtonClick(Sender: TObject);
312 | begin
313 | Apply;
314 | end;
315 |
316 | procedure TImageListEditor.FormDestroy(Sender: TObject);
317 | begin
318 | FImages.Free;
319 | end;
320 |
321 | { TSVGImageListPropertity }
322 |
323 | procedure TSVGImageListProperty.Edit;
324 | var
325 | Editor: TImageListEditor;
326 | SVGImageList: TSVGImageList;
327 | begin
328 | SVGImageList := TSVGImageList(GetComponent(0));
329 | Editor := TImageListEditor.CreateImgListEditor(Application, SVGImageList);
330 | try
331 | Editor.ShowModal;
332 | if Editor.Modified then
333 | Modified;
334 | finally
335 | Editor.Free;
336 | end;
337 | end;
338 |
339 | function TSVGImageListProperty.GetAttributes: TPropertyAttributes;
340 | begin
341 | Result := inherited GetAttributes + [paDialog, paReadOnly];
342 | end;
343 |
344 | function TSVGImageListProperty.GetValue: string;
345 | begin
346 | Result := 'SVGImages';
347 | end;
348 |
349 | { TSVGImageListEditor }
350 |
351 | procedure TSVGImageListEditor.EditProperty(const PropertyEditor: IProperty;
352 | var Continue: Boolean);
353 | var
354 | PropName: string;
355 | begin
356 | PropName := PropertyEditor.GetName;
357 | if (CompareText(PropName, 'Items') = 0) then
358 | begin
359 | PropertyEditor.Edit;
360 | Continue := False;
361 | end;
362 | end;
363 |
364 |
365 | procedure TImageListEditor.ImageNameExit(Sender: TObject);
366 | begin
367 | if FImages[ListView1.ItemIndex].Name <> ImageName.Text then
368 | begin
369 | FChanged := True;
370 | FImages[ListView1.ItemIndex].Name := ImageName.Text;
371 | ListView1.Selected.Caption := ImageName.Text;
372 | ApplyButton.Enabled := True;
373 | end;
374 | end;
375 |
376 | procedure TImageListEditor.FormShow(Sender: TObject);
377 | begin
378 | FImages.Assign(FComponentList.Items);
379 |
380 | BuildList(0);
381 | ClearButton.Enabled := FImages.Count > 0;
382 | end;
383 |
384 | end.
385 |
--------------------------------------------------------------------------------
/vcl/svgimage/SVGSpeedButton.pas:
--------------------------------------------------------------------------------
1 | unit SVGSpeedButton;
2 |
3 | interface
4 |
5 | uses
6 | System.Classes, VCL.Buttons, SVG;
7 |
8 | type
9 | TSVGSpeedButton = class(TSpeedButton)
10 | private
11 | FSVG: TSVG;
12 | function GetSVG: TSVG;
13 | procedure SetSVG(const Value: TSVG);
14 | protected
15 | procedure Paint; override;
16 | public
17 | constructor Create(AOwner: TComponent); override;
18 | destructor Destroy; override;
19 | published
20 | property SVG: TSVG read GetSVG write SetSVG;
21 | end;
22 |
23 | implementation
24 |
25 | uses
26 | Winapi.Windows,
27 | Vcl.Themes, Vcl.Controls, Vcl.ActnList, Vcl.Graphics,
28 | SVGImageList,
29 | Winapi.GDIPAPI, GDIPUtils,
30 | System.Types, System.Math;
31 |
32 | { TSVGSpeedButton }
33 |
34 | constructor TSVGSpeedButton.Create(AOwner: TComponent);
35 | begin
36 | inherited;
37 | FSVG := TSVG.Create;
38 | end;
39 |
40 | destructor TSVGSpeedButton.Destroy;
41 | begin
42 | FSVG.Free;
43 | inherited;
44 | end;
45 |
46 | function TSVGSpeedButton.GetSVG: TSVG;
47 | begin
48 | Result := FSVG;
49 | end;
50 |
51 | procedure TSVGSpeedButton.Paint;
52 | const
53 | DownStyles: array[Boolean] of Integer = (BDR_RAISEDINNER, BDR_SUNKENOUTER);
54 | FillStyles: array[Boolean] of Integer = (BF_MIDDLE, 0);
55 | var
56 | PaintRect: TRect;
57 | DrawFlags: Integer;
58 | Offset: TPoint;
59 | Button: TThemedButton;
60 | ToolButton: TThemedToolBar;
61 | Details: TThemedElementDetails;
62 | Image: TSVG;
63 | FMargin: Integer;
64 | R: TGPRectF;
65 | begin
66 | if Assigned(Action) and (Action is TCustomAction) and
67 | (TCustomAction(Action).ImageIndex <> -1) and
68 | (TCustomAction(Action).ActionList.Images is TSVGImageList) then
69 | Image := TSVGImageList(TCustomAction(Action).ActionList.Images).Images[TCustomAction(Action).ImageIndex]
70 | else
71 | Image := FSVG;
72 |
73 | if not Enabled then
74 | begin
75 | FState := bsDisabled;
76 | end
77 | else if FState = bsDisabled then
78 | if Down and (GroupIndex <> 0) then
79 | FState := bsExclusive
80 | else
81 | FState := bsUp;
82 |
83 | if StyleServices.Enabled then
84 | begin
85 | //PerformEraseBackground(Self, Canvas.Handle);
86 |
87 | if not Enabled then
88 | Button := tbPushButtonDisabled
89 | else
90 | if FState in [bsDown, bsExclusive] then
91 | Button := tbPushButtonPressed
92 | else
93 | if MouseInControl then
94 | Button := tbPushButtonHot
95 | else
96 | Button := tbPushButtonNormal;
97 |
98 | ToolButton := ttbToolbarDontCare;
99 | if Flat then
100 | begin
101 | case Button of
102 | tbPushButtonDisabled:
103 | Toolbutton := ttbButtonDisabled;
104 | tbPushButtonPressed:
105 | Toolbutton := ttbButtonPressed;
106 | tbPushButtonHot:
107 | Toolbutton := ttbButtonHot;
108 | tbPushButtonNormal:
109 | Toolbutton := ttbButtonNormal;
110 | end;
111 | end;
112 |
113 | PaintRect := ClientRect;
114 | if ToolButton = ttbToolbarDontCare then
115 | begin
116 | Details := StyleServices.GetElementDetails(Button);
117 | StyleServices.DrawElement(Canvas.Handle, Details, PaintRect);
118 | StyleServices.GetElementContentRect(Canvas.Handle, Details, ClientRect, PaintRect);
119 | end
120 | else
121 | begin
122 | Details := StyleServices.GetElementDetails(ToolButton);
123 | StyleServices.DrawElement(Canvas.Handle, Details, PaintRect);
124 | StyleServices.GetElementContentRect(Canvas.Handle, Details, ClientRect, PaintRect);
125 | end;
126 |
127 | // A pressed speed button has a white text. This applies however only to flat buttons.
128 | Offset := Point(IfThen(Button = tbPushButtonPressed, 1, 0), 0);
129 | end
130 | else
131 | begin
132 | PaintRect := Rect(0, 0, Width, Height);
133 | if not Flat then
134 | begin
135 | DrawFlags := DFCS_BUTTONPUSH or DFCS_ADJUSTRECT;
136 | if FState in [bsDown, bsExclusive] then
137 | DrawFlags := DrawFlags or DFCS_PUSHED;
138 | DrawFrameControl(Canvas.Handle, PaintRect, DFC_BUTTON, DrawFlags);
139 | end
140 | else
141 | begin
142 | if (FState in [bsDown, bsExclusive]) or
143 | (MouseInControl and (FState <> bsDisabled)) or
144 | (csDesigning in ComponentState) then
145 | DrawEdge(Canvas.Handle, PaintRect, DownStyles[FState in [bsDown, bsExclusive]],
146 | FillStyles[Transparent] or BF_RECT)
147 | else if not Transparent then
148 | begin
149 | Canvas.Brush.Color := Color;
150 | Canvas.FillRect(PaintRect);
151 | end;
152 | InflateRect(PaintRect, -1, -1);
153 | end;
154 | if FState in [bsDown, bsExclusive] then
155 | begin
156 | if (FState = bsExclusive) and (not Flat or not MouseInControl) then
157 | begin
158 | Canvas.Brush.Bitmap := AllocPatternBitmap(clBtnFace, clBtnHighlight);
159 | Canvas.FillRect(PaintRect);
160 | end;
161 | Offset.X := 1;
162 | Offset.Y := 1;
163 | end
164 | else
165 | begin
166 | Offset.X := 0;
167 | Offset.Y := 0;
168 | end;
169 | end;
170 |
171 | if Assigned(Image) then
172 | begin
173 | FMargin := 2 + Margin;
174 | R.X := PaintRect.Left + Margin + Offset.X + 1;
175 | R.Y := PaintRect.Top + Margin + Offset.Y + 1;
176 | R.Width := (PaintRect.Right - PaintRect.Left + 1) - 2 * FMargin;
177 | R.Height := (PaintRect.Bottom - PaintRect.Top + 1) - 2 * FMargin;
178 | R := CalcRect(R, Image.Width, Image.Height, baCenterCenter);
179 | Image.PaintTo(Canvas.Handle, R, nil, 0);
180 | end;
181 | end;
182 |
183 | procedure TSVGSpeedButton.SetSVG(const Value: TSVG);
184 | begin
185 | FSVG.LoadFromText(Value.Source);
186 | Invalidate;
187 | end;
188 |
189 | end.
190 |
--------------------------------------------------------------------------------