fpvectorial.pas 30 KB

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