fpvectorial.pas 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. {
  2. fpvectorial.pas
  3. Vector graphics document
  4. License: The same modified LGPL as the Free Pascal RTL
  5. See the file COPYING.modifiedLGPL for more details
  6. AUTHORS: Felipe Monteiro de Carvalho
  7. Pedro Sol Pegorini L de Lima
  8. }
  9. unit fpvectorial;
  10. {$ifdef fpc}
  11. {$mode delphi}
  12. {$endif}
  13. interface
  14. uses
  15. Classes, SysUtils, Math;
  16. type
  17. TvVectorialFormat = (
  18. { Multi-purpose document formats }
  19. vfPDF, vfPostScript, vfSVG, vfCorelDrawCDR, vfWindowsMetafileWMF,
  20. { CAD formats }
  21. vfDXF,
  22. { GCode formats }
  23. vfGCodeAvisoCNCPrototipoV5, vfGCodeAvisoCNCPrototipoV6);
  24. const
  25. { Default extensions }
  26. { Multi-purpose document formats }
  27. STR_PDF_EXTENSION = '.pdf';
  28. STR_POSTSCRIPT_EXTENSION = '.ps';
  29. STR_SVG_EXTENSION = '.svg';
  30. STR_CORELDRAW_EXTENSION = '.cdr';
  31. STR_WINMETAFILE_EXTENSION = '.wmf';
  32. type
  33. T3DPoint = record
  34. X, Y, Z: Double;
  35. end;
  36. P3DPoint = ^T3DPoint;
  37. TSegmentType = (
  38. st2DLine, st2DBezier,
  39. st3DLine, st3DBezier, stMoveTo);
  40. {@@
  41. The coordinates in fpvectorial are given in millimiters and
  42. the starting point is in the bottom-left corner of the document.
  43. The X grows to the right and the Y grows to the top.
  44. }
  45. { TPathSegment }
  46. TPathSegment = class
  47. public
  48. SegmentType: TSegmentType;
  49. // Fields for linking the list
  50. Previous: TPathSegment;
  51. Next: TPathSegment;
  52. end;
  53. {@@
  54. In a 2D segment, the X and Y coordinates represent usually the
  55. final point of the segment, being that it starts where the previous
  56. segment ends. The exception is for the first segment of all, which simply
  57. holds the starting point for the drawing and should always be of the type
  58. stMoveTo.
  59. }
  60. T2DSegment = class(TPathSegment)
  61. public
  62. X, Y: Double;
  63. end;
  64. {@@
  65. In Bezier segments, we remain using the X and Y coordinates for the ending point.
  66. The starting point is where the previous segment ended, so that the intermediary
  67. bezier control points are [X2, Y2] and [X3, Y3].
  68. }
  69. T2DBezierSegment = class(T2DSegment)
  70. public
  71. X2, Y2: Double;
  72. X3, Y3: Double;
  73. end;
  74. T3DSegment = class(TPathSegment)
  75. public
  76. {@@
  77. Coordinates of the end of the segment.
  78. For the first segment, this is the starting point.
  79. }
  80. X, Y, Z: Double;
  81. end;
  82. T3DBezierSegment = class(T3DSegment)
  83. public
  84. X2, Y2, Z2: Double;
  85. X3, Y3, Z3: Double;
  86. end;
  87. TPath = class
  88. Len: Integer;
  89. Points: TPathSegment; // Beginning of the double-linked list
  90. PointsEnd: TPathSegment; // End of the double-linked list
  91. CurPoint: TPathSegment; // Used in PrepareForSequentialReading and Next
  92. procedure Assign(APath: TPath);
  93. function Count(): TPathSegment;
  94. procedure PrepareForSequentialReading;
  95. function Next(): TPathSegment;
  96. end;
  97. {@@
  98. TvText represents a text in memory.
  99. At the moment fonts are unsupported, only simple texts
  100. up to 255 chars are supported.
  101. }
  102. TvText = class
  103. public
  104. X, Y, Z: Double; // Z is ignored in 2D formats
  105. FontSize: integer;
  106. FontName: utf8string;
  107. Value: utf8string;
  108. end;
  109. {@@
  110. }
  111. TvEntity = class
  112. public
  113. end;
  114. {@@
  115. }
  116. TvCircle = class(TvEntity)
  117. public
  118. CenterX, CenterY, CenterZ, Radius: Double;
  119. end;
  120. {@@
  121. }
  122. TvCircularArc = class(TvEntity)
  123. public
  124. CenterX, CenterY, CenterZ, Radius: Double;
  125. {@@ The Angle is measured in degrees in relation to the positive X axis }
  126. StartAngle, EndAngle: Double;
  127. end;
  128. {@@
  129. }
  130. { TvEllipse }
  131. TvEllipse = class(TvEntity)
  132. public
  133. // Mandatory fields
  134. CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis: Double;
  135. {@@ The Angle is measured in degrees in relation to the positive X axis }
  136. Angle: Double;
  137. // Calculated fields
  138. BoundingRect: TRect;
  139. procedure CalculateBoundingRectangle;
  140. end;
  141. {@@
  142. }
  143. { TvAlignedDimension }
  144. TvAlignedDimension = class(TvEntity)
  145. public
  146. // Mandatory fields
  147. BaseLeft, BaseRight, DimensionLeft, DimensionRight: T3DPoint;
  148. end;
  149. type
  150. TvCustomVectorialWriter = class;
  151. TvCustomVectorialReader = class;
  152. { TvVectorialDocument }
  153. TvVectorialDocument = class
  154. private
  155. FPaths: TFPList;
  156. FTexts: TFPList;
  157. FEntities: TFPList;
  158. FTmpPath: TPath;
  159. FTmpText: TvText;
  160. procedure RemoveCallback(data, arg: pointer);
  161. function CreateVectorialWriter(AFormat: TvVectorialFormat): TvCustomVectorialWriter;
  162. function CreateVectorialReader(AFormat: TvVectorialFormat): TvCustomVectorialReader;
  163. procedure ClearTmpPath();
  164. procedure AppendSegmentToTmpPath(ASegment: TPathSegment);
  165. public
  166. Name: string;
  167. Width, Height: Double; // in millimeters
  168. { Base methods }
  169. constructor Create;
  170. destructor Destroy; override;
  171. procedure WriteToFile(AFileName: string; AFormat: TvVectorialFormat);
  172. procedure WriteToStream(AStream: TStream; AFormat: TvVectorialFormat);
  173. procedure WriteToStrings(AStrings: TStrings; AFormat: TvVectorialFormat);
  174. procedure ReadFromFile(AFileName: string; AFormat: TvVectorialFormat);
  175. procedure ReadFromStream(AStream: TStream; AFormat: TvVectorialFormat);
  176. procedure ReadFromStrings(AStrings: TStrings; AFormat: TvVectorialFormat);
  177. class function GetFormatFromExtension(AFileName: string): TvVectorialFormat;
  178. function GetDetailedFileFormat(): string;
  179. { Data reading methods }
  180. function GetPath(ANum: Cardinal): TPath;
  181. function GetPathCount: Integer;
  182. function GetText(ANum: Cardinal): TvText;
  183. function GetTextCount: Integer;
  184. function GetEntity(ANum: Cardinal): TvEntity;
  185. function GetEntityCount: Integer;
  186. { Data removing methods }
  187. procedure Clear;
  188. procedure RemoveAllPaths;
  189. procedure RemoveAllTexts;
  190. { Data writing methods }
  191. procedure AddPath(APath: TPath);
  192. procedure StartPath(AX, AY: Double);
  193. procedure AddLineToPath(AX, AY: Double); overload;
  194. procedure AddLineToPath(AX, AY, AZ: Double); overload;
  195. procedure AddBezierToPath(AX1, AY1, AX2, AY2, AX3, AY3: Double); overload;
  196. procedure AddBezierToPath(AX1, AY1, AZ1, AX2, AY2, AZ2, AX3, AY3, AZ3: Double); overload;
  197. procedure EndPath();
  198. procedure AddText(AX, AY, AZ: Double; FontName: string; FontSize: integer; AText: utf8string); overload;
  199. procedure AddText(AX, AY, AZ: Double; AStr: utf8string); overload;
  200. procedure AddCircle(ACenterX, ACenterY, ACenterZ, ARadius: Double);
  201. procedure AddCircularArc(ACenterX, ACenterY, ACenterZ, ARadius, AStartAngle, AEndAngle: Double);
  202. procedure AddEllipse(CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis, Angle: Double);
  203. // Dimensions
  204. procedure AddAlignedDimension(BaseLeft, BaseRight, DimLeft, DimRight: T3DPoint);
  205. { properties }
  206. property PathCount: Integer read GetPathCount;
  207. property Paths[Index: Cardinal]: TPath read GetPath;
  208. end;
  209. {@@ TvVectorialReader class reference type }
  210. TvVectorialReaderClass = class of TvCustomVectorialReader;
  211. { TvCustomVectorialReader }
  212. TvCustomVectorialReader = class
  213. public
  214. { General reading methods }
  215. constructor Create; virtual;
  216. procedure ReadFromFile(AFileName: string; AData: TvVectorialDocument); virtual;
  217. procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); virtual;
  218. procedure ReadFromStrings(AStrings: TStrings; AData: TvVectorialDocument); virtual;
  219. end;
  220. {@@ TvVectorialWriter class reference type }
  221. TvVectorialWriterClass = class of TvCustomVectorialWriter;
  222. {@@ TvCustomVectorialWriter }
  223. { TvCustomVectorialWriter }
  224. TvCustomVectorialWriter = class
  225. public
  226. { General writing methods }
  227. constructor Create; virtual;
  228. procedure WriteToFile(AFileName: string; AData: TvVectorialDocument); virtual;
  229. procedure WriteToStream(AStream: TStream; AData: TvVectorialDocument); virtual;
  230. procedure WriteToStrings(AStrings: TStrings; AData: TvVectorialDocument); virtual;
  231. end;
  232. {@@ List of registered formats }
  233. TvVectorialFormatData = record
  234. ReaderClass: TvVectorialReaderClass;
  235. WriterClass: TvVectorialWriterClass;
  236. ReaderRegistered: Boolean;
  237. WriterRegistered: Boolean;
  238. Format: TvVectorialFormat;
  239. end;
  240. var
  241. GvVectorialFormats: array of TvVectorialFormatData;
  242. procedure RegisterVectorialReader(
  243. AReaderClass: TvVectorialReaderClass;
  244. AFormat: TvVectorialFormat);
  245. procedure RegisterVectorialWriter(
  246. AWriterClass: TvVectorialWriterClass;
  247. AFormat: TvVectorialFormat);
  248. implementation
  249. const
  250. Str_Error_Nil_Path = ' The program attempted to add a segment before creating a path';
  251. {@@
  252. Registers a new reader for a format
  253. }
  254. procedure RegisterVectorialReader(
  255. AReaderClass: TvVectorialReaderClass;
  256. AFormat: TvVectorialFormat);
  257. var
  258. i, len: Integer;
  259. FormatInTheList: Boolean;
  260. begin
  261. len := Length(GvVectorialFormats);
  262. FormatInTheList := False;
  263. { First search for the format in the list }
  264. for i := 0 to len - 1 do
  265. begin
  266. if GvVectorialFormats[i].Format = AFormat then
  267. begin
  268. if GvVectorialFormats[i].ReaderRegistered then
  269. raise Exception.Create('RegisterVectorialReader: Reader class for format ' {+ AFormat} + ' already registered.');
  270. GvVectorialFormats[i].ReaderRegistered := True;
  271. GvVectorialFormats[i].ReaderClass := AReaderClass;
  272. FormatInTheList := True;
  273. Break;
  274. end;
  275. end;
  276. { If not already in the list, then add it }
  277. if not FormatInTheList then
  278. begin
  279. SetLength(GvVectorialFormats, len + 1);
  280. GvVectorialFormats[len].ReaderClass := AReaderClass;
  281. GvVectorialFormats[len].WriterClass := nil;
  282. GvVectorialFormats[len].ReaderRegistered := True;
  283. GvVectorialFormats[len].WriterRegistered := False;
  284. GvVectorialFormats[len].Format := AFormat;
  285. end;
  286. end;
  287. {@@
  288. Registers a new writer for a format
  289. }
  290. procedure RegisterVectorialWriter(
  291. AWriterClass: TvVectorialWriterClass;
  292. AFormat: TvVectorialFormat);
  293. var
  294. i, len: Integer;
  295. FormatInTheList: Boolean;
  296. begin
  297. len := Length(GvVectorialFormats);
  298. FormatInTheList := False;
  299. { First search for the format in the list }
  300. for i := 0 to len - 1 do
  301. begin
  302. if GvVectorialFormats[i].Format = AFormat then
  303. begin
  304. if GvVectorialFormats[i].WriterRegistered then
  305. raise Exception.Create('RegisterVectorialWriter: Writer class for format ' + {AFormat +} ' already registered.');
  306. GvVectorialFormats[i].WriterRegistered := True;
  307. GvVectorialFormats[i].WriterClass := AWriterClass;
  308. FormatInTheList := True;
  309. Break;
  310. end;
  311. end;
  312. { If not already in the list, then add it }
  313. if not FormatInTheList then
  314. begin
  315. SetLength(GvVectorialFormats, len + 1);
  316. GvVectorialFormats[len].ReaderClass := nil;
  317. GvVectorialFormats[len].WriterClass := AWriterClass;
  318. GvVectorialFormats[len].ReaderRegistered := False;
  319. GvVectorialFormats[len].WriterRegistered := True;
  320. GvVectorialFormats[len].Format := AFormat;
  321. end;
  322. end;
  323. { TvEllipse }
  324. procedure TvEllipse.CalculateBoundingRectangle;
  325. var
  326. t, tmp: Double;
  327. begin
  328. {
  329. To calculate the bounding rectangle we can do this:
  330. Ellipse equations:You could try using the parametrized equations for an ellipse rotated at an arbitrary angle:
  331. x = CenterX + MajorHalfAxis*cos(t)*cos(Angle) - MinorHalfAxis*sin(t)*sin(Angle)
  332. y = CenterY + MinorHalfAxis*sin(t)*cos(Angle) + MajorHalfAxis*cos(t)*sin(Angle)
  333. You can then differentiate and solve for gradient = 0:
  334. 0 = dx/dt = -MajorHalfAxis*sin(t)*cos(Angle) - MinorHalfAxis*cos(t)*sin(Angle)
  335. =>
  336. tan(t) = -MinorHalfAxis*tan(Angle)/MajorHalfAxis
  337. =>
  338. t = cotang(-MinorHalfAxis*tan(Angle)/MajorHalfAxis)
  339. On the other axis:
  340. 0 = dy/dt = b*cos(t)*cos(phi) - a*sin(t)*sin(phi)
  341. =>
  342. tan(t) = b*cot(phi)/a
  343. }
  344. t := cotan(-MinorHalfAxis*tan(Angle)/MajorHalfAxis);
  345. tmp := CenterX + MajorHalfAxis*cos(t)*cos(Angle) - MinorHalfAxis*sin(t)*sin(Angle);
  346. BoundingRect.Right := Round(tmp);
  347. end;
  348. { TsWorksheet }
  349. {@@
  350. Helper method for clearing the records in a spreadsheet.
  351. }
  352. procedure TvVectorialDocument.RemoveCallback(data, arg: pointer);
  353. begin
  354. { if data <> nil then
  355. begin
  356. ldata := PObject(data);
  357. ldata^.Free;
  358. end;}
  359. end;
  360. {@@
  361. Constructor.
  362. }
  363. constructor TvVectorialDocument.Create;
  364. begin
  365. inherited Create;
  366. FPaths := TFPList.Create;
  367. FTexts := TFPList.Create;
  368. FEntities := TFPList.Create;
  369. FTmpPath := TPath.Create;
  370. end;
  371. {@@
  372. Destructor.
  373. }
  374. destructor TvVectorialDocument.Destroy;
  375. begin
  376. Clear;
  377. FPaths.Free;
  378. FTexts.Free;
  379. FEntities.Free;
  380. inherited Destroy;
  381. end;
  382. {@@
  383. Clears the list of Vectors and releases their memory.
  384. }
  385. procedure TvVectorialDocument.RemoveAllPaths;
  386. begin
  387. // FPaths.ForEachCall(RemoveCallback, nil);
  388. FPaths.Clear;
  389. end;
  390. procedure TvVectorialDocument.RemoveAllTexts;
  391. begin
  392. // FTexts.ForEachCall(RemoveCallback, nil);
  393. FTexts.Clear;
  394. end;
  395. procedure TvVectorialDocument.AddPath(APath: TPath);
  396. var
  397. lPath: TPath;
  398. Len: Integer;
  399. begin
  400. lPath := TPath.Create;
  401. lPath.Assign(APath);
  402. FPaths.Add(Pointer(lPath));
  403. //WriteLn(':>TvVectorialDocument.AddPath 1 Len = ', Len);
  404. //WriteLn(':>TvVectorialDocument.AddPath 2');
  405. //WriteLn(':>TvVectorialDocument.AddPath 3');
  406. //WriteLn(':>TvVectorialDocument.AddPath 4');
  407. end;
  408. {@@
  409. Starts writing a Path in multiple steps.
  410. Should be followed by zero or more calls to AddPointToPath
  411. and by a call to EndPath to effectively add the data.
  412. @see StartPath, AddPointToPath
  413. }
  414. procedure TvVectorialDocument.StartPath(AX, AY: Double);
  415. var
  416. segment: T2DSegment;
  417. begin
  418. ClearTmpPath();
  419. FTmpPath.Len := 1;
  420. segment := T2DSegment.Create;
  421. segment.SegmentType := stMoveTo;
  422. segment.X := AX;
  423. segment.Y := AY;
  424. FTmpPath.Points := segment;
  425. FTmpPath.PointsEnd := segment;
  426. end;
  427. {@@
  428. Adds one more point to the end of a Path being
  429. writing in multiple steps.
  430. Does nothing if not called between StartPath and EndPath.
  431. Can be called multiple times to add multiple points.
  432. @see StartPath, EndPath
  433. }
  434. procedure TvVectorialDocument.AddLineToPath(AX, AY: Double);
  435. var
  436. segment: T2DSegment;
  437. begin
  438. segment := T2DSegment.Create;
  439. segment.SegmentType := st2DLine;
  440. segment.X := AX;
  441. segment.Y := AY;
  442. AppendSegmentToTmpPath(segment);
  443. end;
  444. procedure TvVectorialDocument.AddLineToPath(AX, AY, AZ: Double);
  445. var
  446. segment: T3DSegment;
  447. begin
  448. segment := T3DSegment.Create;
  449. segment.SegmentType := st3DLine;
  450. segment.X := AX;
  451. segment.Y := AY;
  452. segment.Z := AZ;
  453. AppendSegmentToTmpPath(segment);
  454. end;
  455. {@@
  456. Adds a bezier element to the path. It starts where the previous element ended
  457. and it goes throw the control points [AX1, AY1] and [AX2, AY2] and ends
  458. in [AX3, AY3].
  459. }
  460. procedure TvVectorialDocument.AddBezierToPath(AX1, AY1, AX2, AY2, AX3,
  461. AY3: Double);
  462. var
  463. segment: T2DBezierSegment;
  464. begin
  465. segment := T2DBezierSegment.Create;
  466. segment.SegmentType := st2DBezier;
  467. segment.X := AX3;
  468. segment.Y := AY3;
  469. segment.X2 := AX1;
  470. segment.Y2 := AY1;
  471. segment.X3 := AX2;
  472. segment.Y3 := AY2;
  473. AppendSegmentToTmpPath(segment);
  474. end;
  475. procedure TvVectorialDocument.AddBezierToPath(AX1, AY1, AZ1, AX2, AY2, AZ2,
  476. AX3, AY3, AZ3: Double);
  477. var
  478. segment: T3DBezierSegment;
  479. begin
  480. segment := T3DBezierSegment.Create;
  481. segment.SegmentType := st3DBezier;
  482. segment.X := AX3;
  483. segment.Y := AY3;
  484. segment.Z := AZ3;
  485. segment.X2 := AX1;
  486. segment.Y2 := AY1;
  487. segment.Z2 := AZ1;
  488. segment.X3 := AX2;
  489. segment.Y3 := AY2;
  490. segment.Z3 := AZ2;
  491. AppendSegmentToTmpPath(segment);
  492. end;
  493. {@@
  494. Finishes writing a Path, which was created in multiple
  495. steps using StartPath and AddPointToPath,
  496. to the document.
  497. Does nothing if there wasn't a previous correspondent call to
  498. StartPath.
  499. @see StartPath, AddPointToPath
  500. }
  501. procedure TvVectorialDocument.EndPath();
  502. begin
  503. if FTmPPath.Len = 0 then Exit;
  504. AddPath(FTmPPath);
  505. ClearTmpPath();
  506. end;
  507. procedure TvVectorialDocument.AddText(AX, AY, AZ: Double; FontName: string; FontSize: integer; AText: utf8string);
  508. var
  509. lText: TvText;
  510. begin
  511. lText := TvText.Create;
  512. lText.Value := AText;
  513. lText.X := AX;
  514. lText.Y := AY;
  515. lText.Z := AZ;
  516. lText.FontName := FontName;
  517. lText.FontSize := FontSize;
  518. FTexts.Add(lText);
  519. end;
  520. procedure TvVectorialDocument.AddText(AX, AY, AZ: Double; AStr: utf8string);
  521. begin
  522. AddText(AX, AY, AZ, '', 10, AStr);
  523. end;
  524. procedure TvVectorialDocument.AddCircle(ACenterX, ACenterY, ACenterZ, ARadius: Double);
  525. var
  526. lCircle: TvCircle;
  527. begin
  528. lCircle := TvCircle.Create;
  529. lCircle.CenterX := ACenterX;
  530. lCircle.CenterY := ACenterY;
  531. lCircle.CenterZ := ACenterZ;
  532. lCircle.Radius := ARadius;
  533. FEntities.Add(lCircle);
  534. end;
  535. procedure TvVectorialDocument.AddCircularArc(ACenterX, ACenterY, ACenterZ,
  536. ARadius, AStartAngle, AEndAngle: Double);
  537. var
  538. lCircularArc: TvCircularArc;
  539. begin
  540. lCircularArc := TvCircularArc.Create;
  541. lCircularArc.CenterX := ACenterX;
  542. lCircularArc.CenterY := ACenterY;
  543. lCircularArc.CenterZ := ACenterZ;
  544. lCircularArc.Radius := ARadius;
  545. lCircularArc.StartAngle := AStartAngle;
  546. lCircularArc.EndAngle := AEndAngle;
  547. FEntities.Add(lCircularArc);
  548. end;
  549. procedure TvVectorialDocument.AddEllipse(CenterX, CenterY, CenterZ,
  550. MajorHalfAxis, MinorHalfAxis, Angle: Double);
  551. var
  552. lEllipse: TvEllipse;
  553. begin
  554. lEllipse := TvEllipse.Create;
  555. lEllipse.CenterX := CenterX;
  556. lEllipse.CenterY := CenterY;
  557. lEllipse.CenterZ := CenterZ;
  558. lEllipse.MajorHalfAxis := MajorHalfAxis;
  559. lEllipse.MinorHalfAxis := MinorHalfAxis;
  560. lEllipse.Angle := Angle;
  561. FEntities.Add(lEllipse);
  562. end;
  563. procedure TvVectorialDocument.AddAlignedDimension(BaseLeft, BaseRight,
  564. DimLeft, DimRight: T3DPoint);
  565. var
  566. lDim: TvAlignedDimension;
  567. begin
  568. lDim := TvAlignedDimension.Create;
  569. lDim.BaseLeft := BaseLeft;
  570. lDim.BaseRight := BaseRight;
  571. lDim.DimensionLeft := DimLeft;
  572. lDim.DimensionRight := DimRight;
  573. FEntities.Add(lDim);
  574. end;
  575. {@@
  576. Convenience method which creates the correct
  577. writer object for a given vector graphics document format.
  578. }
  579. function TvVectorialDocument.CreateVectorialWriter(AFormat: TvVectorialFormat): TvCustomVectorialWriter;
  580. var
  581. i: Integer;
  582. begin
  583. Result := nil;
  584. for i := 0 to Length(GvVectorialFormats) - 1 do
  585. if GvVectorialFormats[i].Format = AFormat then
  586. begin
  587. Result := GvVectorialFormats[i].WriterClass.Create;
  588. Break;
  589. end;
  590. if Result = nil then raise Exception.Create('Unsuported vector graphics format.');
  591. end;
  592. {@@
  593. Convenience method which creates the correct
  594. reader object for a given vector graphics document format.
  595. }
  596. function TvVectorialDocument.CreateVectorialReader(AFormat: TvVectorialFormat): TvCustomVectorialReader;
  597. var
  598. i: Integer;
  599. begin
  600. Result := nil;
  601. for i := 0 to Length(GvVectorialFormats) - 1 do
  602. if GvVectorialFormats[i].Format = AFormat then
  603. begin
  604. Result := GvVectorialFormats[i].ReaderClass.Create;
  605. Break;
  606. end;
  607. if Result = nil then raise Exception.Create('Unsuported vector graphics format.');
  608. end;
  609. procedure TvVectorialDocument.ClearTmpPath();
  610. var
  611. segment, oldsegment: TPathSegment;
  612. begin
  613. // segment := FTmpPath.Points;
  614. // Don't free segments, because they are used when the path is added
  615. // while segment <> nil do
  616. // begin
  617. // oldsegment := segment;
  618. // segment := segment^.Next;
  619. // oldsegment^.Free;
  620. // end;
  621. FTmpPath.Points := nil;
  622. FTmpPath.PointsEnd := nil;
  623. FTmpPath.Len := 0;
  624. end;
  625. procedure TvVectorialDocument.AppendSegmentToTmpPath(ASegment: TPathSegment);
  626. var
  627. L: Integer;
  628. begin
  629. if FTmpPath.PointsEnd = nil then
  630. Exception.Create('[TvVectorialDocument.AppendSegmentToTmpPath]' + Str_Error_Nil_Path);
  631. L := FTmpPath.Len;
  632. Inc(FTmpPath.Len);
  633. // Adds the element to the end of the list
  634. FTmpPath.PointsEnd.Next := ASegment;
  635. ASegment.Previous := FTmpPath.PointsEnd;
  636. FTmpPath.PointsEnd := ASegment;
  637. end;
  638. {@@
  639. Writes the document to a file.
  640. If the file doesn't exist, it will be created.
  641. }
  642. procedure TvVectorialDocument.WriteToFile(AFileName: string; AFormat: TvVectorialFormat);
  643. var
  644. AWriter: TvCustomVectorialWriter;
  645. begin
  646. AWriter := CreateVectorialWriter(AFormat);
  647. try
  648. AWriter.WriteToFile(AFileName, Self);
  649. finally
  650. AWriter.Free;
  651. end;
  652. end;
  653. {@@
  654. Writes the document to a stream
  655. }
  656. procedure TvVectorialDocument.WriteToStream(AStream: TStream; AFormat: TvVectorialFormat);
  657. var
  658. AWriter: TvCustomVectorialWriter;
  659. begin
  660. AWriter := CreateVectorialWriter(AFormat);
  661. try
  662. AWriter.WriteToStream(AStream, Self);
  663. finally
  664. AWriter.Free;
  665. end;
  666. end;
  667. procedure TvVectorialDocument.WriteToStrings(AStrings: TStrings;
  668. AFormat: TvVectorialFormat);
  669. var
  670. AWriter: TvCustomVectorialWriter;
  671. begin
  672. AWriter := CreateVectorialWriter(AFormat);
  673. try
  674. AWriter.WriteToStrings(AStrings, Self);
  675. finally
  676. AWriter.Free;
  677. end;
  678. end;
  679. {@@
  680. Reads the document from a file.
  681. Any current contents will be removed.
  682. }
  683. procedure TvVectorialDocument.ReadFromFile(AFileName: string;
  684. AFormat: TvVectorialFormat);
  685. var
  686. AReader: TvCustomVectorialReader;
  687. begin
  688. Self.Clear;
  689. AReader := CreateVectorialReader(AFormat);
  690. try
  691. AReader.ReadFromFile(AFileName, Self);
  692. finally
  693. AReader.Free;
  694. end;
  695. end;
  696. {@@
  697. Reads the document from a stream.
  698. Any current contents will be removed.
  699. }
  700. procedure TvVectorialDocument.ReadFromStream(AStream: TStream;
  701. AFormat: TvVectorialFormat);
  702. var
  703. AReader: TvCustomVectorialReader;
  704. begin
  705. Self.Clear;
  706. AReader := CreateVectorialReader(AFormat);
  707. try
  708. AReader.ReadFromStream(AStream, Self);
  709. finally
  710. AReader.Free;
  711. end;
  712. end;
  713. procedure TvVectorialDocument.ReadFromStrings(AStrings: TStrings;
  714. AFormat: TvVectorialFormat);
  715. var
  716. AReader: TvCustomVectorialReader;
  717. begin
  718. Self.Clear;
  719. AReader := CreateVectorialReader(AFormat);
  720. try
  721. AReader.ReadFromStrings(AStrings, Self);
  722. finally
  723. AReader.Free;
  724. end;
  725. end;
  726. class function TvVectorialDocument.GetFormatFromExtension(AFileName: string
  727. ): TvVectorialFormat;
  728. var
  729. lExt: string;
  730. begin
  731. lExt := ExtractFileExt(AFileName);
  732. if AnsiCompareText(lExt, STR_PDF_EXTENSION) = 0 then Result := vfPDF
  733. else if AnsiCompareText(lExt, STR_POSTSCRIPT_EXTENSION) = 0 then Result := vfPostScript
  734. else if AnsiCompareText(lExt, STR_SVG_EXTENSION) = 0 then Result := vfSVG
  735. else if AnsiCompareText(lExt, STR_CORELDRAW_EXTENSION) = 0 then Result := vfCorelDrawCDR
  736. else if AnsiCompareText(lExt, STR_WINMETAFILE_EXTENSION) = 0 then Result := vfWindowsMetafileWMF
  737. else
  738. raise Exception.Create('TvVectorialDocument.GetFormatFromExtension: The extension (' + lExt + ') doesn''t match any supported formats.');
  739. end;
  740. function TvVectorialDocument.GetDetailedFileFormat(): string;
  741. begin
  742. end;
  743. function TvVectorialDocument.GetPath(ANum: Cardinal): TPath;
  744. begin
  745. if ANum >= FPaths.Count then raise Exception.Create('TvVectorialDocument.GetPath: Path number out of bounds');
  746. if FPaths.Items[ANum] = nil then raise Exception.Create('TvVectorialDocument.GetPath: Invalid Path number');
  747. Result := TPath(FPaths.Items[ANum]);
  748. end;
  749. function TvVectorialDocument.GetPathCount: Integer;
  750. begin
  751. Result := FPaths.Count;
  752. end;
  753. function TvVectorialDocument.GetText(ANum: Cardinal): TvText;
  754. begin
  755. if ANum >= FTexts.Count then raise Exception.Create('TvVectorialDocument.GetText: Text number out of bounds');
  756. if FTexts.Items[ANum] = nil then raise Exception.Create('TvVectorialDocument.GetText: Invalid Text number');
  757. Result := TvText(FTexts.Items[ANum]);
  758. end;
  759. function TvVectorialDocument.GetTextCount: Integer;
  760. begin
  761. Result := FTexts.Count;
  762. end;
  763. function TvVectorialDocument.GetEntity(ANum: Cardinal): TvEntity;
  764. begin
  765. if ANum >= FEntities.Count then raise Exception.Create('TvVectorialDocument.GetEntity: Entity number out of bounds');
  766. if FEntities.Items[ANum] = nil then raise Exception.Create('TvVectorialDocument.GetEntity: Invalid Entity number');
  767. Result := TvEntity(FEntities.Items[ANum]);
  768. end;
  769. function TvVectorialDocument.GetEntityCount: Integer;
  770. begin
  771. Result := FEntities.Count;
  772. end;
  773. {@@
  774. Clears all data in the document
  775. }
  776. procedure TvVectorialDocument.Clear;
  777. begin
  778. RemoveAllPaths();
  779. RemoveAllTexts();
  780. end;
  781. { TvCustomVectorialReader }
  782. constructor TvCustomVectorialReader.Create;
  783. begin
  784. inherited Create;
  785. end;
  786. procedure TvCustomVectorialReader.ReadFromFile(AFileName: string; AData: TvVectorialDocument);
  787. var
  788. FileStream: TFileStream;
  789. begin
  790. FileStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone);
  791. try
  792. ReadFromStream(FileStream, AData);
  793. finally
  794. FileStream.Free;
  795. end;
  796. end;
  797. procedure TvCustomVectorialReader.ReadFromStream(AStream: TStream;
  798. AData: TvVectorialDocument);
  799. var
  800. AStringStream: TStringStream;
  801. AStrings: TStringList;
  802. begin
  803. AStringStream := TStringStream.Create('');
  804. AStrings := TStringList.Create;
  805. try
  806. AStringStream.CopyFrom(AStream, AStream.Size);
  807. AStringStream.Seek(0, soFromBeginning);
  808. AStrings.Text := AStringStream.DataString;
  809. ReadFromStrings(AStrings, AData);
  810. finally
  811. AStringStream.Free;
  812. AStrings.Free;
  813. end;
  814. end;
  815. procedure TvCustomVectorialReader.ReadFromStrings(AStrings: TStrings;
  816. AData: TvVectorialDocument);
  817. var
  818. AStringStream: TStringStream;
  819. begin
  820. AStringStream := TStringStream.Create('');
  821. try
  822. AStringStream.WriteString(AStrings.Text);
  823. AStringStream.Seek(0, soFromBeginning);
  824. ReadFromStream(AStringStream, AData);
  825. finally
  826. AStringStream.Free;
  827. end;
  828. end;
  829. { TsCustomSpreadWriter }
  830. constructor TvCustomVectorialWriter.Create;
  831. begin
  832. inherited Create;
  833. end;
  834. {@@
  835. Default file writting method.
  836. Opens the file and calls WriteToStream
  837. @param AFileName The output file name.
  838. If the file already exists it will be replaced.
  839. @param AData The Workbook to be saved.
  840. @see TsWorkbook
  841. }
  842. procedure TvCustomVectorialWriter.WriteToFile(AFileName: string; AData: TvVectorialDocument);
  843. var
  844. OutputFile: TFileStream;
  845. begin
  846. OutputFile := TFileStream.Create(AFileName, fmCreate or fmOpenWrite);
  847. try
  848. WriteToStream(OutputFile, AData);
  849. finally
  850. OutputFile.Free;
  851. end;
  852. end;
  853. {@@
  854. The default stream writer just uses WriteToStrings
  855. }
  856. procedure TvCustomVectorialWriter.WriteToStream(AStream: TStream;
  857. AData: TvVectorialDocument);
  858. var
  859. lStringList: TStringList;
  860. begin
  861. lStringList := TStringList.Create;
  862. try
  863. WriteToStrings(lStringList, AData);
  864. lStringList.SaveToStream(AStream);
  865. finally
  866. lStringList.Free;
  867. end;
  868. end;
  869. procedure TvCustomVectorialWriter.WriteToStrings(AStrings: TStrings;
  870. AData: TvVectorialDocument);
  871. begin
  872. end;
  873. { TPath }
  874. procedure TPath.Assign(APath: TPath);
  875. begin
  876. Len := APath.Len;
  877. Points := APath.Points;
  878. PointsEnd := APath.PointsEnd;
  879. CurPoint := APath.CurPoint;
  880. end;
  881. function TPath.Count(): TPathSegment;
  882. begin
  883. end;
  884. procedure TPath.PrepareForSequentialReading;
  885. begin
  886. CurPoint := nil;
  887. end;
  888. function TPath.Next(): TPathSegment;
  889. begin
  890. if CurPoint = nil then Result := Points
  891. else Result := CurPoint.Next;
  892. CurPoint := Result;
  893. end;
  894. finalization
  895. SetLength(GvVectorialFormats, 0);
  896. end.