fpvectorial.pas 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480
  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, fpimage;
  17. type
  18. TvVectorialFormat = (
  19. { Multi-purpose document formats }
  20. vfPDF, vfSVG, vfCorelDrawCDR, vfWindowsMetafileWMF,
  21. { CAD formats }
  22. vfDXF,
  23. { Printing formats }
  24. vfPostScript, vfEncapsulatedPostScript,
  25. { GCode formats }
  26. vfGCodeAvisoCNCPrototipoV5, vfGCodeAvisoCNCPrototipoV6);
  27. const
  28. { Default extensions }
  29. { Multi-purpose document formats }
  30. STR_PDF_EXTENSION = '.pdf';
  31. STR_POSTSCRIPT_EXTENSION = '.ps';
  32. STR_SVG_EXTENSION = '.svg';
  33. STR_CORELDRAW_EXTENSION = '.cdr';
  34. STR_WINMETAFILE_EXTENSION = '.wmf';
  35. STR_AUTOCAD_EXCHANGE_EXTENSION = '.dxf';
  36. STR_ENCAPSULATEDPOSTSCRIPT_EXTENSION = '.eps';
  37. type
  38. { Pen, Brush and Font }
  39. TvPen = record
  40. Color: TFPColor;
  41. Style: TFPPenStyle;
  42. Width: Integer;
  43. end;
  44. TvBrush = record
  45. Color: TFPColor;
  46. Style: TFPBrushStyle;
  47. end;
  48. TvFont = record
  49. Color: TFPColor;
  50. Size: integer;
  51. Name: utf8string;
  52. {@@
  53. Font orientation is measured in degrees and uses the
  54. same direction as the LCL TFont.orientation, which is counter-clockwise.
  55. Zero is the normal, horizontal, orientation, directed to the right.
  56. }
  57. Orientation: Double;
  58. end;
  59. { Coordinates and polyline segments }
  60. T3DPoint = record
  61. X, Y, Z: Double;
  62. end;
  63. P3DPoint = ^T3DPoint;
  64. TSegmentType = (
  65. st2DLine, st2DLineWithPen, st2DBezier,
  66. st3DLine, st3DBezier, stMoveTo);
  67. {@@
  68. The coordinates in fpvectorial are given in millimiters and
  69. the starting point is in the bottom-left corner of the document.
  70. The X grows to the right and the Y grows to the top.
  71. }
  72. { TPathSegment }
  73. TPathSegment = class
  74. public
  75. SegmentType: TSegmentType;
  76. // Fields for linking the list
  77. Previous: TPathSegment;
  78. Next: TPathSegment;
  79. end;
  80. {@@
  81. In a 2D segment, the X and Y coordinates represent usually the
  82. final point of the segment, being that it starts where the previous
  83. segment ends. The exception is for the first segment of all, which simply
  84. holds the starting point for the drawing and should always be of the type
  85. stMoveTo.
  86. }
  87. T2DSegment = class(TPathSegment)
  88. public
  89. X, Y: Double;
  90. end;
  91. T2DSegmentWithPen = class(T2DSegment)
  92. public
  93. Pen: TvPen;
  94. end;
  95. {@@
  96. In Bezier segments, we remain using the X and Y coordinates for the ending point.
  97. The starting point is where the previous segment ended, so that the intermediary
  98. bezier control points are [X2, Y2] and [X3, Y3].
  99. }
  100. T2DBezierSegment = class(T2DSegment)
  101. public
  102. X2, Y2: Double;
  103. X3, Y3: Double;
  104. end;
  105. T3DSegment = class(TPathSegment)
  106. public
  107. {@@
  108. Coordinates of the end of the segment.
  109. For the first segment, this is the starting point.
  110. }
  111. X, Y, Z: Double;
  112. end;
  113. T3DBezierSegment = class(T3DSegment)
  114. public
  115. X2, Y2, Z2: Double;
  116. X3, Y3, Z3: Double;
  117. end;
  118. TvFindEntityResult = (vfrNotFound, vfrFound, vfrSubpartFound);
  119. { Now all elements }
  120. {@@
  121. All elements should derive from TvEntity, regardless of whatever properties
  122. they might contain.
  123. }
  124. { TvEntity }
  125. TvEntity = class
  126. public
  127. X, Y, Z: Double;
  128. {@@ The global Pen for the entire entity. In the case of paths, individual
  129. elements might be able to override this setting. }
  130. Pen: TvPen;
  131. {@@ The global Brush for the entire entity. In the case of paths, individual
  132. elements might be able to override this setting. }
  133. Brush: TvBrush;
  134. constructor Create; virtual;
  135. procedure CalculateBoundingBox(var ALeft, ATop, ARight, ABottom: Double); virtual;
  136. procedure ExpandBoundingBox(var ALeft, ATop, ARight, ABottom: Double);
  137. function TryToSelect(APos: TPoint): TvFindEntityResult; virtual;
  138. procedure Translate(ADeltaX, ADeltaY: Integer); virtual;
  139. end;
  140. TvClipMode = (vcmNonzeroWindingRule, vcmEvenOddRule);
  141. TPath = class(TvEntity)
  142. Len: Integer;
  143. Points: TPathSegment; // Beginning of the double-linked list
  144. PointsEnd: TPathSegment;// End of the double-linked list
  145. CurPoint: TPathSegment; // Used in PrepareForSequentialReading and Next
  146. ClipPath: TPath;
  147. ClipMode: TvClipMode;
  148. procedure Assign(ASource: TPath);
  149. procedure PrepareForSequentialReading;
  150. function Next(): TPathSegment;
  151. procedure CalculateBoundingBox(var ALeft, ATop, ARight, ABottom: Double); override;
  152. procedure AppendSegment(ASegment: TPathSegment);
  153. end;
  154. {@@
  155. TvText represents a text entity.
  156. }
  157. { TvText }
  158. TvText = class(TvEntity)
  159. public
  160. Value: TStringList;
  161. Font: TvFont;
  162. constructor Create; override;
  163. destructor Destroy; override;
  164. function TryToSelect(APos: TPoint): TvFindEntityResult; override;
  165. end;
  166. {@@
  167. }
  168. TvCircle = class(TvEntity)
  169. public
  170. Radius: Double;
  171. end;
  172. {@@
  173. }
  174. TvCircularArc = class(TvEntity)
  175. public
  176. Radius: Double;
  177. {@@ The Angle is measured in degrees in relation to the positive X axis }
  178. StartAngle, EndAngle: Double;
  179. end;
  180. {@@
  181. }
  182. TvEllipse = class(TvEntity)
  183. public
  184. // Mandatory fields
  185. MajorHalfAxis, MinorHalfAxis: Double;
  186. {@@ The Angle is measured in degrees in relation to the positive X axis }
  187. Angle: Double;
  188. // Calculated fields
  189. BoundingRect: TRect;
  190. procedure CalculateBoundingRectangle;
  191. end;
  192. {@@
  193. The brush has no effect in this class
  194. DimensionLeft ---text--- DimensionRight
  195. | |
  196. | | BaseRight
  197. |
  198. | BaseLeft
  199. }
  200. { TvAlignedDimension }
  201. TvAlignedDimension = class(TvEntity)
  202. public
  203. // Mandatory fields
  204. BaseLeft, BaseRight, DimensionLeft, DimensionRight: T3DPoint;
  205. end;
  206. {@@
  207. Vectorial images can contain raster images inside them and this entity
  208. represents this.
  209. If the Width and Height differ from the same data in the image, then
  210. the raster image will be stretched.
  211. Note that TFPCustomImage does not implement a storage, so the property
  212. RasterImage should be filled with either a FPImage.TFPMemoryImage or with
  213. a TLazIntfImage. The property RasterImage might be nil.
  214. }
  215. TvRasterImage = class(TvEntity)
  216. public
  217. RasterImage: TFPCustomImage;
  218. Top, Left, Width, Height: Double;
  219. end;
  220. type
  221. TvCustomVectorialWriter = class;
  222. TvCustomVectorialReader = class;
  223. TvVectorialPage = class;
  224. { TvVectorialDocument }
  225. TvVectorialDocument = class
  226. private
  227. FPages: TFPList;
  228. FCurrentPageIndex: Integer;
  229. function CreateVectorialWriter(AFormat: TvVectorialFormat): TvCustomVectorialWriter;
  230. function CreateVectorialReader(AFormat: TvVectorialFormat): TvCustomVectorialReader;
  231. public
  232. Width, Height: Double; // in millimeters
  233. Name: string;
  234. // User-Interface information
  235. ZoomLevel: Double; // 1 = 100%
  236. { Selection fields }
  237. SelectedvElement: TvEntity;
  238. { Base methods }
  239. constructor Create; virtual;
  240. destructor Destroy; override;
  241. procedure Assign(ASource: TvVectorialDocument);
  242. procedure AssignTo(ADest: TvVectorialDocument);
  243. procedure WriteToFile(AFileName: string; AFormat: TvVectorialFormat); overload;
  244. procedure WriteToFile(AFileName: string); overload;
  245. procedure WriteToStream(AStream: TStream; AFormat: TvVectorialFormat);
  246. procedure WriteToStrings(AStrings: TStrings; AFormat: TvVectorialFormat);
  247. procedure ReadFromFile(AFileName: string; AFormat: TvVectorialFormat); overload;
  248. procedure ReadFromFile(AFileName: string); overload;
  249. procedure ReadFromStream(AStream: TStream; AFormat: TvVectorialFormat);
  250. procedure ReadFromStrings(AStrings: TStrings; AFormat: TvVectorialFormat);
  251. class function GetFormatFromExtension(AFileName: string): TvVectorialFormat;
  252. function GetDetailedFileFormat(): string;
  253. procedure GuessDocumentSize();
  254. procedure GuessGoodZoomLevel(AScreenSize: Integer = 500);
  255. { Page methods }
  256. function GetPage(AIndex: Integer): TvVectorialPage;
  257. function GetPageCount: Integer;
  258. function GetCurrentPage: TvVectorialPage;
  259. procedure SetCurrentPage(AIndex: Integer);
  260. function AddPage(): TvVectorialPage;
  261. { Data removing methods }
  262. procedure Clear; virtual;
  263. end;
  264. { TvVectorialPage }
  265. TvVectorialPage = class
  266. private
  267. FEntities: TFPList;
  268. FTmpPath: TPath;
  269. FTmpText: TvText;
  270. //procedure RemoveCallback(data, arg: pointer);
  271. procedure ClearTmpPath();
  272. procedure AppendSegmentToTmpPath(ASegment: TPathSegment);
  273. public
  274. Width, Height: Double; // in millimeters
  275. Owner: TvVectorialDocument;
  276. { Base methods }
  277. constructor Create(AOwner: TvVectorialDocument); virtual;
  278. destructor Destroy; override;
  279. procedure Assign(ASource: TvVectorialPage);
  280. { Data reading methods }
  281. function GetEntity(ANum: Cardinal): TvEntity;
  282. function GetEntitiesCount: Integer;
  283. function FindAndSelectEntity(Pos: TPoint): TvFindEntityResult;
  284. { Data removing methods }
  285. procedure Clear; virtual;
  286. { Data writing methods }
  287. function AddEntity(AEntity: TvEntity): Integer;
  288. procedure AddPathCopyMem(APath: TPath);
  289. procedure StartPath(AX, AY: Double); overload;
  290. procedure StartPath(); overload;
  291. procedure AddMoveToPath(AX, AY: Double);
  292. procedure AddLineToPath(AX, AY: Double); overload;
  293. procedure AddLineToPath(AX, AY: Double; AColor: TFPColor); overload;
  294. procedure AddLineToPath(AX, AY, AZ: Double); overload;
  295. procedure GetCurrentPathPenPos(var AX, AY: Double);
  296. procedure AddBezierToPath(AX1, AY1, AX2, AY2, AX3, AY3: Double); overload;
  297. procedure AddBezierToPath(AX1, AY1, AZ1, AX2, AY2, AZ2, AX3, AY3, AZ3: Double); overload;
  298. procedure SetBrushColor(AColor: TFPColor);
  299. procedure SetBrushStyle(AStyle: TFPBrushStyle);
  300. procedure SetPenColor(AColor: TFPColor);
  301. procedure SetPenStyle(AStyle: TFPPenStyle);
  302. procedure SetPenWidth(AWidth: Integer);
  303. procedure SetClipPath(AClipPath: TPath; AClipMode: TvClipMode);
  304. procedure EndPath();
  305. procedure AddText(AX, AY, AZ: Double; FontName: string; FontSize: integer; AText: utf8string); overload;
  306. procedure AddText(AX, AY: Double; AStr: utf8string); overload;
  307. procedure AddText(AX, AY, AZ: Double; AStr: utf8string); overload;
  308. procedure AddCircle(ACenterX, ACenterY, ARadius: Double);
  309. procedure AddCircularArc(ACenterX, ACenterY, ARadius, AStartAngle, AEndAngle: Double; AColor: TFPColor);
  310. procedure AddEllipse(CenterX, CenterY, MajorHalfAxis, MinorHalfAxis, Angle: Double);
  311. // Dimensions
  312. procedure AddAlignedDimension(BaseLeft, BaseRight, DimLeft, DimRight: T3DPoint);
  313. end;
  314. {@@ TvVectorialReader class reference type }
  315. TvVectorialReaderClass = class of TvCustomVectorialReader;
  316. { TvCustomVectorialReader }
  317. TvCustomVectorialReader = class
  318. public
  319. { General reading methods }
  320. constructor Create; virtual;
  321. procedure ReadFromFile(AFileName: string; AData: TvVectorialDocument); virtual;
  322. procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); virtual;
  323. procedure ReadFromStrings(AStrings: TStrings; AData: TvVectorialDocument); virtual;
  324. end;
  325. {@@ TvVectorialWriter class reference type }
  326. TvVectorialWriterClass = class of TvCustomVectorialWriter;
  327. {@@ TvCustomVectorialWriter }
  328. { TvCustomVectorialWriter }
  329. TvCustomVectorialWriter = class
  330. public
  331. { General writing methods }
  332. constructor Create; virtual;
  333. procedure WriteToFile(AFileName: string; AData: TvVectorialDocument); virtual;
  334. procedure WriteToStream(AStream: TStream; AData: TvVectorialDocument); virtual;
  335. procedure WriteToStrings(AStrings: TStrings; AData: TvVectorialDocument); virtual;
  336. end;
  337. {@@ List of registered formats }
  338. TvVectorialFormatData = record
  339. ReaderClass: TvVectorialReaderClass;
  340. WriterClass: TvVectorialWriterClass;
  341. ReaderRegistered: Boolean;
  342. WriterRegistered: Boolean;
  343. Format: TvVectorialFormat;
  344. end;
  345. var
  346. GvVectorialFormats: array of TvVectorialFormatData;
  347. procedure RegisterVectorialReader(
  348. AReaderClass: TvVectorialReaderClass;
  349. AFormat: TvVectorialFormat);
  350. procedure RegisterVectorialWriter(
  351. AWriterClass: TvVectorialWriterClass;
  352. AFormat: TvVectorialFormat);
  353. function Make2DPoint(AX, AY: Double): T3DPoint;
  354. implementation
  355. const
  356. Str_Error_Nil_Path = ' The program attempted to add a segment before creating a path';
  357. {@@
  358. Registers a new reader for a format
  359. }
  360. procedure RegisterVectorialReader(
  361. AReaderClass: TvVectorialReaderClass;
  362. AFormat: TvVectorialFormat);
  363. var
  364. i, len: Integer;
  365. FormatInTheList: Boolean;
  366. begin
  367. len := Length(GvVectorialFormats);
  368. FormatInTheList := False;
  369. { First search for the format in the list }
  370. for i := 0 to len - 1 do
  371. begin
  372. if GvVectorialFormats[i].Format = AFormat then
  373. begin
  374. if GvVectorialFormats[i].ReaderRegistered then
  375. raise Exception.Create('RegisterVectorialReader: Reader class for format ' {+ AFormat} + ' already registered.');
  376. GvVectorialFormats[i].ReaderRegistered := True;
  377. GvVectorialFormats[i].ReaderClass := AReaderClass;
  378. FormatInTheList := True;
  379. Break;
  380. end;
  381. end;
  382. { If not already in the list, then add it }
  383. if not FormatInTheList then
  384. begin
  385. SetLength(GvVectorialFormats, len + 1);
  386. GvVectorialFormats[len].ReaderClass := AReaderClass;
  387. GvVectorialFormats[len].WriterClass := nil;
  388. GvVectorialFormats[len].ReaderRegistered := True;
  389. GvVectorialFormats[len].WriterRegistered := False;
  390. GvVectorialFormats[len].Format := AFormat;
  391. end;
  392. end;
  393. {@@
  394. Registers a new writer for a format
  395. }
  396. procedure RegisterVectorialWriter(
  397. AWriterClass: TvVectorialWriterClass;
  398. AFormat: TvVectorialFormat);
  399. var
  400. i, len: Integer;
  401. FormatInTheList: Boolean;
  402. begin
  403. len := Length(GvVectorialFormats);
  404. FormatInTheList := False;
  405. { First search for the format in the list }
  406. for i := 0 to len - 1 do
  407. begin
  408. if GvVectorialFormats[i].Format = AFormat then
  409. begin
  410. if GvVectorialFormats[i].WriterRegistered then
  411. raise Exception.Create('RegisterVectorialWriter: Writer class for format ' + {AFormat +} ' already registered.');
  412. GvVectorialFormats[i].WriterRegistered := True;
  413. GvVectorialFormats[i].WriterClass := AWriterClass;
  414. FormatInTheList := True;
  415. Break;
  416. end;
  417. end;
  418. { If not already in the list, then add it }
  419. if not FormatInTheList then
  420. begin
  421. SetLength(GvVectorialFormats, len + 1);
  422. GvVectorialFormats[len].ReaderClass := nil;
  423. GvVectorialFormats[len].WriterClass := AWriterClass;
  424. GvVectorialFormats[len].ReaderRegistered := False;
  425. GvVectorialFormats[len].WriterRegistered := True;
  426. GvVectorialFormats[len].Format := AFormat;
  427. end;
  428. end;
  429. function Make2DPoint(AX, AY: Double): T3DPoint;
  430. begin
  431. Result.X := AX;
  432. Result.Y := AY;
  433. Result.Z := 0;
  434. end;
  435. { TvVectorialPage }
  436. procedure TvVectorialPage.ClearTmpPath;
  437. var
  438. segment, oldsegment: TPathSegment;
  439. begin
  440. FTmpPath.Points := nil;
  441. FTmpPath.PointsEnd := nil;
  442. FTmpPath.Len := 0;
  443. FTmpPath.Brush.Color := colBlue;
  444. FTmpPath.Brush.Style := bsClear;
  445. FTmpPath.Pen.Color := colBlack;
  446. FTmpPath.Pen.Style := psSolid;
  447. FTmpPath.Pen.Width := 1;
  448. end;
  449. procedure TvVectorialPage.AppendSegmentToTmpPath(ASegment: TPathSegment);
  450. begin
  451. FTmpPath.AppendSegment(ASegment);
  452. end;
  453. constructor TvVectorialPage.Create(AOwner: TvVectorialDocument);
  454. begin
  455. inherited Create;
  456. FEntities := TFPList.Create;
  457. FTmpPath := TPath.Create;
  458. Owner := AOwner;
  459. end;
  460. destructor TvVectorialPage.Destroy;
  461. begin
  462. Clear;
  463. FEntities.Free;
  464. inherited Destroy;
  465. end;
  466. procedure TvVectorialPage.Assign(ASource: TvVectorialPage);
  467. var
  468. i: Integer;
  469. begin
  470. Clear;
  471. for i := 0 to ASource.GetEntitiesCount - 1 do
  472. Self.AddEntity(ASource.GetEntity(i));
  473. end;
  474. function TvVectorialPage.GetEntity(ANum: Cardinal): TvEntity;
  475. begin
  476. if ANum >= FEntities.Count then raise Exception.Create('TvVectorialDocument.GetEntity: Entity number out of bounds');
  477. if FEntities.Items[ANum] = nil then raise Exception.Create('TvVectorialDocument.GetEntity: Invalid Entity number');
  478. Result := TvEntity(FEntities.Items[ANum]);
  479. end;
  480. function TvVectorialPage.GetEntitiesCount: Integer;
  481. begin
  482. Result := FEntities.Count;
  483. end;
  484. function TvVectorialPage.FindAndSelectEntity(Pos: TPoint): TvFindEntityResult;
  485. var
  486. lEntity: TvEntity;
  487. i: Integer;
  488. begin
  489. Result := vfrNotFound;
  490. for i := 0 to GetEntitiesCount() - 1 do
  491. begin
  492. lEntity := GetEntity(i);
  493. Result := lEntity.TryToSelect(Pos);
  494. if Result <> vfrNotFound then
  495. begin
  496. Owner.SelectedvElement := lEntity;
  497. Exit;
  498. end;
  499. end;
  500. end;
  501. procedure TvVectorialPage.Clear;
  502. begin
  503. FEntities.Clear();
  504. end;
  505. {@@
  506. Adds an entity to the document and returns it's current index
  507. }
  508. function TvVectorialPage.AddEntity(AEntity: TvEntity): Integer;
  509. begin
  510. Result := FEntities.Count;
  511. FEntities.Add(Pointer(AEntity));
  512. end;
  513. procedure TvVectorialPage.AddPathCopyMem(APath: TPath);
  514. var
  515. lPath: TPath;
  516. Len: Integer;
  517. begin
  518. lPath := TPath.Create;
  519. lPath.Assign(APath);
  520. AddEntity(lPath);
  521. //WriteLn(':>TvVectorialDocument.AddPath 1 Len = ', Len);
  522. end;
  523. {@@
  524. Starts writing a Path in multiple steps.
  525. Should be followed by zero or more calls to AddPointToPath
  526. and by a call to EndPath to effectively add the data.
  527. @see EndPath, AddPointToPath
  528. }
  529. procedure TvVectorialPage.StartPath(AX, AY: Double);
  530. var
  531. segment: T2DSegment;
  532. begin
  533. ClearTmpPath();
  534. FTmpPath.Len := 1;
  535. segment := T2DSegment.Create;
  536. segment.SegmentType := stMoveTo;
  537. segment.X := AX;
  538. segment.Y := AY;
  539. FTmpPath.Points := segment;
  540. FTmpPath.PointsEnd := segment;
  541. end;
  542. procedure TvVectorialPage.StartPath;
  543. begin
  544. ClearTmpPath();
  545. end;
  546. procedure TvVectorialPage.AddMoveToPath(AX, AY: Double);
  547. var
  548. segment: T2DSegment;
  549. begin
  550. segment := T2DSegment.Create;
  551. segment.SegmentType := stMoveTo;
  552. segment.X := AX;
  553. segment.Y := AY;
  554. AppendSegmentToTmpPath(segment);
  555. end;
  556. {@@
  557. Adds one more point to the end of a Path being
  558. writing in multiple steps.
  559. Does nothing if not called between StartPath and EndPath.
  560. Can be called multiple times to add multiple points.
  561. @see StartPath, EndPath
  562. }
  563. procedure TvVectorialPage.AddLineToPath(AX, AY: Double);
  564. var
  565. segment: T2DSegment;
  566. begin
  567. segment := T2DSegment.Create;
  568. segment.SegmentType := st2DLine;
  569. segment.X := AX;
  570. segment.Y := AY;
  571. AppendSegmentToTmpPath(segment);
  572. end;
  573. procedure TvVectorialPage.AddLineToPath(AX, AY: Double; AColor: TFPColor);
  574. var
  575. segment: T2DSegmentWithPen;
  576. begin
  577. segment := T2DSegmentWithPen.Create;
  578. segment.SegmentType := st2DLineWithPen;
  579. segment.X := AX;
  580. segment.Y := AY;
  581. segment.Pen.Color := AColor;
  582. AppendSegmentToTmpPath(segment);
  583. end;
  584. procedure TvVectorialPage.AddLineToPath(AX, AY, AZ: Double);
  585. var
  586. segment: T3DSegment;
  587. begin
  588. segment := T3DSegment.Create;
  589. segment.SegmentType := st3DLine;
  590. segment.X := AX;
  591. segment.Y := AY;
  592. segment.Z := AZ;
  593. AppendSegmentToTmpPath(segment);
  594. end;
  595. {@@
  596. Gets the current Pen Pos in the temporary path
  597. }
  598. procedure TvVectorialPage.GetCurrentPathPenPos(var AX, AY: Double);
  599. begin
  600. // Check if we are the first segment in the tmp path
  601. if FTmpPath.PointsEnd = nil then raise Exception.Create('[TvVectorialDocument.GetCurrentPathPenPos] One cannot obtain the Pen Pos if there are no segments in the temporary path');
  602. AX := T2DSegment(FTmpPath.PointsEnd).X;
  603. AY := T2DSegment(FTmpPath.PointsEnd).Y;
  604. end;
  605. {@@
  606. Adds a bezier element to the path. It starts where the previous element ended
  607. and it goes throw the control points [AX1, AY1] and [AX2, AY2] and ends
  608. in [AX3, AY3].
  609. }
  610. procedure TvVectorialPage.AddBezierToPath(AX1, AY1, AX2, AY2, AX3, AY3: Double);
  611. var
  612. segment: T2DBezierSegment;
  613. begin
  614. segment := T2DBezierSegment.Create;
  615. segment.SegmentType := st2DBezier;
  616. segment.X := AX3;
  617. segment.Y := AY3;
  618. segment.X2 := AX1;
  619. segment.Y2 := AY1;
  620. segment.X3 := AX2;
  621. segment.Y3 := AY2;
  622. AppendSegmentToTmpPath(segment);
  623. end;
  624. procedure TvVectorialPage.AddBezierToPath(AX1, AY1, AZ1, AX2, AY2, AZ2, AX3, AY3, AZ3: Double);
  625. var
  626. segment: T3DBezierSegment;
  627. begin
  628. segment := T3DBezierSegment.Create;
  629. segment.SegmentType := st3DBezier;
  630. segment.X := AX3;
  631. segment.Y := AY3;
  632. segment.Z := AZ3;
  633. segment.X2 := AX1;
  634. segment.Y2 := AY1;
  635. segment.Z2 := AZ1;
  636. segment.X3 := AX2;
  637. segment.Y3 := AY2;
  638. segment.Z3 := AZ2;
  639. AppendSegmentToTmpPath(segment);
  640. end;
  641. procedure TvVectorialPage.SetBrushColor(AColor: TFPColor);
  642. begin
  643. FTmPPath.Brush.Color := AColor;
  644. end;
  645. procedure TvVectorialPage.SetBrushStyle(AStyle: TFPBrushStyle);
  646. begin
  647. FTmPPath.Brush.Style := AStyle;
  648. end;
  649. procedure TvVectorialPage.SetPenColor(AColor: TFPColor);
  650. begin
  651. FTmPPath.Pen.Color := AColor;
  652. end;
  653. procedure TvVectorialPage.SetPenStyle(AStyle: TFPPenStyle);
  654. begin
  655. FTmPPath.Pen.Style := AStyle;
  656. end;
  657. procedure TvVectorialPage.SetPenWidth(AWidth: Integer);
  658. begin
  659. FTmPPath.Pen.Width := AWidth;
  660. end;
  661. procedure TvVectorialPage.SetClipPath(AClipPath: TPath; AClipMode: TvClipMode);
  662. begin
  663. FTmPPath.ClipPath := AClipPath;
  664. FTmPPath.ClipMode := AClipMode;
  665. end;
  666. {@@
  667. Finishes writing a Path, which was created in multiple
  668. steps using StartPath and AddPointToPath,
  669. to the document.
  670. Does nothing if there wasn't a previous correspondent call to
  671. StartPath.
  672. @see StartPath, AddPointToPath
  673. }
  674. procedure TvVectorialPage.EndPath;
  675. begin
  676. if FTmPPath.Len = 0 then Exit;
  677. AddPathCopyMem(FTmPPath);
  678. ClearTmpPath();
  679. end;
  680. procedure TvVectorialPage.AddText(AX, AY, AZ: Double; FontName: string;
  681. FontSize: integer; AText: utf8string);
  682. var
  683. lText: TvText;
  684. begin
  685. lText := TvText.Create;
  686. lText.Value.Text := AText;
  687. lText.X := AX;
  688. lText.Y := AY;
  689. lText.Z := AZ;
  690. lText.Font.Name := FontName;
  691. lText.Font.Size := FontSize;
  692. AddEntity(lText);
  693. end;
  694. procedure TvVectorialPage.AddText(AX, AY: Double; AStr: utf8string);
  695. begin
  696. AddText(AX, AY, 0, '', 10, AStr);
  697. end;
  698. procedure TvVectorialPage.AddText(AX, AY, AZ: Double; AStr: utf8string);
  699. begin
  700. AddText(AX, AY, AZ, '', 10, AStr);
  701. end;
  702. procedure TvVectorialPage.AddCircle(ACenterX, ACenterY, ARadius: Double);
  703. var
  704. lCircle: TvCircle;
  705. begin
  706. lCircle := TvCircle.Create;
  707. lCircle.X := ACenterX;
  708. lCircle.Y := ACenterY;
  709. lCircle.Radius := ARadius;
  710. AddEntity(lCircle);
  711. end;
  712. procedure TvVectorialPage.AddCircularArc(ACenterX, ACenterY, ARadius,
  713. AStartAngle, AEndAngle: Double; AColor: TFPColor);
  714. var
  715. lCircularArc: TvCircularArc;
  716. begin
  717. lCircularArc := TvCircularArc.Create;
  718. lCircularArc.X := ACenterX;
  719. lCircularArc.Y := ACenterY;
  720. lCircularArc.Radius := ARadius;
  721. lCircularArc.StartAngle := AStartAngle;
  722. lCircularArc.EndAngle := AEndAngle;
  723. lCircularArc.Pen.Color := AColor;
  724. AddEntity(lCircularArc);
  725. end;
  726. procedure TvVectorialPage.AddEllipse(CenterX, CenterY, MajorHalfAxis,
  727. MinorHalfAxis, Angle: Double);
  728. var
  729. lEllipse: TvEllipse;
  730. begin
  731. lEllipse := TvEllipse.Create;
  732. lEllipse.X := CenterX;
  733. lEllipse.Y := CenterY;
  734. lEllipse.MajorHalfAxis := MajorHalfAxis;
  735. lEllipse.MinorHalfAxis := MinorHalfAxis;
  736. lEllipse.Angle := Angle;
  737. AddEntity(lEllipse);
  738. end;
  739. procedure TvVectorialPage.AddAlignedDimension(BaseLeft, BaseRight, DimLeft,
  740. DimRight: T3DPoint);
  741. var
  742. lDim: TvAlignedDimension;
  743. begin
  744. lDim := TvAlignedDimension.Create;
  745. lDim.BaseLeft := BaseLeft;
  746. lDim.BaseRight := BaseRight;
  747. lDim.DimensionLeft := DimLeft;
  748. lDim.DimensionRight := DimRight;
  749. AddEntity(lDim);
  750. end;
  751. { TvText }
  752. constructor TvText.Create;
  753. begin
  754. inherited Create;
  755. Value := TStringList.Create;
  756. end;
  757. destructor TvText.Destroy;
  758. begin
  759. Value.Free;
  760. inherited Destroy;
  761. end;
  762. function TvText.TryToSelect(APos: TPoint): TvFindEntityResult;
  763. var
  764. lProximityFactor: Integer;
  765. begin
  766. lProximityFactor := 5;
  767. if (APos.X > X - lProximityFactor) and (APos.X < X + lProximityFactor)
  768. and (APos.Y > Y - lProximityFactor) and (APos.Y < Y + lProximityFactor) then
  769. Result := vfrFound
  770. else Result := vfrNotFound;
  771. end;
  772. { TvEntity }
  773. constructor TvEntity.Create;
  774. begin
  775. Pen.Style := psSolid;
  776. Pen.Color := colBlack;
  777. Brush.Style := bsClear;
  778. Brush.Color := colBlue;
  779. end;
  780. procedure TvEntity.CalculateBoundingBox(var ALeft, ATop, ARight, ABottom: Double);
  781. begin
  782. ALeft := 0;
  783. ATop := 0;
  784. ARight := 0;
  785. ABottom := 0;
  786. end;
  787. procedure TvEntity.ExpandBoundingBox(var ALeft, ATop, ARight, ABottom: Double);
  788. var
  789. lLeft, lTop, lRight, lBottom: Double;
  790. begin
  791. CalculateBoundingBox(lLeft, lTop, lRight, lBottom);
  792. if lLeft < ALeft then ALeft := lLeft;
  793. if lTop < ATop then ATop := lTop;
  794. if lRight > ARight then ARight := lRight;
  795. if lBottom > ABottom then ABottom := lBottom;
  796. end;
  797. function TvEntity.TryToSelect(APos: TPoint): TvFindEntityResult;
  798. begin
  799. Result := vfrNotFound;
  800. end;
  801. procedure TvEntity.Translate(ADeltaX, ADeltaY: Integer);
  802. begin
  803. X := X + ADeltaX;
  804. Y := Y + ADeltaY;
  805. end;
  806. { TvEllipse }
  807. procedure TvEllipse.CalculateBoundingRectangle;
  808. var
  809. t, tmp: Double;
  810. begin
  811. {
  812. To calculate the bounding rectangle we can do this:
  813. Ellipse equations:You could try using the parametrized equations for an ellipse rotated at an arbitrary angle:
  814. x = CenterX + MajorHalfAxis*cos(t)*cos(Angle) - MinorHalfAxis*sin(t)*sin(Angle)
  815. y = CenterY + MinorHalfAxis*sin(t)*cos(Angle) + MajorHalfAxis*cos(t)*sin(Angle)
  816. You can then differentiate and solve for gradient = 0:
  817. 0 = dx/dt = -MajorHalfAxis*sin(t)*cos(Angle) - MinorHalfAxis*cos(t)*sin(Angle)
  818. =>
  819. tan(t) = -MinorHalfAxis*tan(Angle)/MajorHalfAxis
  820. =>
  821. t = cotang(-MinorHalfAxis*tan(Angle)/MajorHalfAxis)
  822. On the other axis:
  823. 0 = dy/dt = b*cos(t)*cos(phi) - a*sin(t)*sin(phi)
  824. =>
  825. tan(t) = b*cot(phi)/a
  826. }
  827. t := cotan(-MinorHalfAxis*tan(Angle)/MajorHalfAxis);
  828. tmp := X + MajorHalfAxis*cos(t)*cos(Angle) - MinorHalfAxis*sin(t)*sin(Angle);
  829. BoundingRect.Right := Round(tmp);
  830. end;
  831. { TsWorksheet }
  832. {@@
  833. Constructor.
  834. }
  835. constructor TvVectorialDocument.Create;
  836. begin
  837. inherited Create;
  838. FPages := TFPList.Create;
  839. end;
  840. {@@
  841. Destructor.
  842. }
  843. destructor TvVectorialDocument.Destroy;
  844. begin
  845. Clear;
  846. FPages.Free;
  847. inherited Destroy;
  848. end;
  849. procedure TvVectorialDocument.Assign(ASource: TvVectorialDocument);
  850. //var
  851. // i: Integer;
  852. begin
  853. // Clear;
  854. //
  855. // for i := 0 to ASource.GetEntitiesCount - 1 do
  856. // Self.AddEntity(ASource.GetEntity(i));
  857. end;
  858. procedure TvVectorialDocument.AssignTo(ADest: TvVectorialDocument);
  859. begin
  860. ADest.Assign(Self);
  861. end;
  862. {@@
  863. Convenience method which creates the correct
  864. writer object for a given vector graphics document format.
  865. }
  866. function TvVectorialDocument.CreateVectorialWriter(AFormat: TvVectorialFormat): TvCustomVectorialWriter;
  867. var
  868. i: Integer;
  869. begin
  870. Result := nil;
  871. for i := 0 to Length(GvVectorialFormats) - 1 do
  872. if GvVectorialFormats[i].Format = AFormat then
  873. begin
  874. if GvVectorialFormats[i].WriterClass <> nil then
  875. Result := GvVectorialFormats[i].WriterClass.Create;
  876. Break;
  877. end;
  878. if Result = nil then raise Exception.Create('Unsupported vector graphics format.');
  879. end;
  880. {@@
  881. Convenience method which creates the correct
  882. reader object for a given vector graphics document format.
  883. }
  884. function TvVectorialDocument.CreateVectorialReader(AFormat: TvVectorialFormat): TvCustomVectorialReader;
  885. var
  886. i: Integer;
  887. begin
  888. Result := nil;
  889. for i := 0 to Length(GvVectorialFormats) - 1 do
  890. if GvVectorialFormats[i].Format = AFormat then
  891. begin
  892. if GvVectorialFormats[i].ReaderClass <> nil then
  893. Result := GvVectorialFormats[i].ReaderClass.Create;
  894. Break;
  895. end;
  896. if Result = nil then raise Exception.Create('Unsupported vector graphics format.');
  897. end;
  898. {@@
  899. Writes the document to a file.
  900. If the file doesn't exist, it will be created.
  901. }
  902. procedure TvVectorialDocument.WriteToFile(AFileName: string; AFormat: TvVectorialFormat);
  903. var
  904. AWriter: TvCustomVectorialWriter;
  905. begin
  906. AWriter := CreateVectorialWriter(AFormat);
  907. try
  908. AWriter.WriteToFile(AFileName, Self);
  909. finally
  910. AWriter.Free;
  911. end;
  912. end;
  913. procedure TvVectorialDocument.WriteToFile(AFileName: string);
  914. var
  915. lFormat: TvVectorialFormat;
  916. begin
  917. lFormat := GetFormatFromExtension(ExtractFileExt(AFileName));
  918. WriteToFile(AFileName, lFormat);
  919. end;
  920. {@@
  921. Writes the document to a stream
  922. }
  923. procedure TvVectorialDocument.WriteToStream(AStream: TStream; AFormat: TvVectorialFormat);
  924. var
  925. AWriter: TvCustomVectorialWriter;
  926. begin
  927. AWriter := CreateVectorialWriter(AFormat);
  928. try
  929. AWriter.WriteToStream(AStream, Self);
  930. finally
  931. AWriter.Free;
  932. end;
  933. end;
  934. procedure TvVectorialDocument.WriteToStrings(AStrings: TStrings;
  935. AFormat: TvVectorialFormat);
  936. var
  937. AWriter: TvCustomVectorialWriter;
  938. begin
  939. AWriter := CreateVectorialWriter(AFormat);
  940. try
  941. AWriter.WriteToStrings(AStrings, Self);
  942. finally
  943. AWriter.Free;
  944. end;
  945. end;
  946. {@@
  947. Reads the document from a file.
  948. Any current contents in this object will be removed.
  949. }
  950. procedure TvVectorialDocument.ReadFromFile(AFileName: string;
  951. AFormat: TvVectorialFormat);
  952. var
  953. AReader: TvCustomVectorialReader;
  954. begin
  955. Self.Clear;
  956. AReader := CreateVectorialReader(AFormat);
  957. try
  958. AReader.ReadFromFile(AFileName, Self);
  959. finally
  960. AReader.Free;
  961. end;
  962. end;
  963. {@@
  964. Reads the document from a file. A variant that auto-detects the format from the extension and other factors.
  965. }
  966. procedure TvVectorialDocument.ReadFromFile(AFileName: string);
  967. var
  968. lFormat: TvVectorialFormat;
  969. begin
  970. lFormat := GetFormatFromExtension(ExtractFileExt(AFileName));
  971. ReadFromFile(AFileName, lFormat);
  972. end;
  973. {@@
  974. Reads the document from a stream.
  975. Any current contents in this object will be removed.
  976. }
  977. procedure TvVectorialDocument.ReadFromStream(AStream: TStream;
  978. AFormat: TvVectorialFormat);
  979. var
  980. AReader: TvCustomVectorialReader;
  981. begin
  982. Self.Clear;
  983. AReader := CreateVectorialReader(AFormat);
  984. try
  985. AReader.ReadFromStream(AStream, Self);
  986. finally
  987. AReader.Free;
  988. end;
  989. end;
  990. procedure TvVectorialDocument.ReadFromStrings(AStrings: TStrings;
  991. AFormat: TvVectorialFormat);
  992. var
  993. AReader: TvCustomVectorialReader;
  994. begin
  995. Self.Clear;
  996. AReader := CreateVectorialReader(AFormat);
  997. try
  998. AReader.ReadFromStrings(AStrings, Self);
  999. finally
  1000. AReader.Free;
  1001. end;
  1002. end;
  1003. class function TvVectorialDocument.GetFormatFromExtension(AFileName: string
  1004. ): TvVectorialFormat;
  1005. var
  1006. lExt: string;
  1007. begin
  1008. lExt := ExtractFileExt(AFileName);
  1009. if AnsiCompareText(lExt, STR_PDF_EXTENSION) = 0 then Result := vfPDF
  1010. else if AnsiCompareText(lExt, STR_POSTSCRIPT_EXTENSION) = 0 then Result := vfPostScript
  1011. else if AnsiCompareText(lExt, STR_SVG_EXTENSION) = 0 then Result := vfSVG
  1012. else if AnsiCompareText(lExt, STR_CORELDRAW_EXTENSION) = 0 then Result := vfCorelDrawCDR
  1013. else if AnsiCompareText(lExt, STR_WINMETAFILE_EXTENSION) = 0 then Result := vfWindowsMetafileWMF
  1014. else if AnsiCompareText(lExt, STR_AUTOCAD_EXCHANGE_EXTENSION) = 0 then Result := vfDXF
  1015. else if AnsiCompareText(lExt, STR_ENCAPSULATEDPOSTSCRIPT_EXTENSION) = 0 then Result := vfEncapsulatedPostScript
  1016. else
  1017. raise Exception.Create('TvVectorialDocument.GetFormatFromExtension: The extension (' + lExt + ') doesn''t match any supported formats.');
  1018. end;
  1019. function TvVectorialDocument.GetDetailedFileFormat(): string;
  1020. begin
  1021. end;
  1022. procedure TvVectorialDocument.GuessDocumentSize();
  1023. var
  1024. i, j: Integer;
  1025. lEntity: TvEntity;
  1026. lLeft, lTop, lRight, lBottom: Double;
  1027. CurPage: TvVectorialPage;
  1028. begin
  1029. lLeft := 0;
  1030. lTop := 0;
  1031. lRight := 0;
  1032. lBottom := 0;
  1033. for j := 0 to GetPageCount()-1 do
  1034. begin
  1035. CurPage := GetPage(j);
  1036. for i := 0 to CurPage.GetEntitiesCount() - 1 do
  1037. begin
  1038. lEntity := CurPage.GetEntity(I);
  1039. lEntity.ExpandBoundingBox(lLeft, lTop, lRight, lBottom);
  1040. end;
  1041. end;
  1042. Width := lRight - lLeft;
  1043. Height := lBottom - lTop;
  1044. end;
  1045. procedure TvVectorialDocument.GuessGoodZoomLevel(AScreenSize: Integer);
  1046. begin
  1047. ZoomLevel := AScreenSize / Height;
  1048. end;
  1049. function TvVectorialDocument.GetPage(AIndex: Integer): TvVectorialPage;
  1050. begin
  1051. Result := TvVectorialPage(FPages.Items[AIndex]);
  1052. end;
  1053. function TvVectorialDocument.GetPageCount: Integer;
  1054. begin
  1055. Result := FPages.Count;
  1056. end;
  1057. function TvVectorialDocument.GetCurrentPage: TvVectorialPage;
  1058. begin
  1059. if FCurrentPageIndex >= 0 then
  1060. Result := GetPage(FCurrentPageIndex)
  1061. else
  1062. Result := nil;
  1063. end;
  1064. procedure TvVectorialDocument.SetCurrentPage(AIndex: Integer);
  1065. begin
  1066. FCurrentPageIndex := AIndex;
  1067. end;
  1068. function TvVectorialDocument.AddPage: TvVectorialPage;
  1069. begin
  1070. Result := TvVectorialPage.Create(Self);
  1071. FPages.Add(Result);
  1072. if FCurrentPageIndex < 0 then FCurrentPageIndex := FPages.Count-1;
  1073. end;
  1074. {@@
  1075. Clears all data in the document
  1076. }
  1077. procedure TvVectorialDocument.Clear;
  1078. begin
  1079. end;
  1080. { TvCustomVectorialReader }
  1081. constructor TvCustomVectorialReader.Create;
  1082. begin
  1083. inherited Create;
  1084. end;
  1085. procedure TvCustomVectorialReader.ReadFromFile(AFileName: string; AData: TvVectorialDocument);
  1086. var
  1087. FileStream: TFileStream;
  1088. begin
  1089. FileStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone);
  1090. try
  1091. ReadFromStream(FileStream, AData);
  1092. finally
  1093. FileStream.Free;
  1094. end;
  1095. end;
  1096. procedure TvCustomVectorialReader.ReadFromStream(AStream: TStream;
  1097. AData: TvVectorialDocument);
  1098. var
  1099. AStringStream: TStringStream;
  1100. AStrings: TStringList;
  1101. begin
  1102. AStringStream := TStringStream.Create('');
  1103. AStrings := TStringList.Create;
  1104. try
  1105. AStringStream.CopyFrom(AStream, AStream.Size);
  1106. AStringStream.Seek(0, soFromBeginning);
  1107. AStrings.Text := AStringStream.DataString;
  1108. ReadFromStrings(AStrings, AData);
  1109. finally
  1110. AStringStream.Free;
  1111. AStrings.Free;
  1112. end;
  1113. end;
  1114. procedure TvCustomVectorialReader.ReadFromStrings(AStrings: TStrings;
  1115. AData: TvVectorialDocument);
  1116. var
  1117. AStringStream: TStringStream;
  1118. begin
  1119. AStringStream := TStringStream.Create('');
  1120. try
  1121. AStringStream.WriteString(AStrings.Text);
  1122. AStringStream.Seek(0, soFromBeginning);
  1123. ReadFromStream(AStringStream, AData);
  1124. finally
  1125. AStringStream.Free;
  1126. end;
  1127. end;
  1128. { TsCustomSpreadWriter }
  1129. constructor TvCustomVectorialWriter.Create;
  1130. begin
  1131. inherited Create;
  1132. end;
  1133. {@@
  1134. Default file writting method.
  1135. Opens the file and calls WriteToStream
  1136. @param AFileName The output file name.
  1137. If the file already exists it will be replaced.
  1138. @param AData The Workbook to be saved.
  1139. @see TsWorkbook
  1140. }
  1141. procedure TvCustomVectorialWriter.WriteToFile(AFileName: string; AData: TvVectorialDocument);
  1142. var
  1143. OutputFile: TFileStream;
  1144. begin
  1145. OutputFile := TFileStream.Create(AFileName, fmCreate or fmOpenWrite);
  1146. try
  1147. WriteToStream(OutputFile, AData);
  1148. finally
  1149. OutputFile.Free;
  1150. end;
  1151. end;
  1152. {@@
  1153. The default stream writer just uses WriteToStrings
  1154. }
  1155. procedure TvCustomVectorialWriter.WriteToStream(AStream: TStream;
  1156. AData: TvVectorialDocument);
  1157. var
  1158. lStringList: TStringList;
  1159. begin
  1160. lStringList := TStringList.Create;
  1161. try
  1162. WriteToStrings(lStringList, AData);
  1163. lStringList.SaveToStream(AStream);
  1164. finally
  1165. lStringList.Free;
  1166. end;
  1167. end;
  1168. procedure TvCustomVectorialWriter.WriteToStrings(AStrings: TStrings;
  1169. AData: TvVectorialDocument);
  1170. begin
  1171. end;
  1172. { TPath }
  1173. procedure TPath.Assign(ASource: TPath);
  1174. begin
  1175. Len := ASource.Len;
  1176. Points := ASource.Points;
  1177. PointsEnd := ASource.PointsEnd;
  1178. CurPoint := ASource.CurPoint;
  1179. Pen := ASource.Pen;
  1180. Brush := ASource.Brush;
  1181. ClipPath := ASource.ClipPath;
  1182. ClipMode := ASource.ClipMode;
  1183. end;
  1184. procedure TPath.PrepareForSequentialReading;
  1185. begin
  1186. CurPoint := nil;
  1187. end;
  1188. function TPath.Next(): TPathSegment;
  1189. begin
  1190. if CurPoint = nil then Result := Points
  1191. else Result := CurPoint.Next;
  1192. CurPoint := Result;
  1193. end;
  1194. procedure TPath.CalculateBoundingBox(var ALeft, ATop, ARight, ABottom: Double);
  1195. var
  1196. lSegment: TPathSegment;
  1197. l2DSegment: T2DSegment;
  1198. lFirstValue: Boolean = True;
  1199. begin
  1200. inherited CalculateBoundingBox(ALeft, ATop, ARight, ABottom);
  1201. PrepareForSequentialReading();
  1202. lSegment := Next();
  1203. while lSegment <> nil do
  1204. begin
  1205. if lSegment is T2DSegment then
  1206. begin
  1207. l2DSegment := T2DSegment(lSegment);
  1208. if lFirstValue then
  1209. begin
  1210. ALeft := l2DSegment.X;
  1211. ATop := l2DSegment.Y;
  1212. ARight := l2DSegment.X;
  1213. ABottom := l2DSegment.Y;
  1214. lFirstValue := False;
  1215. end
  1216. else
  1217. begin
  1218. if l2DSegment.X < ALeft then ALeft := l2DSegment.X;
  1219. if l2DSegment.Y < ATop then ATop := l2DSegment.Y;
  1220. if l2DSegment.X > ARight then ARight := l2DSegment.X;
  1221. if l2DSegment.Y > ABottom then ABottom := l2DSegment.Y;
  1222. end;
  1223. end;
  1224. lSegment := Next();
  1225. end;
  1226. end;
  1227. procedure TPath.AppendSegment(ASegment: TPathSegment);
  1228. var
  1229. L: Integer;
  1230. begin
  1231. // Check if we are the first segment in the tmp path
  1232. if PointsEnd = nil then
  1233. begin
  1234. if Len <> 0 then
  1235. Exception.Create('[TPath.AppendSegment] Assertion failed Len <> 0 with PointsEnd = nil');
  1236. Points := ASegment;
  1237. PointsEnd := ASegment;
  1238. Len := 1;
  1239. Exit;
  1240. end;
  1241. L := Len;
  1242. Inc(Len);
  1243. // Adds the element to the end of the list
  1244. PointsEnd.Next := ASegment;
  1245. ASegment.Previous := PointsEnd;
  1246. PointsEnd := ASegment;
  1247. end;
  1248. finalization
  1249. SetLength(GvVectorialFormats, 0);
  1250. end.