1 {
2 *****************************************************************************
3 See the file COPYING.modifiedLGPL.txt, included in this distribution,
4 for details about the license.
5 *****************************************************************************
6
7 Authors: Alexander Klenin
8
9 }
10 unit TADrawerSVG;
11
12 {$H+}
13
14 interface
15
16 uses
17 Graphics, Classes, FPImage, FPCanvas, EasyLazFreeType,
18 TAFonts, TAChartUtils, TADrawUtils, TAGraph;
19
20 type
21 TSVGDrawer = class(TBasicDrawer, IChartDrawer)
22 strict private
23 FAntialiasingMode: TChartAntialiasingMode;
24 FBrushColor: TFPColor;
25 FBrushStyle: TFPBrushStyle;
26 FClippingPathId: Integer;
27 FFont: TFreeTypeFont;
28 FFontHeight: Integer; // Height of text in pixels
29 FFontOrientation: Integer; // angle*10 (i.e. 90° --> 900, >0 if ccs.
30 FFontColor: TFPColor;
31 FPatterns: TStrings;
32 FPen: TFPCustomPen;
33 FPrevPos: TPoint;
34 FStream: TStream;
35
OpacityStrnull36 function OpacityStr: String;
PointsToStrnull37 function PointsToStr(
38 const APoints: array of TPoint; AStartIndex, ANumPts: Integer): String;
39
40 procedure SetBrush(ABrush: TFPCustomBrush);
41 procedure SetFont(AFont: TFPCustomFont);
42 procedure SetPen(APen: TFPCustomPen);
43
StyleFillnull44 function StyleFill: String;
StyleStrokenull45 function StyleStroke: String;
46
47 procedure WriteFmt(const AFormat: String; AParams: array of const);
48 procedure WriteStr(const AString: String);
49 strict protected
SimpleTextExtentnull50 function SimpleTextExtent(const AText: String): TPoint; override;
51 procedure SimpleTextOut(AX, AY: Integer; const AText: String); override;
52
53 public
54 constructor Create(AStream: TStream; AWriteDocType: Boolean);
55 destructor Destroy; override;
56 public
57 procedure AddToFontOrientation(ADelta: Integer);
58 procedure ClippingStart;
59 procedure ClippingStart(const AClipRect: TRect);
60 procedure ClippingStop;
61 procedure DrawingBegin(const ABoundingBox: TRect); override;
62 procedure DrawingEnd; override;
63 procedure Ellipse(AX1, AY1, AX2, AY2: Integer);
64 procedure FillRect(AX1, AY1, AX2, AY2: Integer);
GetBrushColornull65 function GetBrushColor: TChartColor;
GetFontAnglenull66 function GetFontAngle: Double; override;
GetFontColornull67 function GetFontColor: TFPColor; override;
GetFontNamenull68 function GetFontName: String; override;
GetFontSizenull69 function GetFontSize: Integer; override;
GetFontStylenull70 function GetFontStyle: TChartFontStyles; override;
71 procedure Line(AX1, AY1, AX2, AY2: Integer);
72 procedure Line(const AP1, AP2: TPoint);
73 procedure LineTo(AX, AY: Integer); override;
74 procedure MoveTo(AX, AY: Integer); override;
75 procedure Polygon(
76 const APoints: array of TPoint; AStartIndex, ANumPts: Integer); override;
77 procedure Polyline(
78 const APoints: array of TPoint; AStartIndex, ANumPts: Integer);
79 procedure PrepareSimplePen(AColor: TChartColor);
80 procedure PutImage(AX, AY: Integer; AImage: TFPCustomImage); override;
81 procedure PutPixel(AX, AY: Integer; AColor: TChartColor); override;
82 procedure RadialPie(
83 AX1, AY1, AX2, AY2: Integer;
84 AStartAngle16Deg, AAngleLength16Deg: Integer);
85 procedure Rectangle(const ARect: TRect);
86 procedure Rectangle(AX1, AY1, AX2, AY2: Integer);
87 procedure ResetFont;
88 procedure SetAntialiasingMode(AValue: TChartAntialiasingMode);
89 procedure SetBrushColor(AColor: TChartColor);
90 procedure SetBrushParams(AStyle: TFPBrushStyle; AColor: TChartColor);
91 procedure SetPenParams(AStyle: TFPPenStyle; AColor: TChartColor);
92 end;
93
94
95 { TSVGChartHelper }
96
97 TSVGChartHelper = class helper for TChart
98 procedure SaveToSVGFile(const AFileName: String);
99 end;
100
101
102 implementation
103
104 uses
105 Base64, FPWritePNG, Math, SysUtils, TAGeometry;
106
107 const
108 RECT_FMT =
109 '<rect x="%d" y="%d" width="%d" height="%d" style="%s"/>';
110 var
111 fmtSettings: TFormatSettings;
112
EscapeXMLnull113 function EscapeXML(const AText: String): String;
114 var
115 ch: Char;
116 begin
117 Result := '';
118 for ch in AText do
119 case ch of
120 '<': Result := Result + '<';
121 '>': Result := Result + '>';
122 '"': Result := Result + '"';
123 '''':Result := Result + ''';
124 '&': Result := Result + '&';
125 else Result := Result + ch;
126 end;
127 end;
128
ColorToHexnull129 function ColorToHex(AColor: TFPColor): String;
130 begin
131 if AColor = colBlack then
132 Result := 'black'
133 else if AColor = colWhite then
134 Result := 'white'
135 else
136 with AColor do
137 Result := Format('#%.2x%.2x%.2x', [red shr 8, green shr 8, blue shr 8]);
138 end;
139
DP2Snull140 function DP2S(AValue: TDoublePoint): String;
141 begin
142 Result := Format('%g,%g', [AValue.X, AValue.Y], fmtSettings);
143 end;
144
F2Snull145 function F2S(AValue: Double): String;
146 begin
147 Result := FloatToStr(AValue, fmtSettings);
148 end;
149
SVGGetFontOrientationFuncnull150 function SVGGetFontOrientationFunc(AFont: TFPCustomFont): Integer;
151 begin
152 if AFont is TFont then
153 Result := (AFont as TFont).Orientation
154 else
155 Result := AFont.Orientation;
156 end;
157
SVGChartColorToFPColornull158 function SVGChartColorToFPColor(AChartColor: TChartColor): TFPColor;
159 begin
160 Result := ChartColorToFPColor(ColorToRGB(AChartColor));
161 end;
162
163
164 { TSVGDrawer }
165
166 procedure TSVGDrawer.AddToFontOrientation(ADelta: Integer);
167 begin
168 FFontOrientation += ADelta;
169 end;
170
171 procedure TSVGDrawer.ClippingStart(const AClipRect: TRect);
172 begin
173 FClippingPathId += 1;
174 WriteFmt('<clipPath id="clip%d">', [FClippingPathId]);
175 with AClipRect do
176 WriteFmt(RECT_FMT, [Left, Top, Right - Left, Bottom - Top, '']);
177 WriteStr('</clipPath>');
178 ClippingStart;
179 end;
180
181 procedure TSVGDrawer.ClippingStart;
182 begin
183 WriteFmt('<g clip-path="url(#clip%d)">', [FClippingPathId]);
184 end;
185
186 procedure TSVGDrawer.ClippingStop;
187 begin
188 WriteStr('</g>');
189 end;
190
191 constructor TSVGDrawer.Create(AStream: TStream; AWriteDocType: Boolean);
192 begin
193 inherited Create;
194 InitFonts;
195 FStream := AStream;
196 FPatterns := TStringList.Create;
197 FPen := TFPCustomPen.Create;
198 FGetFontOrientationFunc := @SVGGetFontOrientationFunc;
199 FChartColorToFPColorFunc := @SVGChartColorToFPColor;
200 if AWriteDocType then begin
201 WriteStr('<?xml version="1.0"?>');
202 WriteStr('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"');
203 WriteStr('"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">');
204 end;
205 end;
206
207 destructor TSVGDrawer.Destroy;
208 begin
209 FreeAndNil(FFont);
210 FreeAndNil(FPatterns);
211 FreeAndNil(FPen);
212 inherited Destroy;
213 end;
214
215 procedure TSVGDrawer.DrawingBegin(const ABoundingBox: TRect);
216 begin
217 FAntialiasingMode := amDontCare;
218 with ABoundingBox do
219 WriteFmt(
220 '<svg ' +
221 'xmlns="http://www.w3.org/2000/svg" ' +
222 'xmlns:xlink="http://www.w3.org/1999/xlink" ' +
223 'width="%dpx" height="%dpx" viewBox="%d %d %d %d">',
224 [Right - Left, Bottom - Top, Left, Top, Right, Bottom]);
225 FClippingPathId := 0;
226 end;
227
228 procedure TSVGDrawer.DrawingEnd;
229 var
230 i: Integer;
231 begin
232 if FAntialiasingMode <> amDontCare then
233 WriteStr('</g>');
234 if FPatterns.Count > 0 then begin
235 WriteStr('<defs>');
236 for i := 0 to FPatterns.Count - 1 do
237 WriteFmt(
238 '<pattern id="bs%d" width="8" height="8" patternUnits="userSpaceOnUse">' +
239 '%s</pattern>',
240 [i, FPatterns[i]]);
241 WriteStr('</defs>');
242 FPatterns.Clear;
243 end;
244 WriteStr('</svg>');
245 end;
246
247 procedure TSVGDrawer.Ellipse(AX1, AY1, AX2, AY2: Integer);
248 var
249 e: TEllipse;
250 begin
251 e.InitBoundingBox(AX1, AY1, AX2, AY2);
252 WriteFmt(
253 '<ellipse cx="%g" cy="%g" rx="%g" ry="%g" style="%s"/>',
254 [e.FC.X, e.FC.Y, e.FR.X, e.FR.Y, StyleFill + StyleStroke]);
255 end;
256
257 procedure TSVGDrawer.FillRect(AX1, AY1, AX2, AY2: Integer);
258 begin
259 WriteFmt(RECT_FMT, [AX1, AY1, AX2 - AX1, AY2 - AY1, StyleFill]);
260 end;
261
GetBrushColornull262 function TSVGDrawer.GetBrushColor: TChartColor;
263 begin
264 Result := FPColorToChartColor(FBrushColor);
265 end;
266
GetFontAnglenull267 function TSVGDrawer.GetFontAngle: Double;
268 begin
269 Result := OrientToRad(FFontOrientation);
270 end;
271
TSVGDrawer.GetFontColornull272 function TSVGDrawer.GetFontColor: TFPColor;
273 begin
274 Result := FFontColor;
275 end;
276
TSVGDrawer.GetFontNamenull277 function TSVGDrawer.GetFontName: String;
278 begin
279 Result := FFont.Family;
280 end;
281
GetFontSizenull282 function TSVGDrawer.GetFontSize: Integer;
283 begin
284 Result := Round(FFont.SizeInPoints);
285 end;
286
TSVGDrawer.GetFontStylenull287 function TSVGDrawer.GetFontStyle: TChartFontStyles;
288 begin
289 Result := [];
290 if ftsBold in FFont.Style then Include(Result, cfsBold);
291 if ftsItalic in FFont.Style then Include(Result, cfsItalic);
292 if FFont.UnderlineDecoration then Include(Result, cfsUnderline);
293 if FFont.StrikeoutDecoration then Include(Result, cfsStrikeout);
294 end;
295
296 procedure TSVGDrawer.Line(AX1, AY1, AX2, AY2: Integer);
297 begin
298 WriteFmt(
299 '<line x1="%d" y1="%d" x2="%d" y2="%d" style="%s"/>',
300 [AX1, AY1, AX2, AY2, StyleStroke]);
301 end;
302
303 procedure TSVGDrawer.Line(const AP1, AP2: TPoint);
304 begin
305 Line(AP1.X, AP1.Y, AP2.X, AP2.Y);
306 end;
307
308 procedure TSVGDrawer.LineTo(AX, AY: Integer);
309 begin
310 Line(FPrevPos.X, FPrevPos.Y, AX, AY);
311 FPrevPos := Point(AX, AY);
312 end;
313
314 procedure TSVGDrawer.MoveTo(AX, AY: Integer);
315 begin
316 FPrevPos := Point(AX, AY);
317 end;
318
OpacityStrnull319 function TSVGDrawer.OpacityStr: String;
320 begin
321 if FTransparency = 0 then
322 Result := ''
323 else
324 Result := F2S((255 - FTransparency) / 256);
325 end;
326
PointsToStrnull327 function TSVGDrawer.PointsToStr(
328 const APoints: array of TPoint; AStartIndex, ANumPts: Integer): String;
329 var
330 i: Integer;
331 begin
332 if ANumPts < 0 then
333 ANumPts := Length(APoints) - AStartIndex;
334 Result := '';
335 for i := 0 to ANumPts - 1 do
336 with APoints[i + AStartIndex] do
337 Result += Format('%d %d ', [X, Y]);
338 end;
339
340 procedure TSVGDrawer.Polygon(
341 const APoints: array of TPoint; AStartIndex, ANumPts: Integer);
342 begin
343 WriteFmt(
344 '<polygon points="%s" style="%s"/>',
345 [PointsToStr(APoints, AStartIndex, ANumPts), StyleFill + StyleStroke]);
346 end;
347
348 procedure TSVGDrawer.Polyline(
349 const APoints: array of TPoint; AStartIndex, ANumPts: Integer);
350 begin
351 WriteFmt(
352 '<polyline points="%s" style="fill: none; %s"/>',
353 [PointsToStr(APoints, AStartIndex, ANumPts), StyleStroke]);
354 end;
355
356 procedure TSVGDrawer.PrepareSimplePen(AColor: TChartColor);
357 begin
358 FPen.FPColor := FChartColorToFPColorFunc(ColorOrMono(AColor));
359 FPen.Style := psSolid;
360 FPen.Width := 1;
361 end;
362
363 procedure TSVGDrawer.PutImage(AX, AY: Integer; AImage: TFPCustomImage);
364 var
365 s: TStringStream = nil;
366 w: TFPWriterPNG = nil;
367 b: TBase64EncodingStream = nil;
368 begin
369 s := TStringStream.Create('');
370 b := TBase64EncodingStream.Create(s);
371 w := TFPWriterPNG.Create;
372 try
373 w.Indexed := false;
374 w.UseAlpha := true;
375 AImage.SaveToStream(b, w);
376 b.Flush;
377 WriteFmt(
378 '<image x="%d" y="%d" width="%d" height="%d" ' +
379 'xlink:href="data:image/png;base64,%s"/>',
380 [AX, AY, AImage.Width, AImage.Height, s.DataString]);
381 finally
382 w.Free;
383 s.Free;
384 b.Free;
385 end;
386 end;
387
388 procedure TSVGDrawer.PutPixel(AX, AY: Integer; AColor: TChartColor);
389 var
390 stroke: String;
391 begin
392 stroke := 'stroke:'+ColorToHex(FChartColorToFPColorFunc(ColorOrMono(AColor))) + ';stroke-width:1;';
393 WriteFmt(RECT_FMT, [AX, AY, 1, 1, stroke]);
394 end;
395
396 procedure TSVGDrawer.RadialPie(
397 AX1, AY1, AX2, AY2: Integer; AStartAngle16Deg, AAngleLength16Deg: Integer);
398 var
399 e: TEllipse;
400 p1, p2: TDoublePoint;
401 begin
402 e.InitBoundingBox(AX1, AY1, AX2, AY2);
403 p1 := e.GetPoint(Deg16ToRad(AStartAngle16Deg));
404 p2 := e.GetPoint(Deg16ToRad(AStartAngle16Deg + AAngleLength16Deg));
405 WriteFmt(
406 '<path d="M%s L%s A%s 0 0,0 %s Z" style="%s"/>',
407 [DP2S(e.FC), DP2S(p1), DP2S(e.FR), DP2S(p2), StyleFill + StyleStroke]);
408 end;
409
410 procedure TSVGDrawer.Rectangle(AX1, AY1, AX2, AY2: Integer);
411 begin
412 WriteFmt(
413 RECT_FMT, [AX1, AY1, AX2 - AX1, AY2 - AY1, StyleFill + StyleStroke]);
414 end;
415
416 procedure TSVGDrawer.Rectangle(const ARect: TRect);
417 begin
418 with ARect do
419 Rectangle(Left, Top, Right, Bottom);
420 end;
421
422 procedure TSVGDrawer.ResetFont;
423 begin
424 FFontOrientation := 0;
425 end;
426
427 procedure TSVGDrawer.SetAntialiasingMode(AValue: TChartAntialiasingMode);
428 const
429 AM_TO_CSS: array [amOn .. amOff] of String =
430 ('geometricPrecision', 'crispEdges');
431 begin
432 if FAntialiasingMode = AValue then exit;
433 if FAntialiasingMode <> amDontCare then
434 WriteStr('</g>');
435 FAntialiasingMode := AValue;
436 if FAntialiasingMode <> amDontCare then
437 WriteFmt('<g style="shape-rendering: %s">',[AM_TO_CSS[FAntialiasingMode]]);
438 end;
439
440 procedure TSVGDrawer.SetBrush(ABrush: TFPCustomBrush);
441 begin
442 if ABrush is TBrush then
443 FBrushColor := FChartColorToFPColorFunc(ColorOrMono(TBrush(ABrush).Color))
444 else
445 FBrushColor := FPColorOrMono(ABrush.FPColor);
446 FBrushStyle := ABrush.Style;
447 end;
448
449 procedure TSVGDrawer.SetBrushColor(AColor: TChartColor);
450 begin
451 FBrushColor := FChartColorToFPColorFunc(ColorOrMono(AColor));
452 end;
453
454 procedure TSVGDrawer.SetBrushParams(
455 AStyle: TFPBrushStyle; AColor: TChartColor);
456 begin
457 FBrushColor := FChartColorToFPColorFunc(ColorOrMono(AColor));
458 FBrushStyle := AStyle;
459 end;
460
461 procedure TSVGDrawer.SetFont(AFont: TFPCustomFont);
462 var
463 style: TFreeTypeStyles;
464 fn: String;
465 begin
466 style := [];
467 if AFont.Bold then Include(style, ftsBold);
468 if AFont.Italic then Include(style, ftsItalic);
469
470 // create a new font if not yet loaded
471 if (FFont = nil) or (FFont.Family <> AFont.Name) or(FFont.Style <> style) then
472 begin
473 FreeAndNil(FFont);
474 if SameText(AFont.Name, 'default') then
475 fn := 'Arial' // FIXME: Find font in FontCollection!
476 else
477 fn := AFont.Name;
478 FFont := LoadFont(fn, style);
479 if FFont = nil then
480 raise Exception.CreateFmt('Font "%s" not found."', [AFont.Name]);
481 end;
482
483 // Set the requested font attributes
484 FFont.SizeInPoints := IfThen(AFont.Size = 0, DEFAULT_FONT_SIZE, AFont.Size);
485 FFont.UnderlineDecoration := AFont.Underline;
486 FFont.StrikeoutDecoration := AFont.StrikeThrough;
487 FFont.Hinted := true;
488 FFont.Quality := grqHighQuality;
489
490 if FMonochromeColor <> clTAColor then
491 FFontColor := FChartColorToFPColorFunc(FMonochromeColor)
492 else
493 FFontColor := AFont.FPColor;
494 FFontOrientation := FGetFontOrientationFunc(AFont);
495 FFontHeight := round(FFont.TextHeight('Tg'));
496 end;
497
498 procedure TSVGDrawer.SetPen(APen: TFPCustomPen);
499 begin
500 if APen is TPen then
501 FPen.FPColor := FChartColorToFPColorFunc(ColorOrMono(TPen(APen).Color))
502 else
503 FPen.FPColor := FPColorOrMono(APen.FPColor);
504 FPen.Style := APen.Style;
505 FPen.Width := APen.Width;
506 end;
507
508 procedure TSVGDrawer.SetPenParams(AStyle: TFPPenStyle; AColor: TChartColor);
509 begin
510 FPen.FPColor := FChartColorToFPColorFunc(ColorOrMono(AColor));
511 FPen.Style := AStyle;
512 end;
513
TSVGDrawer.SimpleTextExtentnull514 function TSVGDrawer.SimpleTextExtent(const AText: String): TPoint;
515 begin
516 Result.X := Round(FFont.TextWidth(AText));
517 Result.Y := FFontHeight;
518 end;
519
520 type
521 TFreeTypeFontOpener = class(TFreeTypeFont);
522
523 procedure TSVGDrawer.SimpleTextOut(AX, AY: Integer; const AText: String);
524 var
525 p: TPoint;
526 stext: String;
527 sstyle: String;
528 dy: Integer;
529 phi: Double;
530 begin
531 dy := round(TFreeTypeFontOpener(FFont).GetAscent);
532 phi := OrientToRad(FFontOrientation);
533 p := RotatePoint(Point(0, dy), -phi) + Point(AX, AY);
534 stext := Format('x="%d" y="%d"', [p.X, p.Y]);
535 if FFontOrientation <> 0 then
536 stext := stext + Format(' transform="rotate(%g,%d,%d)"',
537 [-FFontOrientation*0.1, p.X, p.Y], fmtSettings);
538
539 sstyle := Format('fill:%s; font-family:''%s''; font-size:%dpt;',
540 [ColorToHex(GetFontColor), GetFontName, round(FFont.SizeInPoints)]);
541 if (ftsBold in FFont.Style) then
542 sstyle := sstyle + ' font-weight:bold;';
543 if (ftsItalic in FFont.Style) then
544 sstyle := sstyle + ' font-style:oblique;';
545 if FFont.UnderlineDecoration and FFont.StrikeoutDecoration then
546 sstyle := sstyle + ' text-decoration:underline,line-through;'
547 else if FFont.UnderlineDecoration then
548 sstyle := sstyle + ' text-deocration:underline;'
549 else if FFont.StrikeoutDecoration then
550 sstyle := sstyle + ' text-decoration:line-through;';
551 if OpacityStr <> '' then
552 sstyle := sstyle + OpacityStr + ';';
553
554 WriteFmt('<text %s style="%s">%s</text>', [stext, sstyle, EscapeXML(AText)]);
555 end;
556
StyleFillnull557 function TSVGDrawer.StyleFill: String;
558
AddPatternnull559 function AddPattern(APattern: String): String;
560 var
561 i: Integer;
562 begin
563 i := FPatterns.IndexOf(APattern);
564 if i < 0 then
565 i := FPatterns.Add(APattern);
566 Result := Format('url(#bs%d)', [i]);
567 end;
568
569 const
570 PATTERNS: array [TFPBrushStyle] of String = (
571 '', '',
572 'M0,4 h8', // bsHorizontal
573 'M4,0 v8', // bsVertical
574 'M0,0 l8,8', // bsFDiagonal
575 'M0,8 l8,-8', // bsBDiagonal
576 'M0,4 h8 M4,0 v8', // bsCross
577 'M0,0 l8,8 M0,8 l8,-8', // bsDiagCross
578 '', '');
579 var
580 fill: String;
581 begin
582 case FBrushStyle of
583 bsClear: exit('fill: none;');
584 bsHorizontal..bsDiagCross:
585 fill := AddPattern(Format(
586 '<path d="%s" stroke="%s"/>',
587 [PATTERNS[FBrushStyle], ColorToHex(FBrushColor)]));
588 else
589 fill := ColorToHex(FBrushColor);
590 end;
591 Result :=
592 Format('fill:%s;', [fill]) + FormatIfNotEmpty('fill-opacity:%s;', OpacityStr);
593 end;
594
TSVGDrawer.StyleStrokenull595 function TSVGDrawer.StyleStroke: String;
596 const
597 PEN_DASHARRAY: array [TFPPenStyle] of String =
598 ('', '2,2', '1,1', '2,1,1,1', '2,1,1,1,1,1', '', '', '');
599 begin
600 if FPen.Style = psClear then
601 exit('stroke: none');
602 Result := 'stroke:' + ColorToHex(FPen.FPColor) + ';';
603 if FPen.Width <> 1 then
604 Result += 'stroke-width:' + IntToStr(FPen.Width) + ';';
605 Result +=
606 FormatIfNotEmpty('stroke-dasharray:%s;', PEN_DASHARRAY[FPen.Style]) +
607 FormatIfNotEmpty('stroke-opacity:%s;', OpacityStr);
608 end;
609
610 procedure TSVGDrawer.WriteFmt(const AFormat: String; AParams: array of const);
611 begin
612 WriteStr(Format(AFormat, AParams, fmtSettings));
613 end;
614
615 procedure TSVGDrawer.WriteStr(const AString: String);
616 var
617 le: String = LineEnding;
618 begin
619 FStream.WriteBuffer(AString[1], Length(AString));
620 FStream.WriteBuffer(le[1], Length(le));
621 end;
622
623
624 { TSVGChartHelper }
625
626 procedure TSVGChartHelper.SaveToSVGFile(const AFileName: String);
627 var
628 fs: TFileStream;
629 begin
630 fs := TFileStream.Create(AFileName, fmCreate);
631 try
632 Draw(TSVGDrawer.Create(fs, true), Rect(0, 0, Width, Height));
633 finally
634 fs.Free;
635 end;
636 end;
637
638
639 initialization
640 fmtSettings := DefaultFormatSettings;
641 fmtSettings.DecimalSeparator := '.';
642
643 end.
644
645