1 { Current issues:
2   - Radial gradient not rendered correctly (position, colors), saving to svg ok.
3   - Save polygon to svg empty
4   - Nonzero/even-odd winding rule not working
5 }
6 
7 unit vtmain;
8 
9 {$mode objfpc}{$H+}
10 
11 interface
12 
13 uses
14   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
15   ExtCtrls, ComCtrls, EditBtn, fpimage, fpvectorial, Types;
16 
17 type
18 
19   TRenderEvent = procedure(APage: TvVectorialPage;
20     AIntParam: Integer = MaxInt) of object;
21 
22   TRenderParams = class
23     RefFile: String;
24     IntParam: Integer;
25     OnRender: TRenderEvent;
26     constructor Create(ARenderEvent: TRenderEvent; ARefFilename: String;
27       AIntParam: Integer = MaxInt);
28   end;
29 
30   TRenderCoords  = (rcBottomLeftCoords, rcTopLeftCoords);
31 
32   { TMainForm }
33 
34   TMainForm = class(TForm)
35     BtnSaveAsRef: TButton;
36     BtnSaveToFiles: TButton;
37     BtnViewBottomLeft: TButton;
38     BtnViewTopLeft: TButton;
39     CbFileFormat: TComboBox;
40     gbWRBottomLeft: TGroupBox;
41     gbRenderTest: TGroupBox;
42     gbBottomLeft: TGroupBox;
43     gbWRTopLeft: TGroupBox;
44     gbTopLeft: TGroupBox;
45     gbReferenceImageTest: TGroupBox;
46     GroupBox1: TGroupBox;
47     gbReadWriteTest: TGroupBox;
48     GbTree: TGroupBox;
49     Label1: TLabel;
50     Label14: TLabel;
51     LblBothImagesMustMatch1: TLabel;
52     RefImage: TImage;
53     Label10: TLabel;
54     Label11: TLabel;
55     Label13: TLabel;
56     Label6: TLabel;
57     Label7: TLabel;
58     Label8: TLabel;
59     LblBothImagesMustMatch: TLabel;
60     LblRefImgMustMatch: TLabel;
61     LblReadWriteInstructions: TLabel;
62     BottomLeftPaintbox: TPaintBox;
63     ScrollBox1: TScrollBox;
64     WRTopLeftPaintbox: TPaintBox;
65     TopLeftPaintbox: TPaintBox;
66     WRBottomLeftPaintbox: TPaintBox;
67     AllTestsPanel: TPanel;
68     Tree: TTreeView;
69     procedure BtnSaveToFilesClick(Sender: TObject);
70     procedure BtnSaveAsRefClick(Sender: TObject);
71     procedure BtnViewImageClick(Sender: TObject);
72     procedure CbFileFormatChange(Sender: TObject);
73     procedure FormCreate(Sender: TObject);
74     procedure FormDestroy(Sender: TObject);
75     procedure PaintBoxPaint(Sender: TObject);
76     procedure TreeCustomDrawItem(Sender: TCustomTreeView; Node: TTreeNode;
77       State: TCustomDrawState; var DefaultDraw: Boolean);
78     procedure TreeSelectionChanged(Sender: TObject);
79 
80   private
81     { private declarations }
82     FDoc: array[TRenderCoords] of TvVectorialDocument;
83     FDocFromWMF: array[TRenderCoords] of TvVectorialDocument;
84     FDocFromSVG: array[TRenderCoords] of TvVectorialDocument;
GetFileFormatnull85     function GetFileFormat: TvVectorialFormat;
GetFileFormatExtnull86     function GetFileFormatExt: String;
87     procedure Populate;
88     procedure PrepareDoc(var ADoc: TvVectorialDocument; var APage: TvVectorialPage;
89       AUseTopLeftCoords: boolean);
90     procedure ReadIni;
91     procedure ShowFileImage(AFilename: String; AUseTopLeftCoords: Boolean;
92       APaintbox: TPaintbox);
93     procedure ShowRefImageTest;
94     procedure ShowRenderTestImages;
95     procedure ShowWriteReadTestImages;
96     procedure UpdateCmdStates;
97     procedure WriteIni;
98 
99     // Simple shapes, solid fills and gradients
100     procedure Render_Shape(APage: TvVectorialPage; AIntParam: Integer);
101 
102     // Complex shapes
103     procedure Render_Path_Hole_SolidFill(APage: TvVectorialPage;
104       AIntParam: Integer);
105     procedure Render_Path_Hole_GradientFill(APage: TvVectorialPage;
106       AIntParam: Integer);
107     procedure Render_SelfIntersectingPoly_SolidFill_EvenOdd(APage: TvVectorialPage;
108       AIntParam: Integer);
109     procedure Render_SelfIntersectingPoly_GradientFill_EvenOdd(APage: TvVectorialPage;
110       AIntParam: Integer);
111     procedure Render_SelfIntersectingPoly_SolidFill_NonZeroWinding(APage: TvVectorialPage;
112       AIntParam: Integer);
113     procedure Render_SelfIntersectingPoly_GradientFill_NonZeroWinding(APage: TvVectorialPage;
114       AIntParam: Integer);
115 
116     // Arcs
117     procedure Render_Arc(APage: TvVectorialPage; AIntParam: Integer);
118 
119     // Bezier
120     procedure Render_Bezier(Apage: TvVectorialPage; AIntParam: Integer);
121 
122     // Text
123     procedure Render_Text(APage: TvVectorialpage; AIntParam: Integer);
124     procedure Render_Text_Fonts(APage: TvVectorialPage; AIntParam: Integer);
125     procedure Render_Text_Colors(APage: TvVectorialPage; AIntParam: Integer);
126   public
127     { public declarations }
128   end;
129 
130 var
131   MainForm: TMainForm;
132 
133 
134 implementation
135 
136 {$R *.lfm}
137 
138 uses
139   Math, FPCanvas, IniFiles, LazFileUtils, LCLIntf,
140   fpvutils, vtprimitives;
141 
142 const
143   IMG_FOLDER = 'images' + PathDelim;
144   NOT_SAVED = '(not saved)';
145 
146 
147 { TRenderParams }
148 
149 constructor TRenderParams.Create(ARenderEvent: TRenderEvent;
150   ARefFilename: String; AIntParam: Integer = MaxInt);
151 begin
152   OnRender := ARenderEvent;
153   RefFile := ARefFileName;
154   IntParam := AIntParam;
155 end;
156 
157 
158 { TMainForm }
159 
160 procedure TMainForm.BtnSaveAsRefClick(Sender: TObject);
161 var
162   bmp: TBitmap;
163   png: TPortableNetworkGraphic;
164   fn: String;
165   renderParams: TRenderParams;
166   page: TvVectorialPage;
167 begin
168   renderParams := TRenderParams(Tree.Selected.Data);
169   if RenderParams = nil then
170     exit;
171   if FDoc[rcBottomLeftCoords] = nil then
172     exit;
173 
174   page := FDoc[rcBottomLeftCoords].GetPageAsVectorial(0);
175 
176   bmp := TBitmap.Create;
177   try
178     bmp.SetSize(BottomLeftPaintbox.Width, BottomLeftPaintbox.Height);
179     bmp.Canvas.GetUpdatedHandle([csHandleValid]);  // create the Handle needed by next line
180     page.DrawBackground(bmp.Canvas);
181     // bmp canvas has origin at top/left
182     page.Render(bmp.Canvas, 0, bmp.Height, 1.0, -1.0);
183     png := TPortableNetworkGraphic.Create;
184     try
185       png.Assign(bmp);
186      // renderParams := TRenderParams(Tree.Selected.Data);
187       ForceDirectory(IMG_FOLDER);
188       fn := IncludeTrailingPathDelimiter(IMG_FOLDER) + renderParams.RefFile;
189       png.SaveToFile(fn);
190     finally
191       png.Free;
192     end;
193     RefImage.Picture.Assign(bmp);
194     RefImage.Hint := fn;
195   finally
196     bmp.Free;
197   end;
198 end;
199 
200 procedure TMainForm.BtnSaveToFilesClick(Sender: TObject);
201 var
202   fn: String;
203   renderParams: TRenderParams;
204   folder: String;
205   fmt: TvVectorialFormat;
206   ext: String;
207 begin
208   renderParams := TRenderParams(Tree.Selected.Data);
209   if RenderParams = nil then
210     exit;
211 
212   fmt := GetFileFormat;
213   ext := GetFileFormatExt;
214   folder := IMG_FOLDER + ext + PathDelim;
215   ForceDirectory(folder);
216 
217   if FDoc[rcBottomLeftCoords] <> nil then begin
218     fn := folder + 'bl_' + ChangeFileExt(renderParams.RefFile, '.' + ext);
219     FDoc[rcBottomLeftCoords].WriteToFile(fn, fmt);
220     ShowFileImage(fn, false, WRBottomLeftPaintbox);
221   end;
222 
223   if FDoc[rcTopLeftCoords] <> nil then begin
224     fn := folder + 'tl_' + ChangeFileExt(renderParams.RefFile, '.' + ext);
225     FDoc[rcTopLeftCoords].WriteToFile(fn, fmt);
226     ShowFileImage(fn, true, WRTopLeftPaintbox);
227   end;
228 
229   UpdateCmdStates;
230 end;
231 
232 procedure TMainForm.BtnViewImageClick(Sender: TObject);
233 var
234   fn: String;
235   ext: String;
236   folder: String;
237   renderParams: TRenderParams;
238 begin
239   BtnSaveToFilesClick(nil);
240 
241   renderParams := TRenderParams(Tree.Selected.Data);
242   if renderParams = nil then
243     exit;
244 
245   ext := GetFileFormatExt;
246   folder := IMG_FOLDER + ext + PathDelim;
247 
248   if Sender = BtnViewBottomLeft then
249     fn := folder + 'bl_' + ChangeFileExt(renderParams.RefFile, '.' + ext)
250   else if Sender = BtnViewTopLeft then
251     fn := folder + 'tl_' + ChangeFileExt(renderParams.RefFile, '.' + ext)
252   else
253     raise Exception.Create('BtnViewImageClick: this sender is not supported.');
254 
255   if FileExists(fn) then
256     OpenDocument(fn);
257 end;
258 
259 procedure TMainForm.CbFileFormatChange(Sender: TObject);
260 begin
261   ShowWriteReadTestImages;
262   UpdateCmdStates;
263 end;
264 
265 procedure TMainForm.PrepareDoc(var ADoc: TvVectorialDocument;
266   var APage: TvVectorialPage; AUseTopLeftCoords: boolean);
267 var
268   r: TvRectangle;
269 begin
270   FreeAndNil(ADoc);
271   ADoc := TvVectorialDocument.Create;
272   APage := ADoc.AddPage;
273   APage.BackgroundColor := colWhite;
274   APage.Width := PAGE_SIZE;
275   APage.Height := PAGE_SIZE;
276   ADoc.Width := PAGE_SIZE;
277   ADoc.Height := PAGE_SIZE;
278   APage.UseTopLeftCoordinates := AUseTopLeftCoords;
279 
280   // Add a frame around the page
281   r := TvRectangle.Create(APage);
282   r.X := 0;
283   if AUseTopLeftCoords then
284     r.Y := 0
285   else
286     r.Y := APage.Height;
287   r.CX := APage.Width - 1;
288   r.CY := APage.Height - 1;
289   r.Brush := CreateSimpleBrush(bsClear);
290   r.Pen := CreatePen(psSolid, 1, colSilver);
291   APage.AddEntity(r);
292 end;
293 
294 procedure TMainForm.FormCreate(Sender: TObject);
295 begin
296   RefImage.Hint := NOT_SAVED;
297   WRBottomLeftPaintbox.Hint := NOT_SAVED;
298   WRTopLeftPaintbox.Hint := NOT_SAVED;
299 
300   ReadIni;
301   Populate;
302   TreeSelectionChanged(nil);
303 end;
304 
305 procedure TMainForm.FormDestroy(Sender: TObject);
306 var
307   parentnode, node: TTreeNode;
308   rc: TRenderCoords;
309 begin
310   parentnode := Tree.Items.GetFirstNode;
311   while parentnode <> nil do begin
312     node := parentnode.GetFirstChild;
313     while node <> nil do begin
314       TObject(node.Data).Free;
315       node := node.GetNextSibling;
316     end;
317     parentnode := parentnode.GetNextSibling;
318   end;
319 
320   for rc in TRenderCoords do begin
321     FreeAndNil(FDoc[rc]);
322     FreeAndNil(FDocFromSVG[rc]);
323     FreeAndNil(FDocFromWMF[rc]);
324   end;
325 
326   WriteIni;
327 end;
328 
TMainForm.GetFileFormatnull329 function TMainForm.GetFileFormat: TvVectorialFormat;
330 begin
331   case CbFileFormat.ItemIndex of
332     0: Result := vfSVG;
333     1: Result := vfWindowsMetafileWMF;
334     else raise Exception.Create('Format not supported');
335   end;
336 end;
337 
TMainForm.GetFileFormatExtnull338 function TMainForm.GetFileFormatExt: String;
339 begin
340   case CbFileFormat.ItemIndex of
341     0: Result := 'svg';
342     1: Result := 'wmf';
343     else raise Exception.Create('Format not supported');
344   end;
345 end;
346 
347 procedure TMainForm.PaintBoxPaint(Sender: TObject);
348 var
349   doc: TvVectorialDocument;
350   page: TvVectorialPage;
351   w, h: Integer;
352   fmt: TvVectorialFormat;
353   rc: TRenderCoords;
354 begin
355   fmt := GetFileFormat;
356 
357   if (Sender = BottomLeftPaintbox) or (Sender = WRBottomLeftPaintbox) then
358     rc := rcBottomLeftCoords
359   else
360   if (Sender = TopLeftPaintbox) or (Sender = WRTopLeftPaintbox) then
361     rc := rcTopLeftCoords
362   else
363     raise Exception.Create('This sender is not supported here.');
364 
365   doc := nil;
366   if (Sender = BottomLeftPaintbox) or (Sender = TopLeftPaintbox) then
367     doc := FDoc[rc]
368   else
369   if (Sender = WRBottomLeftPaintbox) or (Sender = WRTopLeftPaintbox) then
370     case GetFileFormat of
371       vfSVG:
372         doc := FDocFromSVG[rc];
373       vfWindowsMetafileWMF:
374         doc := FDocFromWMF[rc];
375       else
376         raise Exception.Create('File format not supported.');
377     end;
378 
379   w := TPaintbox(Sender).Width;
380   h := TPaintbox(Sender).Height;
381 
382   if doc = nil then begin
383     TPaintbox(Sender).Canvas.Brush.Color := clDefault;
384     TPaintbox(Sender).Canvas.Brush.Style := bsSolid;
385     TPaintbox(Sender).Canvas.FillRect(0, 0, w, h);
386     exit;
387   end;
388 
389   page := doc.GetPageAsVectorial(0);
390   page.DrawBackground(TPaintbox(Sender).Canvas);
391   if page.UseTopLeftCoordinates then
392     page.Render(TPaintbox(Sender).Canvas, 0, 0, 1.0, 1.0)
393   else
394     page.Render(TPaintbox(Sender).Canvas, 0, h, 1.0, -1.0);
395 end;
396 
397 procedure TMainForm.Populate;
398 var
399   node, node0, node1: TTreeNode;
400 begin
401   Tree.Items.Clear;
402 
403   { --------------------------------------------------}
404   node := Tree.Items.AddChild(nil, 'Simple shapes');
405   { --------------------------------------------------}
406   Tree.Items.AddChildObject(node, 'Circle (solid) - move up',
407     TRenderParams.Create(@Render_Shape, 'circle_solid.png', $0100));
408   Tree.Items.AddChildObject(node, 'Ellipse (solid) - moved up',
409     TRenderParams.Create(@Render_Shape, 'ellipse_solid.png', $0200));
410   Tree.Items.AddChildObject(node, 'Rectangle(solid) - moved up',
411     TRenderParams.Create(@Render_Shape, 'rect_solid.png', $0300));
412   Tree.Items.AddChildObject(node, 'Rounded rectangle (solid) - moved up',
413     TRenderParams.Create(@Render_Shape, 'rounded_rect_solid.png', $0400));
414   Tree.Items.AddChildObject(node, 'Polygon (solid) - moved up',
415     TRenderParams.Create(@Render_Shape, 'polygon_solid.png', $0500));
416 
417   { --------------------------------------------------}
418   node := Tree.Items.AddChild(nil, 'Complex shapes');
419   { --------------------------------------------------}
420   Tree.Items.AddChildObject(node, 'Path with hole (solid fill)',
421     TRenderParams.Create(@Render_Path_Hole_SolidFill, 'path_hole_solid.png'));
422   Tree.Items.AddChildObject(node, 'Path with hole (gradient fill)',
423     TRenderParams.Create(@Render_Path_Hole_GradientFill, 'path_hole_gradient.png'));
424   Tree.Items.AddChildObject(node, 'Self-intersecting polygon (solid fill, even-odd rule) - tip at bottom',
425     TRenderParams.Create(@Render_SelfIntersectingPoly_SolidFill_EvenOdd, 'selfintersecting_poly_solid_eo.png'));
426   Tree.Items.AddChildObject(node, 'Self-intersecting polygon (gradient fill, even-odd rule) - tip at bottom',
427     TRenderParams.Create(@Render_SelfIntersectingPoly_GradientFill_EvenOdd, 'selfintersecting_poly_gradient_eo.png'));
428   Tree.Items.AddChildObject(node, 'Self-intersecting polygon (solid fill, nonzero winding rule) - tip at bottom',
429     TRenderParams.Create(@Render_SelfIntersectingPoly_SolidFill_NonZeroWinding, 'selfintersecting_poly_solid_nzw.png'));
430   Tree.Items.AddChildObject(node, 'Self-intersecting polygon (gradient fill, nonzero winding rule) - tip at bottom',
431     TRenderParams.Create(@Render_SelfIntersectingPoly_GradientFill_NonZeroWinding, 'selfintersecting_poly_gradient_nzw.png'));
432 
433   { -----------------------------------------}
434   node0 := Tree.Items.AddChild(nil, 'Arcs');
435   { -----------------------------------------}
436   node1 := Tree.Items.AddChild(node0, 'circular');
437   node := Tree.Items.AddChild(node1, 'clockwise from point 1 to point 2');
438   Tree.Items.AddChildObject(node, 'Quadrant I',
439     TRenderParams.Create(@Render_Arc, 'arc_cw_q1.png', $0200));
440   Tree.Items.AddChildObject(node, 'Quadrant I+II',
441     TRenderParams.Create(@Render_Arc, 'arc_cw_q12.png', $0201));
442   Tree.Items.AddChildObject(node, 'Quadrant II',
443     TRenderParams.Create(@Render_Arc, 'arc_cw_q2.png', $0202));
444   Tree.Items.AddChildObject(node, 'Quadrant II+III',
445     TRenderParams.Create(@Render_Arc, 'arc_cw_q23.png', $0203));
446   Tree.Items.AddChildObject(node, 'Quadrant III',
447     TRenderParams.Create(@Render_Arc, 'arc_cw_q3.png', $0204));
448   Tree.Items.AddChildObject(node, 'Quadrant III+IV',
449     TRenderParams.Create(@Render_Arc, 'arc_cw_q34.png', $0205));
450   Tree.Items.AddChildObject(node, 'Quadrant IV',
451     TRenderParams.Create(@Render_Arc, 'arc_cw_q4.png', $0206));
452   Tree.Items.AddChildObject(node, 'Quadrant IV+I',
453     TRenderParams.Create(@Render_Arc, 'arc_cw_q41.png', $0207));
454 
455   Tree.Items.AddChildObject(node, 'Quadrant I, reverse',
456     TRenderParams.Create(@Render_Arc, 'arc_cw_q1r.png', $0300));
457   Tree.Items.AddChildObject(node, 'Quadrant I+II, reverse',
458     TRenderParams.Create(@Render_Arc, 'arc_cw_q12r.png', $0301));
459   Tree.Items.AddChildObject(node, 'Quadrant II, reverse',
460     TRenderParams.Create(@Render_Arc, 'arc_cw_q2r.png', $0302));
461   Tree.Items.AddChildObject(node, 'Quadrant II+III, reverse',
462     TRenderParams.Create(@Render_Arc, 'arc_cw_q23r.png', $0303));
463   Tree.Items.AddChildObject(node, 'Quadrant III, reverse',
464     TRenderParams.Create(@Render_Arc, 'arc_cw_q3r.png', $0304));
465   Tree.Items.AddChildObject(node, 'Quadrant III+IV, reverse',
466     TRenderParams.Create(@Render_Arc, 'arc_cw_q34r.png', $0305));
467   Tree.Items.AddChildObject(node, 'Quadrant IV, reverse',
468     TRenderParams.Create(@Render_Arc, 'arc_cw_q4r.png', $0306));
469   Tree.Items.AddChildObject(node, 'Quadrant IV+I, reverse',
470     TRenderParams.Create(@Render_Arc, 'arc_cw_q41r.png', $0307));
471 
472   node := Tree.Items.AddChild(node1, 'counter-clockwise from point 1 to point 2');
473   Tree.Items.AddChildObject(node, 'Quadrant I',
474     TRenderParams.Create(@Render_Arc, 'arc_ccw_q1.png', $0000));
475   Tree.Items.AddChildObject(node, 'Quadrant I+II',
476     TRenderParams.Create(@Render_Arc, 'arc_ccw_q12.png', $0001));
477   Tree.Items.AddChildObject(node, 'Quadrant II',
478     TRenderParams.Create(@Render_Arc, 'arc_ccw_q2.png', $0002));
479   Tree.Items.AddChildObject(node, 'Quadrant II+III',
480     TRenderParams.Create(@Render_Arc, 'arc_ccw_q23.png', $0003));
481   Tree.Items.AddChildObject(node, 'Quadrant III',
482     TRenderParams.Create(@Render_Arc, 'arc_ccw_q3.png', $0004));
483   Tree.Items.AddChildObject(node, 'Quadrant III+IV',
484     TRenderParams.Create(@Render_Arc, 'arc_ccw_q34.png', $0005));
485   Tree.Items.AddChildObject(node, 'Quadrant IV',
486     TRenderParams.Create(@Render_Arc, 'arc_ccw_q4.png', $0006));
487   Tree.Items.AddChildObject(node, 'Quadrant IV+I',
488     TRenderParams.Create(@Render_Arc, 'arc_ccw_q41.png', $0007));
489 
490   Tree.Items.AddChildObject(node, 'Quadrant I, reverse',
491     TRenderParams.Create(@Render_Arc, 'arc_ccw_q1r.png', $0100));
492   Tree.Items.AddChildObject(node, 'Quadrant I+II, reverse',
493     TRenderParams.Create(@Render_Arc, 'arc_ccw_q12r.png', $0101));
494   Tree.Items.AddChildObject(node, 'Quadrant II, reverse',
495     TRenderParams.Create(@Render_Arc, 'arc_ccw_q2r.png', $0102));
496   Tree.Items.AddChildObject(node, 'Quadrant II+III, reverse',
497     TRenderParams.Create(@Render_Arc, 'arc_ccw_q23r.png', $0103));
498   Tree.Items.AddChildObject(node, 'Quadrant III, reverse',
499     TRenderParams.Create(@Render_Arc, 'arc_ccw_q3r.png', $0104));
500   Tree.Items.AddChildObject(node, 'Quadrant III+IV, reverse',
501     TRenderParams.Create(@Render_Arc, 'arc_ccw_q34r.png', $0105));
502   Tree.Items.AddChildObject(node, 'Quadrant IV, reverse',
503     TRenderParams.Create(@Render_Arc, 'arc_ccw_q4r.png', $0106));
504   Tree.Items.AddChildObject(node, 'Quadrant IV+I, reverse',
505     TRenderParams.Create(@Render_Arc, 'arc_ccw_q41r.png', $0107));
506 
507   node1 := Tree.Items.AddChild(node0, 'elliptical');
508   node := Tree.Items.AddChild(node1, 'clockwise from point 1 to point 2');
509   Tree.Items.AddChildObject(node, 'Quadrant I',
510     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q1.png', $1200));
511   Tree.Items.AddChildObject(node, 'Quadrant I+II',
512     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q12.png', $1201));
513   Tree.Items.AddChildObject(node, 'Quadrant II',
514     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q2.png', $1202));
515   Tree.Items.AddChildObject(node, 'Quadrant II+III',
516     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q23.png', $1203));
517   Tree.Items.AddChildObject(node, 'Quadrant III',
518     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q3.png', $1204));
519   Tree.Items.AddChildObject(node, 'Quadrant III+IV',
520     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q34.png', $1205));
521   Tree.Items.AddChildObject(node, 'Quadrant IV',
522     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q4.png', $1206));
523   Tree.Items.AddChildObject(node, 'Quadrant IV+I',
524     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q41.png', $1207));
525 
526   Tree.Items.AddChildObject(node, 'Quadrant I, reverse',
527     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q1r.png', $1300));
528   Tree.Items.AddChildObject(node, 'Quadrant I+II, reverse',
529     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q12r.png', $1301));
530   Tree.Items.AddChildObject(node, 'Quadrant II, reverse',
531     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q2r.png', $1302));
532   Tree.Items.AddChildObject(node, 'Quadrant II+III, reverse',
533     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q23r.png', $1303));
534   Tree.Items.AddChildObject(node, 'Quadrant III, reverse',
535     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q3r.png', $1304));
536   Tree.Items.AddChildObject(node, 'Quadrant III+IV, reverse',
537     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q34r.png', $1305));
538   Tree.Items.AddChildObject(node, 'Quadrant IV, reverse',
539     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q4r.png', $1306));
540   Tree.Items.AddChildObject(node, 'Quadrant IV+I, reverse',
541     TRenderParams.Create(@Render_Arc, 'arc_ell_cw_q41r.png', $1307));
542 
543   node := Tree.Items.AddChild(node1, 'counter-clockwise from point 1 to point 2');
544   Tree.Items.AddChildObject(node, 'Quadrant I',
545     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q1.png', $1000));
546   Tree.Items.AddChildObject(node, 'Quadrant I+II',
547     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q12.png', $1001));
548   Tree.Items.AddChildObject(node, 'Quadrant II',
549     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q2.png', $1002));
550   Tree.Items.AddChildObject(node, 'Quadrant II+III',
551     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q23.png', $1003));
552   Tree.Items.AddChildObject(node, 'Quadrant III',
553     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q3.png', $1004));
554   Tree.Items.AddChildObject(node, 'Quadrant III+IV',
555     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q34.png', $1005));
556   Tree.Items.AddChildObject(node, 'Quadrant IV',
557     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q4.png', $1006));
558   Tree.Items.AddChildObject(node, 'Quadrant IV+I',
559     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q41.png', $1007));
560 
561   Tree.Items.AddChildObject(node, 'Quadrant I, reverse',
562     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q1r.png', $1100));
563   Tree.Items.AddChildObject(node, 'Quadrant I+II, reverse',
564     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q12r.png', $1101));
565   Tree.Items.AddChildObject(node, 'Quadrant II, reverse',
566     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q2r.png', $1102));
567   Tree.Items.AddChildObject(node, 'Quadrant II+III, reverse',
568     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q23r.png', $1103));
569   Tree.Items.AddChildObject(node, 'Quadrant III, reverse',
570     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q3r.png', $1104));
571   Tree.Items.AddChildObject(node, 'Quadrant III+IV, reverse',
572     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q34r.png', $1105));
573   Tree.Items.AddChildObject(node, 'Quadrant IV, reverse',
574     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q4r.png', $1106));
575   Tree.Items.AddChildObject(node, 'Quadrant IV+I, reverse',
576     TRenderParams.Create(@Render_Arc, 'arc_ell_ccw_q41r.png', $1107));
577 
578   node1 := Tree.Items.AddChild(node0, 'elliptical, rotated 30deg');
579   node := Tree.Items.AddChild(node1, 'clockwise from point 1 to point 2');
580   Tree.Items.AddChildObject(node, 'Quadrant I',
581     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q1.png', $2200));
582   Tree.Items.AddChildObject(node, 'Quadrant I+II',
583     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q12.png', $2201));
584   Tree.Items.AddChildObject(node, 'Quadrant II',
585     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q2.png', $2202));
586   Tree.Items.AddChildObject(node, 'Quadrant II+III',
587     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q23.png', $2203));
588   Tree.Items.AddChildObject(node, 'Quadrant III',
589     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q3.png', $2204));
590   Tree.Items.AddChildObject(node, 'Quadrant III+IV',
591     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q34.png', $2205));
592   Tree.Items.AddChildObject(node, 'Quadrant IV',
593     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q4.png', $2206));
594   Tree.Items.AddChildObject(node, 'Quadrant IV+I',
595     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q41.png', $2207));
596 
597   Tree.Items.AddChildObject(node, 'Quadrant I, reverse',
598     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q1r.png', $2300));
599   Tree.Items.AddChildObject(node, 'Quadrant I+II, reverse',
600     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q12r.png', $2301));
601   Tree.Items.AddChildObject(node, 'Quadrant II, reverse',
602     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q2r.png', $2302));
603   Tree.Items.AddChildObject(node, 'Quadrant II+III, reverse',
604     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q23r.png', $2303));
605   Tree.Items.AddChildObject(node, 'Quadrant III, reverse',
606     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q3r.png', $2304));
607   Tree.Items.AddChildObject(node, 'Quadrant III+IV, reverse',
608     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q34r.png', $2305));
609   Tree.Items.AddChildObject(node, 'Quadrant IV, reverse',
610     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q4r.png', $2306));
611   Tree.Items.AddChildObject(node, 'Quadrant IV+I, reverse',
612     TRenderParams.Create(@Render_Arc, 'arc_ellrot_cw_q41r.png', $2307));
613 
614   node := Tree.Items.AddChild(node1, 'counter-clockwise from point 1 to point 2');
615   Tree.Items.AddChildObject(node, 'Quadrant I',
616     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q1.png', $2000));
617   Tree.Items.AddChildObject(node, 'Quadrant I+II',
618     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q12.png', $2001));
619   Tree.Items.AddChildObject(node, 'Quadrant II',
620     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q2.png', $2002));
621   Tree.Items.AddChildObject(node, 'Quadrant II+III',
622     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q23.png', $2003));
623   Tree.Items.AddChildObject(node, 'Quadrant III',
624     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q3.png', $2004));
625   Tree.Items.AddChildObject(node, 'Quadrant III+IV',
626     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q34.png', $2005));
627   Tree.Items.AddChildObject(node, 'Quadrant IV',
628     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q4.png', $2006));
629   Tree.Items.AddChildObject(node, 'Quadrant IV+I',
630     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q41.png', $2007));
631 
632   Tree.Items.AddChildObject(node, 'Quadrant I, reverse',
633     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q1r.png', $2100));
634   Tree.Items.AddChildObject(node, 'Quadrant I+II, reverse',
635     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q12r.png', $2101));
636   Tree.Items.AddChildObject(node, 'Quadrant II, reverse',
637     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q2r.png', $2102));
638   Tree.Items.AddChildObject(node, 'Quadrant II+III, reverse',
639     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q23r.png', $2103));
640   Tree.Items.AddChildObject(node, 'Quadrant III, reverse',
641     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q3r.png', $2104));
642   Tree.Items.AddChildObject(node, 'Quadrant III+IV, reverse',
643     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q34r.png', $2105));
644   Tree.Items.AddChildObject(node, 'Quadrant IV, reverse',
645     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q4r.png', $2106));
646   Tree.Items.AddChildObject(node, 'Quadrant IV+I, reverse',
647     TRenderParams.Create(@Render_Arc, 'arc_ellrot_ccw_q41r.png', $2107));
648 
649   { -----------------------------------------------}
650   node := Tree.Items.AddChild(nil, 'Bezier');
651   { -----------------------------------------------}
652   Tree.Items.AddChildObject(node, 'Single segment',
653     TRenderParams.Create(@Render_Bezier, 'bezier.png'));
654 
655   { -----------------------------------------------}
656   node0 := Tree.Items.AddChild(nil, 'Gradients');
657   { -----------------------------------------------}
658   node := Tree.Items.AddChild(node0, 'horizontal');
659   Tree.Items.AddChildObject(node, 'Circle',
660     TRenderParams.Create(@Render_Shape, 'circle_gradienthor.png', $0101));
661   Tree.Items.AddChildObject(node, 'Ellipse',
662     TRenderParams.Create(@Render_Shape, 'ellipse_gradienthor.png', $0201));
663   Tree.Items.AddChildObject(node, 'Rectangle',
664     TRenderParams.Create(@Render_Shape, 'rect_gradienthor.png', $0301));
665   Tree.Items.AddChildObject(node, 'Rounded rectangle',
666     TRenderParams.Create(@Render_Shape, 'rounded_rect_gradienthor.png', $0401));
667   Tree.Items.AddChildObject(node, 'Polygon',
668     TRenderParams.Create(@Render_Shape, 'polygon_gradienthor.png', $0501));
669 
670   node := Tree.Items.AddChild(node0, 'vertical');
671   Tree.Items.AddChildObject(node, 'Circle',
672     TRenderParams.Create(@Render_Shape, 'circle_gradientvert.png', $0102));
673   Tree.Items.AddChildObject(node, 'Ellipse',
674     TRenderParams.Create(@Render_Shape, 'ellipse_gradientvert.png', $0202));
675   Tree.Items.AddChildObject(node, 'Rectangle',
676     TRenderParams.Create(@Render_Shape, 'rect_gradientvert.png', $0302));
677   Tree.Items.AddChildObject(node, 'Rounded rectangle',
678     TRenderParams.Create(@Render_Shape, 'rounded_rect_gradientvert.png', $0402));
679   Tree.Items.AddChildObject(node, 'Polygon',
680     TRenderParams.Create(@Render_Shape, 'polygon_gradientvert.png', $0502));
681 
682   node := Tree.Items.AddChild(node0, 'linear');
683   Tree.Items.AddChildObject(node, 'Circle',
684     TRenderParams.Create(@Render_Shape, 'circle_gradientlinear.png', $0103));
685   Tree.Items.AddChildObject(node, 'Ellipse',
686     TRenderParams.Create(@Render_Shape, 'ellipse_gradientlinear.png', $0203));
687   Tree.Items.AddChildObject(node, 'Rectangle',
688     TRenderParams.Create(@Render_Shape, 'rect_gradientlinear.png', $0303));
689   Tree.Items.AddChildObject(node, 'Rounded rectangle',
690     TRenderParams.Create(@Render_Shape, 'rounded_rect_gradientlinear.png', $0403));
691   Tree.Items.AddChildObject(node, 'Polygon',
692     TRenderParams.Create(@Render_Shape, 'polygon_gradientlinear.png', $0503));
693 
694   node := Tree.Items.AddChild(node0, 'radial');
695   Tree.Items.AddChildObject(node, 'Circle',
696     TRenderParams.Create(@Render_Shape, 'circle_gradientradial.png', $0104));
697   Tree.Items.AddChildObject(node, 'Ellipse',
698     TRenderParams.Create(@Render_Shape, 'ellipse_gradientradial.png', $0204));
699   Tree.Items.AddChildObject(node, 'Rectangle',
700     TRenderParams.Create(@Render_Shape, 'rect_gradientradial.png', $0304));
701   Tree.Items.AddChildObject(node, 'Rounded rectangle',
702     TRenderParams.Create(@Render_Shape, 'rounded_rect_gradientradial.png', $0404));
703   Tree.Items.AddChildObject(node, 'Polygon',
704     TRenderParams.Create(@Render_Shape, 'polygon_gradientradial.png', $0504));
705 
706   { -----------------------------------------------}
707   node0 := Tree.Items.AddChild(nil, 'Text');
708   { -----------------------------------------------}
709   node := Tree.Items.AddChild(node0, 'horizontal');
710   Tree.Items.AddChildObject(node, 'left aligned',
711     TRenderParams.Create(@Render_Text, 'text_left.png', $0000));
712   Tree.Items.AddChildObject(node, 'centered',
713     TRenderParams.Create(@Render_Text, 'text_center.png', $0001));
714   Tree.Items.AddChildObject(node, 'right aligned',
715     TRenderParams.Create(@Render_Text, 'text_right.png', $0002));
716 
717   node := Tree.Items.AddChild(node0, 'rotated by 30 deg');
718   Tree.Items.AddChildObject(node, 'left aligned',
719     TRenderParams.Create(@Render_Text, 'text_left_30.png', $1000));
720   Tree.Items.AddChildObject(node, 'centered',
721     TRenderParams.Create(@Render_Text, 'text_center_30.png', $1001));
722   Tree.Items.AddChildObject(node, 'right aligned',
723     TRenderParams.Create(@Render_Text, 'text_right_30.png', $1002));
724 
725   node := Tree.Items.AddChild(node0, 'vertical (90 deg, upward)');
726   Tree.Items.AddChildObject(node, 'left aligned',
727     TRenderParams.Create(@Render_Text, 'text_left_90.png', $2000));
728   Tree.Items.AddChildObject(node, 'centered',
729     TRenderParams.Create(@Render_Text, 'text_center_90.png', $2001));
730   Tree.Items.AddChildObject(node, 'right aligned',
731     TRenderParams.Create(@Render_Text, 'text_right_90.png', $2002));
732 
733   node := Tree.Items.AddChild(node0, 'vertical (-90 deg, downward)');
734   Tree.Items.AddChildObject(node, 'left aligned',
735     TRenderParams.Create(@Render_Text, 'text_left_m90.png', $3000));
736   Tree.Items.AddChildObject(node, 'centered',
737     TRenderParams.Create(@Render_Text, 'text_center_m90.png', $3001));
738   Tree.Items.AddChildObject(node, 'right aligned',
739     TRenderParams.Create(@Render_Text, 'text_right_m90.png', $3002));
740 
741   node := Tree.Items.AddChild(node0, 'Fonts');
742   Tree.Items.AddChildObject(node, 'Times New Roman + Courier New',
743     TRenderParams.Create(@Render_Text_Fonts, 'text_fonts.png'));
744   Tree.Items.AddChildObject(node0, 'Text colors',
745     TRenderParams.Create(@Render_Text_Colors, 'text_colors.png'));
746 end;
747 
748 procedure TMainForm.Render_Shape(APage: TvVectorialPage;
749   AIntParam: Integer);
750 { AIntParam and $00FF = 0 --> solid fill
751                         1 --> horizontal gradient
752                         2 --> vertical gradient
753                         3 --> linear gradient
754                         4 --> radial gradient
755   AIntParam and $FF00 = $0100 --> circle
756                         $0200 --> ellipse
757                         $0300 --> rectangle
758                         $0400 --> rounded rect
759                         $0500 --> polygon (triangle) }
760 var
761   ent: TvEntityWithPenAndBrush;
762 begin
763   case AIntParam and $FF00 of
764     $0100: ent := CreateStdCircle(APage);
765     $0200: ent := CreateStdEllipse(APage);
766     $0300: ent := CreateStdRect(APage);
767     $0400: ent := CreateStdRoundedRect(APage);
768     $0500: ent := CreateStdPolygon(APage);
769     else   raise Exception.Create('Shape not supported.');
770   end;
771   case AIntParam and $00FF of
772     $0000: ent.Brush := StdSolidBrush;
773     $0001: ent.Brush := StdHorizGradientBrush;
774     $0002: ent.Brush := StdVertGradientBrush;
775     $0003: ent.Brush := StdLinearGradientBrush;
776     $0004: ent.Brush := StdRadialGradientBrush;
777     else raise Exception.Create('Brush not supported');
778   end;
779   APage.AddEntity(ent);
780 end;
781 
782 procedure TMainForm.Render_Arc(APage: TvVectorialPage; AIntParam: Integer);
783 //
784 // AIntParam and $000F = $0000  --> circular arc
785 //                       $1000  --> elliptical arc
786 //                       $2000  --> elliptical arc, rotated
787 // AIntParam and $000F = $0000  --> quarter 1
788 //                       $0001  --> quarter 1 + 2
789 //                       $0002  --> quarter 2
790 //                       $0003  --> quarter 2 + 3
791 //                       $0004  --> quarter 3
792 //                       $0005  --> quarter 3+4
793 //                       $0006  --> quarter 4
794 //                       $0007  --> quarter 4+1
795 // AIntParam and $0100 = $0100  --> start and end points exchanged
796 // AIntParan and $0200 = $0200  --> clockwise
797 const
798   ROT_ANGLE = 30;
799   RY_MULT = 0.6;
800   CX = 50;
801   CY = 55;
802   R = 30;
803 var
804   isReversed, isClockwise, isEllipse, isRotated: Boolean;
805   p: T3dPoint;
806   x1, y1, x2, y2, rx, ry: Double;
807   startAngle, endAngle, phi: Double;
808 begin
809   isReversed := AIntParam and $0100 <> 0;
810   isClockwise := AIntParam and $0200 <> 0;
811   isEllipse := AIntParam and $F000 <> 0;
812   isRotated := AIntParam and $F000 = $2000;
813 
814   rx := R;
815   ry := IfThen(isEllipse, R * RY_MULT, R);
816   phi := IfThen(isRotated, DegToRad(ROT_ANGLE), 0.0);
817 
818   startAngle := DegToRad((AIntParam and $000F) * 45);  //  0°,  45°,  90°, ...
819   endAngle := startAngle + pi/2;                       // 90°, 135°, 180°, ...
820   x1 := CX + rx * cos(startAngle);
821   y1 := CY + ry * sin(startAngle);
822   x2 := CX + rx * cos(endAngle);
823   y2 := CY + ry * sin(endAngle);
824   if isRotated then begin
825     p := Rotate3DPointInXY(Make3DPoint(x1, y1), Make3DPoint(CX, CY), -phi);
826     // See comment at Rotate3DPointInXY regarding the negative sign of phi
827     x1 := p.x;
828     y1 := p.y;
829     p := Rotate3DPointInXY(Make3DPoint(x2, y2), Make3DPoint(CX, CY), -phi);
830     x2 := p.x;
831     y2 := p.y;
832   end;
833 
834   if isReversed then
835     CreateArc(APage, x2, y2, x1, y1, CX, CY, rx, ry, phi, isClockwise)
836   else
837     CreateArc(APage, x1, y1, x2, y2, CX, CY, rx, ry, phi, isClockwise);
838 end;
839 
840 procedure TMainForm.Render_Bezier(APage: TvVectorialpage; AIntParam: Integer);
841 const
842   X1 = 10;
843   Y1 = 25;
844   X2 = 30;
845   Y2 = 80;
846   X3 = 50;
847   Y3 = 70;
848   X4 = 90;
849   Y4 = 25;
850 begin
851   CreateBezier(APage, X1,Y1, X2,Y2, X3,Y3, X4,Y4);
852 end;
853 
854 procedure TMainForm.Render_Path_Hole_SolidFill(APage: TvVectorialPage;
855   AIntParam: Integer);
856 var
857   obj: TPath;
858 begin
859   obj := CreatePathWithHole(APage);  // no need to AddEntity!
860   obj.Brush.Color := colYellow;
861   obj.Brush.Style := bsSolid;
862   obj.Pen.Width := 3;
863   obj.Pen.Color := colBlue;
864 end;
865 
866 procedure TMainForm.Render_Path_Hole_GradientFill(APage: TvVectorialPage;
867   AIntParam: Integer);
868 var
869   obj: TPath;
870 begin
871   obj := CreatePathWithHole(APage);  // No need to AddEntity!
872   obj.Brush := StdLinearGradientBrush;
873   obj.Pen.Width := 3;
874   obj.Pen.Color := colBlue;
875 end;
876 
877 procedure TMainForm.Render_SelfIntersectingPoly_SolidFill_EvenOdd(
878   APage: TvVectorialPage; AIntParam: Integer);
879 var
880   obj: TvPolygon;
881 begin
882   obj := CreateStdSelfIntersectingPolygon(APage);
883   obj.WindingRule := vcmEvenOddRule;
884   obj.Brush := StdSolidBrush;
885   APage.AddEntity(obj);
886 end;
887 
888 procedure TMainForm.Render_SelfIntersectingPoly_GradientFill_EvenOdd(
889   APage: TvVectorialPage; AIntParam: Integer);
890 var
891   obj: TvPolygon;
892 begin
893   obj := CreateStdSelfIntersectingPolygon(APage);
894   obj.Brush := StdHorizGradientBrush;
895   obj.WindingRule := vcmEvenOddRule;
896   APage.AddEntity(obj);
897 end;
898 
899 procedure TMainForm.Render_SelfIntersectingPoly_SolidFill_NonZeroWinding(
900   APage: TvVectorialPage; AIntParam: Integer);
901 var
902   obj: TvPolygon;
903 begin
904   obj := CreateStdSelfIntersectingPolygon(APage);
905   obj.WindingRule := vcmNonZeroWindingRule;
906   obj.Brush := StdSolidBrush;
907   APage.AddEntity(obj);
908 end;
909 
910 procedure TMainForm.Render_SelfIntersectingPoly_GradientFill_NonZeroWinding(
911   APage: TvVectorialPage; AIntParam: Integer);
912 var
913   obj: TvPolygon;
914 begin
915   obj := CreateStdSelfIntersectingPolygon(APage);
916   obj.Brush := StdHorizGradientBrush;
917   obj.WindingRule := vcmNonzeroWindingRule;
918   APage.AddEntity(obj);
919 end;
920 
921 procedure TMainForm.Render_Text(APage: TvVectorialPage; AIntParam: Integer);
922 { AIntParam and $000F = $0000  --> anchor at left
923                         $0001  --> anchor at center
924                         $0002  --> anchor at right
925   AIntParam and $F000 = $0000  --> horizontal
926                         $1000  --> rotated 30deg
927                         $2000  --> rotated 90deg
928                         $3000  --> rotated -90deg }
929 const
930   XTEXT = 50;
931   YTEXT = 40;  // we assume that y points up
932   L = 10;
933 var
934   txt: TvText;
935   p: TPath;
936   angle: double;
937   anchor: TvTextAnchor;
938 begin
939   case AIntParam and $000F of
940     $0000 : anchor := vtaStart;
941     $0001 : anchor := vtaMiddle;
942     $0002 : anchor := vtaEnd;
943     else raise Exception.Create('Text anchor not supported');
944   end;
945   case AIntParam and $F000 of
946     $0000 : angle := 0;
947     $1000 : angle := 30;
948     $2000 : angle := 90;
949     $3000 : angle := -90;
950     else raise Exception.Create('Text angle not supported.');
951   end;
952 
953   // Draw "+" at the origin of the text
954   if APage.UseTopLeftCoordinates then begin
955     APage.StartPath    (XTEXT - L, PAGE_SIZE - YTEXT);
956     APage.AddLineToPath(XTEXT + L, PAGE_SIZE - YTEXT);
957     APage.AddMoveToPath(XTEXT,     PAGE_SIZE - YTEXT - L);
958     APage.AddLineToPath(XTEXT,     PAGE_SIZE - YTEXT + L);
959   end else begin
960     APage.StartPath    (XTEXT - L, YTEXT);
961     APage.AddLineToPath(XTEXT + L, YTEXT);
962     APage.AddMoveToPath(XTEXT,     YTEXT - L);
963     APage.AddLineToPath(XTEXT,     YTEXT + L);
964   end;
965   p := APage.EndPath;
966   p.Pen.Width := 1;
967   p.Pen.Color := colRed;
968 
969   // Draw text
970   txt := TvText.Create(APage);
971   txt.X := XTEXT;
972   if APage.UseTopLeftCoordinates then
973     txt.Y := PAGE_SIZE - YTEXT else
974     txt.Y := YTEXT;
975   txt.Value.Add('ABC');
976   txt.Font.Size := 14;
977   txt.TextAnchor := anchor;
978   txt.Font.Orientation := angle;
979 
980   APage.AddEntity(txt);
981 end;
982 
983 procedure TMainForm.Render_Text_Fonts(APage: TvVectorialPage;
984   AIntParam: Integer);
985 var
986   txt: TvText;
987 begin
988   txt := TvText.Create(APage);
989   txt.X := 10;
990   txt.Y := 80;
991   txt.Font.Name := 'Times New Roman';
992   txt.Font.Size := 10;
993   txt.Value.Add('Times');
994   APage.AddEntity(txt);
995 
996   txt := TvText.Create(APage);
997   txt.X := 10;
998   txt.Y := 60;
999   txt.Font.Name := 'Courier New';
1000   txt.Font.Size := 12;
1001   txt.Value.Add('Courier');
1002   APage.AddEntity(txt);
1003 end;
1004 
1005 procedure TMainForm.Render_Text_Colors(APage: TvVectorialPage;
1006   AIntParam: Integer);
1007 var
1008   txt: TvText;
1009 begin
1010   txt := TvText.Create(APage);
1011   txt.X := 10;
1012   txt.Y := 80;
1013   txt.Font.Name := 'Times New Roman';
1014   txt.Font.Size := 14;
1015   txt.Font.Color := colRed;
1016   txt.Value.Add('Text');
1017   txt.Brush.Style := bsSolid;
1018   txt.Brush.Color := colYellow;
1019   txt.Brush.Kind := bkSimpleBrush;
1020   APage.AddEntity(txt);
1021 end;
1022 
1023 procedure TMainForm.ReadIni;
1024 var
1025   ini: TCustomIniFile;
1026   L, T, W, H: Integer;
1027   rct: TRect;
1028 begin
1029   ini := TMemIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
1030   try
1031     L := ini.ReadInteger('MainForm', 'Left', Left);
1032     T := ini.ReadInteger('MainForm', 'Top', Top);
1033     W := ini.ReadInteger('MainForm', 'Width', Width);
1034     H := ini.ReadInteger('MainForm', 'Height', Height);
1035     rct := Screen.DesktopRect;
1036     if L + W > rct.Right - rct.Left then L := rct.Right - W;
1037     if L < 0 then L := rct.Left;
1038     if T + H > rct.Bottom - rct.Top then T := rct.Bottom - H;
1039     if T < 0 then T := rct.Top;
1040     SetBounds(L, T, W, H);
1041   finally
1042     ini.Free;
1043   end;
1044 end;
1045 
1046 procedure TMainForm.WriteIni;
1047 var
1048   ini: TCustomIniFile;
1049 begin
1050   ini := TMemIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
1051   try
1052     if WindowState = wsNormal then
1053     begin
1054       ini.WriteInteger('MainForm', 'Left', Left);
1055       ini.WriteInteger('MainForm', 'Top', Top);
1056       ini.WriteInteger('MainForm', 'Width', Width);
1057       ini.WriteInteger('MainForm', 'Height', Height);
1058     end;
1059   finally
1060     ini.Free;
1061   end;
1062 end;
1063 
1064 procedure TMainForm.ShowFileImage(AFilename: String; AUseTopLeftCoords: Boolean;
1065   APaintbox: TPaintbox);
1066 var
1067   ext: String;
1068   rc: TRenderCoords;
1069 begin
1070   if AUseTopLeftCoords then
1071     rc := rcTopLeftCoords else
1072     rc := rcBottomLeftCoords;
1073 
1074   ext := Lowercase(ExtractFileExt(AFileName));
1075 
1076   if not FileExists(AFileName) then begin
1077     case ext of
1078       '.svg': FreeAndNil(FDocFromSVG[rc]);
1079       '.wmf': FreeAndNil(FDocFromWMF[rc]);
1080       else    raise Exception.Create('File type not supported');
1081     end;
1082     APaintbox.Hint := NOT_SAVED;
1083     APaintbox.Invalidate;
1084     exit;
1085   end;
1086 
1087   if ext = '.svg' then begin
1088     FreeAndNil(FDocFromSVG[rc]);
1089     FDocFromSVG[rc] := TvVectorialDocument.Create;
1090     FDocFromSVG[rc].ReadFromFile(AFileName);
1091   end else
1092   if ext = '.wmf' then begin
1093     FreeAndNil(FDocFromWMF[rc]);
1094     FDocFromWMF[rc] := TvVectorialDocument.Create;
1095     FDocFromWMF[rc].ReadFromFile(AFilename);
1096   end;
1097   APaintbox.Hint := AFileName;
1098   APaintBox.Invalidate;
1099 end;
1100 
1101 procedure TMainForm.ShowRefImageTest;
1102 var
1103   renderParams: TRenderParams;
1104   fn: String;
1105 begin
1106   if Tree.Selected = nil then
1107     exit;
1108 
1109   renderParams := TRenderParams(Tree.Selected.Data);
1110   if renderParams = nil then
1111   begin
1112     RefImage.Picture := nil;
1113     exit;
1114   end;
1115 
1116   fn := IncludeTrailingPathDelimiter(IMG_FOLDER) + renderParams.RefFile;
1117   if FileExists(fn) then begin
1118     RefImage.Picture.LoadFromFile(fn);
1119     RefImage.Hint := fn;
1120   end else begin
1121     RefImage.Picture := nil;
1122     RefImage.Hint := NOT_SAVED;
1123   end;
1124 end;
1125 
1126 procedure TMainForm.ShowRenderTestImages;
1127 var
1128   renderParams: TRenderParams;
1129   page: TvVectorialPage = nil;
1130 begin
1131   if Tree.Selected = nil then
1132     exit;
1133 
1134   renderParams := TRenderParams(Tree.Selected.Data);
1135   if renderParams = nil then
1136   begin
1137     BottomLeftPaintbox.Invalidate;
1138     TopLeftPaintbox.Invalidate;
1139     exit;
1140   end;
1141 
1142   // Render document with bottom/left origin
1143   PrepareDoc(FDoc[rcBottomLeftCoords], page, false);
1144   renderParams.OnRender(page, renderParams.IntParam);
1145   BottomLeftPaintbox.Invalidate;
1146 
1147   // Render document with top/left origin
1148   PrepareDoc(FDoc[rcTopLeftCoords], page, true);
1149   renderParams.OnRender(page, renderParams.IntParam);
1150   TopLeftPaintbox.Invalidate;
1151 end;
1152 
1153 procedure TMainForm.ShowWriteReadTestImages;
1154 var
1155   renderParams: TRenderParams;
1156   folder: String;
1157   fn: String;
1158   ext: String;
1159   rc: TRenderCoords;
1160 begin
1161   for rc in TRenderCoords do begin
1162     FreeAndNil(FDocFromSVG[rc]);
1163     FreeAndNil(FDocFromWMF[rc]);
1164   end;
1165 
1166   if Tree.Selected = nil then
1167     exit;
1168 
1169   renderParams := TRenderParams(Tree.Selected.Data);
1170   if renderParams = nil then
1171   begin
1172     WRBottomLeftPaintbox.Invalidate;
1173     WRTopLeftPaintbox.Invalidate;
1174     exit;
1175   end;
1176 
1177   ext := GetFileFormatExt;
1178   folder := IMG_FOLDER + ext + PathDelim;
1179 
1180   fn := folder + 'bl_' + ChangeFileExt(renderParams.RefFile, '.' + ext);
1181   ShowFileImage(fn, false, WRBottomLeftPaintbox);
1182 
1183   fn := folder + 'tl_' + ChangeFileExt(renderParams.RefFile, '.' + ext);
1184   ShowFileImage(fn, true, WRTopLeftPaintbox);
1185 end;
1186 
1187 procedure TMainForm.TreeSelectionChanged(Sender: TObject);
1188 begin
1189   ShowRenderTestImages;
1190   ShowRefImageTest;
1191   ShowWriteReadTestImages;
1192   UpdateCmdStates;
1193 end;
1194 
1195 procedure TMainForm.TreeCustomDrawItem(Sender: TCustomTreeView; Node: TTreeNode;
1196   State: TCustomDrawState; var DefaultDraw: Boolean);
1197 begin
1198   if Node.HasChildren then
1199     Sender.Canvas.Font.Style := [fsBold] else
1200     Sender.Canvas.Font.Style := [];
1201 end;
1202 
1203 procedure TMainForm.UpdateCmdStates;
1204 var
1205   fn: String;
1206   folder: string;
1207   renderParams: TRenderParams;
1208   ext: String;
1209   rc: TRenderCoords;
1210   rcOK: array[TRenderCoords] of boolean = (false, false);
1211 begin
1212   BtnSaveAsRef.Enabled := Tree.Selected <> nil;
1213   BtnSaveToFiles.Enabled := Tree.Selected <> nil;
1214   BtnViewBottomLeft.Enabled := Tree.Selected <> nil;
1215   BtnViewTopLeft.Enabled := Tree.Selected <> nil;
1216 
1217   if Tree.Selected <> nil then begin
1218     renderParams := TRenderParams(Tree.Selected.Data);
1219     if renderParams <> nil then begin
1220       ext := GetFileFormatExt;
1221       folder := IMG_FOLDER + ext + PathDelim;
1222       fn := folder + 'bl_' + ChangeFileExt(renderParams.RefFile, '.' + ext);
1223       rcOK[rcBottomLeftCoords] := FileExists(fn);
1224       fn := folder + 'tl_' + ChangeFileExt(renderParams.RefFile, '.' + ext);
1225       rcOK[rcTopLeftCoords] := FileExists(fn);
1226     end;
1227   end;
1228   BtnViewBottomLeft.Enabled := rcOK[rcBottomLeftcoords];
1229   BtnViewTopLeft.Enabled := rcOK[rcTopLeftCoords];
1230 end;
1231 
1232 end.
1233 
1234