pscanvas.pp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 2003 by the Free Pascal development team
  5. TPostScriptCanvas implementation.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. { ---------------------------------------------------------------------
  13. This code is heavily based on Tony Maro's initial TPostScriptCanvas
  14. implementation in the LCL, but was adapted to work with the custom
  15. canvas code and to work with streams instead of strings.
  16. ---------------------------------------------------------------------}
  17. {$mode objfpc}
  18. {$H+}
  19. unit pscanvas;
  20. interface
  21. uses
  22. Classes, SysUtils,fpimage,fpcanvas;
  23. type
  24. TPostScript = class;
  25. TPSPaintType = (ptColored, ptUncolored);
  26. TPSTileType = (ttConstant, ttNoDistortion, ttFast);
  27. TPostScriptCanvas = class; // forward reference
  28. {Remember, modifying a pattern affects that pattern for the ENTIRE document!}
  29. TPSPattern = class(TFPCanvasHelper)
  30. private
  31. FStream : TMemoryStream;
  32. FPatternCanvas : TPostScriptCanvas;
  33. FOldName: String;
  34. FOnChange: TNotifyEvent;
  35. FBBox: TRect;
  36. FName: String;
  37. FPaintType: TPSPaintType;
  38. FPostScript: TStringList;
  39. FTilingType: TPSTileType;
  40. FXStep: Real;
  41. FYStep: Real;
  42. function GetpostScript: TStringList;
  43. procedure SetBBox(const AValue: TRect);
  44. procedure SetName(const AValue: String);
  45. procedure SetPaintType(const AValue: TPSPaintType);
  46. procedure SetTilingType(const AValue: TPSTileType);
  47. procedure SetXStep(const AValue: Real);
  48. procedure SetYStep(const AValue: Real);
  49. protected
  50. public
  51. constructor Create;
  52. destructor Destroy; override;
  53. procedure Changed;
  54. property BBox: TRect read FBBox write SetBBox;
  55. property PaintType: TPSPaintType read FPaintType write SetPaintType;
  56. property TilingType: TPSTileType read FTilingType write SetTilingType;
  57. property XStep: Real read FXStep write SetXStep;
  58. property YStep: Real read FYStep write SetYStep;
  59. property Name: String read FName write SetName;
  60. property GetPS: TStringList read GetPostscript;
  61. property OldName: string read FOldName write FOldName; // used when notifying that name changed
  62. property OnChange: TNotifyEvent read FOnChange write FOnChange;
  63. Property PatternCanvas : TPostScriptCanvas Read FPatternCanvas;
  64. end;
  65. PPSPattern = ^TPSPattern; // used for array
  66. { Pen and brush object both right now...}
  67. TPSPen = class(TFPCustomPen)
  68. private
  69. FPattern: TPSPattern;
  70. procedure SetPattern(const AValue: TPSPattern);
  71. public
  72. destructor Destroy; override;
  73. property Pattern: TPSPattern read FPattern write SetPattern;
  74. function AsString: String;
  75. end;
  76. TPSBrush = Class(TFPCustomBrush)
  77. Private
  78. Function GetAsString : String;
  79. Public
  80. Property AsString : String Read GetAsString;
  81. end;
  82. TPSFont = Class(TFPCustomFont)
  83. end;
  84. { Custom canvas-like object that handles postscript code }
  85. TPostScriptCanvas = class(TFPCustomCanvas)
  86. private
  87. FHeight,FWidth : Integer;
  88. FStream : TStream;
  89. FLineSpacing: Integer;
  90. LastX: Integer;
  91. LastY: Integer;
  92. function TranslateY(Ycoord: Integer): Integer; // Y axis is backwards in postscript
  93. procedure AddFill;
  94. procedure ResetPos; // reset back to last moveto location
  95. procedure SetWidth (AValue : integer); override;
  96. function GetWidth : integer; override;
  97. procedure SetHeight (AValue : integer); override;
  98. function GetHeight : integer; override;
  99. Protected
  100. Procedure WritePS(Const Cmd : String);
  101. Procedure WritePS(Const Fmt : String; Args : Array of Const);
  102. procedure DrawRectangle(const Bounds: TRect; DoFill : Boolean);
  103. procedure DrawEllipse(const Bounds: TRect; DoFill : Boolean);
  104. public
  105. constructor Create(AStream : TStream);
  106. destructor Destroy; override;
  107. function DoCreateDefaultFont : TFPCustomFont; override;
  108. function DoCreateDefaultPen : TFPCustomPen; override;
  109. function DoCreateDefaultBrush : TFPCustomBrush; override;
  110. property LineSpacing: Integer read FLineSpacing write FLineSpacing;
  111. Procedure DoMoveTo(X1,Y1 : Integer); override;
  112. Procedure DoLineTo(X1,Y1 : Integer); override;
  113. Procedure DoLine(X1,Y1,X2,Y2 : Integer); override;
  114. Procedure DoRectangle(Const Bounds : TRect); override;
  115. Procedure DoRectangleFill(Const Bounds : TRect); override;
  116. procedure DoPolyline(Const Points: Array of TPoint); override;
  117. procedure DoEllipse(const Bounds: TRect); override;
  118. procedure DoEllipseFill(const Bounds: TRect); override;
  119. procedure DoPie(x,y,awidth,aheight,angle1,angle2 : Integer);
  120. //procedure Pie(x,y,width,height,SX,SY,EX,EY : Integer);
  121. procedure Writeln(AString: String);
  122. procedure TextOut(X,Y: Integer; const Text: String);
  123. //procedure Chord(x,y,width,height,angle1,angle2 : Integer);
  124. //procedure Chord(x,y,width,height,SX,SY,EX,EY : Integer);
  125. //procedure PolyBezier(Points: PPoint; NumPts: Integer;
  126. // Filled: boolean{$IFDEF VER1_1} = False{$ENDIF};
  127. // Continuous: boolean{$IFDEF VER1_1} = False{$ENDIF});
  128. //procedure PolyBezier(const Points: array of TPoint;
  129. // Filled: boolean{$IFDEF VER1_1} = False{$ENDIF};
  130. // Continuous: boolean{$IFDEF VER1_1} = False{$ENDIF});
  131. //procedure PolyBezier(const Points: array of TPoint);
  132. //procedure Polygon(const Points: array of TPoint;
  133. // Winding: Boolean{$IFDEF VER1_1} = False{$ENDIF};
  134. // StartIndex: Integer{$IFDEF VER1_1} = 0{$ENDIF};
  135. // NumPts: Integer {$IFDEF VER1_1} = -1{$ENDIF});
  136. //procedure Polygon(Points: PPoint; NumPts: Integer;
  137. // Winding: boolean{$IFDEF VER1_1} = False{$ENDIF});
  138. //Procedure Polygon(const Points: array of TPoint);
  139. //Procedure FillRect(const Rect : TRect);
  140. //procedure FloodFill(X, Y: Integer; FillColor: TFPColor; FillStyle: TFillStyle);
  141. //Procedure RoundRect(X1, Y1, X2, Y2: Integer; RX,RY : Integer);
  142. //Procedure RoundRect(const Rect : TRect; RX,RY : Integer);
  143. Property Stream : TStream read FStream;
  144. end;
  145. { Encapsulates ALL the postscript and uses the TPostScriptCanvas object for a single page }
  146. TPostScript = class(TComponent)
  147. private
  148. FDocStarted : Boolean;
  149. FCreator : String;
  150. FStream : TStream;
  151. FCanvas: TPostScriptCanvas;
  152. FHeight: Integer;
  153. FLineSpacing: Integer;
  154. FPageNumber: Integer;
  155. FTitle: String;
  156. FWidth: Integer;
  157. FPatterns: TList; // array of pointers to pattern objects
  158. procedure SetHeight(const AValue: Integer);
  159. procedure SetLineSpacing(const AValue: Integer);
  160. procedure SetWidth(const AValue: Integer);
  161. procedure UpdateBoundingBox;
  162. procedure PatternChanged(Sender: TObject);
  163. procedure InsertPattern(APattern: TPSPattern); // adds the pattern to the postscript
  164. Procedure SetStream (Value : TStream);
  165. Function GetCreator : String;
  166. Protected
  167. Procedure WritePS(Const Cmd : String);
  168. Procedure WritePS(Const Fmt : String; Args : Array of Const);
  169. Procedure WriteDocumentHeader; virtual;
  170. Procedure WriteStandardFont; virtual;
  171. Procedure WritePage; virtual;
  172. Procedure FreePatterns;
  173. Procedure CheckStream;
  174. public
  175. Constructor Create(AOwner : TComponent);
  176. destructor Destroy; override;
  177. procedure AddPattern(APSPattern: TPSPattern);
  178. function FindPattern(AName: String): TPSPattern;
  179. function DelPattern(AName: String): Boolean;
  180. function NewPattern(AName: String): TPSPattern;
  181. property Canvas: TPostScriptCanvas read FCanvas;
  182. property Height: Integer read FHeight write SetHeight;
  183. property Width: Integer read FWidth write SetWidth;
  184. property PageNumber: Integer read FPageNumber;
  185. property Title: String read FTitle write FTitle;
  186. property LineSpacing: Integer read FLineSpacing write SetLineSpacing;
  187. procedure BeginDoc;
  188. procedure NewPage;
  189. procedure EndDoc;
  190. Property Stream : TStream Read FStream Write SetStream;
  191. Property Creator : String Read GetCreator Write FCreator;
  192. end;
  193. implementation
  194. Resourcestring
  195. SErrNoStreamAssigned = 'Invalid operation: No stream assigned';
  196. SErrDocumentAlreadyStarted = 'Cannot start document twice.';
  197. {$ifdef ver1_0}
  198. const lineending=#10;
  199. procedure freeandnil(var o:Tobject);
  200. begin
  201. o.destroy;
  202. o:=nil;
  203. end;
  204. {$endif}
  205. { TPostScriptCanvas ----------------------------------------------------------}
  206. Procedure TPostScriptCanvas.WritePS(const Cmd : String);
  207. begin
  208. If length(Cmd)>0 then
  209. FStream.Write(Cmd[1],Length(Cmd));
  210. FStream.Write(LineEnding,SizeOf(LineEnding));
  211. end;
  212. Procedure TPostScriptCanvas.WritePS(Const Fmt : String; Args : Array of Const);
  213. begin
  214. WritePS(Format(Fmt,Args));
  215. end;
  216. { Y coords in postscript are backwards... }
  217. function TPostScriptCanvas.TranslateY(Ycoord: Integer): Integer;
  218. begin
  219. Result:=Height-Ycoord;
  220. end;
  221. { Adds a fill finishing line to any path we desire to fill }
  222. procedure TPostScriptCanvas.AddFill;
  223. begin
  224. WritePs('gsave '+(Brush as TPSBrush).AsString+' fill grestore');
  225. end;
  226. { Return to last moveto location }
  227. procedure TPostScriptCanvas.ResetPos;
  228. begin
  229. WritePS(inttostr(LastX)+' '+inttostr(TranslateY(LastY))+' moveto');
  230. end;
  231. constructor TPostScriptCanvas.Create(AStream : TStream);
  232. begin
  233. inherited create;
  234. FStream:=AStream;
  235. Height := 792; // length of page in points at 72 ppi
  236. { // Choose a standard font in case the user doesn't
  237. FFontFace := 'AvantGarde-Book';
  238. SetFontSize(10);
  239. FLineSpacing := MPostScript.LineSpacing;
  240. end;
  241. FPen := TPSPen.Create;
  242. FPen.Width := 1;
  243. FPen.Color := 0;
  244. FPen.OnChange := @PenChanged;
  245. FBrush := TPSPen.Create;
  246. FBrush.Width := 1;
  247. FBrush.Color := -1;
  248. // don't notify us that the brush changed...
  249. }
  250. end;
  251. destructor TPostScriptCanvas.Destroy;
  252. begin
  253. {
  254. FPostScript.Free;
  255. FPen.Free;
  256. FBrush.Free;
  257. }
  258. inherited Destroy;
  259. end;
  260. procedure TPostScriptCanvas.SetWidth (AValue : integer);
  261. begin
  262. FWidth:=AValue;
  263. end;
  264. function TPostScriptCanvas.GetWidth : integer;
  265. begin
  266. Result:=FWidth;
  267. end;
  268. procedure TPostScriptCanvas.SetHeight (AValue : integer);
  269. begin
  270. FHeight:=AValue;
  271. end;
  272. function TPostScriptCanvas.GetHeight : integer;
  273. begin
  274. Result:=FHeight;
  275. end;
  276. { Move draw location }
  277. procedure TPostScriptCanvas.DoMoveTo(X1, Y1: Integer);
  278. var
  279. Y: Integer;
  280. begin
  281. Y := TranslateY(Y1);
  282. WritePS(inttostr(X1)+' '+inttostr(Y)+' moveto');
  283. LastX := X1;
  284. LastY := Y1;
  285. end;
  286. { Draw a line from current location to these coords }
  287. procedure TPostScriptCanvas.DoLineTo(X1, Y1: Integer);
  288. var
  289. Y: Integer;
  290. begin
  291. Y := TranslateY(Y1);
  292. WritePS(inttostr(X1)+' '+inttostr(Y)+' lineto');
  293. LastX := X1;
  294. LastY := Y1;
  295. end;
  296. procedure TPostScriptCanvas.DoLine(X1, Y1, X2, Y2: Integer);
  297. var
  298. Y12, Y22: Integer;
  299. begin
  300. Y12 := TranslateY(Y1);
  301. Y22 := TranslateY(Y2);
  302. WritePS('newpath '+inttostr(X1)+' '+inttostr(Y12)+' moveto '+
  303. inttostr(X2)+' '+inttostr(Y22)+' lineto closepath stroke');
  304. // go back to last moveto position
  305. ResetPos;
  306. end;
  307. { Draw a rectangle }
  308. procedure TPostScriptCanvas.DoRectangleFill(const Bounds: TRect);
  309. begin
  310. DrawRectangle(Bounds,true)
  311. end;
  312. procedure TPostScriptCanvas.DoRectangle(const Bounds: TRect);
  313. begin
  314. DrawRectangle(Bounds,False);
  315. end;
  316. procedure TPostScriptCanvas.DrawRectangle(const Bounds: TRect; DoFill : Boolean);
  317. var
  318. Y12, Y22: Integer;
  319. begin
  320. Y12 := TranslateY(Bounds.Top);
  321. Y22 := TranslateY(Bounds.Bottom);
  322. WritePS('stroke newpath');
  323. With Bounds do
  324. begin
  325. WritePS(inttostr(Left)+' '+inttostr(Y12)+' moveto');
  326. WritePS(inttostr(Right)+' '+inttostr(Y12)+' lineto');
  327. WritePS(inttostr(Right)+' '+inttostr(Y22)+' lineto');
  328. WritePS(inttostr(Left)+' '+inttostr(Y22)+' lineto');
  329. end;
  330. WritePS('closepath');
  331. If DoFill and (Brush.Style<>bsClear) then
  332. AddFill;
  333. WritePS('stroke');
  334. ResetPos;
  335. end;
  336. { Draw a series of lines }
  337. procedure TPostScriptCanvas.DoPolyline(Const Points: Array of TPoint);
  338. var
  339. i : Longint;
  340. begin
  341. MoveTo(Points[0].X, Points[0].Y);
  342. For i := 1 to High(Points) do
  343. LineTo(Points[i].X, Points[i].Y);
  344. ResetPos;
  345. end;
  346. { This was a pain to figure out... }
  347. procedure TPostScriptCanvas.DoEllipse(Const Bounds : TRect);
  348. begin
  349. DrawEllipse(Bounds,False);
  350. end;
  351. procedure TPostScriptCanvas.DoEllipseFill(Const Bounds : TRect);
  352. begin
  353. DrawEllipse(Bounds,true);
  354. end;
  355. procedure TPostScriptCanvas.DrawEllipse(Const Bounds : TRect; DoFill : Boolean);
  356. var
  357. radius: Integer;
  358. YRatio: Real;
  359. centerX, centerY: Integer;
  360. begin
  361. // set radius to half the width
  362. With Bounds do
  363. begin
  364. radius := (Right-Left) div 2;
  365. if radius <1 then
  366. exit;
  367. YRatio := (Bottom - Top) / (Right-Left);
  368. // find center
  369. CenterX := (Right+Left) div 2;
  370. CenterY := (Top+Bottom) div 2;
  371. end;
  372. WritePS('newpath '+inttostr(CenterX)+' '+inttostr(TranslateY(CenterY))+' translate');
  373. // move to edge
  374. WritePS(inttostr(radius)+' 0 moveto');
  375. // now draw it
  376. WritePS('gsave 1 '+format('%.3f',[YRatio])+' scale');
  377. WritePS('0 0 '+inttostr(radius)+' 0 360 arc');
  378. if DoFill and (Brush.Style<>bsClear) then
  379. AddFill;
  380. // reset scale for drawing line thickness so it doesn't warp
  381. YRatio := 1 / YRatio;
  382. WritePS('1 '+format('%.2f',[YRatio])+' scale stroke grestore');
  383. // move origin back
  384. WritePS(inttostr(-CenterX)+' '+inttostr(-TranslateY(CenterY))+' translate closepath stroke');
  385. ResetPos;
  386. end;
  387. procedure TPostScriptCanvas.DoPie(x, y, AWidth, AHeight, angle1, angle2: Integer);
  388. begin
  389. // set zero at center
  390. WritePS('newpath '+inttostr(X)+' '+inttostr(TranslateY(Y))+' translate');
  391. // scale it
  392. WritePS('gsave '+inttostr(AWidth)+' '+inttostr(Aheight)+' scale');
  393. //WritePS('gsave 1 1 scale');
  394. // draw line to edge
  395. WritePS('0 0 moveto');
  396. WritePS('0 0 1 '+inttostr(angle1)+' '+inttostr(angle2)+' arc closepath');
  397. if Brush.Style<>bsClear then
  398. AddFill;
  399. // reset scale so we don't change the line thickness
  400. // adding 0.01 to compensate for scaling error - there may be a deeper problem here...
  401. WritePS(format('%.6f',[(real(1) / X)+0.01])+' '+format('%.6f',[(real(1) / Y)+0.01])+' scale stroke grestore');
  402. // close out and return origin
  403. WritePS(inttostr(-X)+' '+inttostr(-TranslateY(Y))+' translate closepath stroke');
  404. resetpos;
  405. end;
  406. { Writes text with a carriage return }
  407. procedure TPostScriptCanvas.Writeln(AString: String);
  408. begin
  409. TextOut(LastX, LastY, AString);
  410. LastY := LastY+Font.Size+FLineSpacing;
  411. MoveTo(LastX, LastY);
  412. end;
  413. { Output text, restoring draw location }
  414. procedure TPostScriptCanvas.TextOut(X, Y: Integer; const Text: String);
  415. var
  416. Y1: Integer;
  417. begin
  418. Y1 := TranslateY(Y);
  419. WritePS(inttostr(X)+' '+inttostr(Y1)+' moveto');
  420. WritePS('('+Text+') show');
  421. ResetPos; // move back to last moveto location
  422. end;
  423. function TPostScriptCanvas.DoCreateDefaultFont : TFPCustomFont;
  424. begin
  425. Result:=TPSFont.Create;
  426. end;
  427. function TPostScriptCanvas.DoCreateDefaultPen : TFPCustomPen;
  428. begin
  429. Result:=TPSPen.Create;
  430. end;
  431. function TPostScriptCanvas.DoCreateDefaultBrush : TFPCustomBrush;
  432. begin
  433. Result:=TPSBrush.Create;
  434. end;
  435. { TPostScript -------------------------------------------------------------- }
  436. procedure TPostScript.SetHeight(const AValue: Integer);
  437. begin
  438. if FHeight=AValue then exit;
  439. FHeight:=AValue;
  440. UpdateBoundingBox;
  441. // filter down to the canvas height property
  442. if assigned(FCanvas) then
  443. FCanvas.Height := FHeight;
  444. end;
  445. procedure TPostScript.SetLineSpacing(const AValue: Integer);
  446. begin
  447. if FLineSpacing=AValue then exit;
  448. FLineSpacing:=AValue;
  449. // filter down to the canvas
  450. if assigned(FCanvas) then FCanvas.LineSpacing := AValue;
  451. end;
  452. procedure TPostScript.SetWidth(const AValue: Integer);
  453. begin
  454. if FWidth=AValue then exit;
  455. FWidth:=AValue;
  456. UpdateBoundingBox;
  457. end;
  458. { Take our sizes and change the boundingbox line }
  459. procedure TPostScript.UpdateBoundingBox;
  460. begin
  461. {
  462. // need to not hard-link this to line 1
  463. FDocument[1] := '%%BoundingBox: 0 0 '+inttostr(FWidth)+' '+inttostr(FHeight);
  464. }
  465. end;
  466. { Pattern changed so update the postscript code }
  467. procedure TPostScript.PatternChanged(Sender: TObject);
  468. begin
  469. // called anytime a pattern changes. Update the postscript code.
  470. // look for and delete the current postscript code for this pattern
  471. // then paste the pattern back into the code before the first page
  472. InsertPattern(Sender As TPSPattern);
  473. end;
  474. { Places a pattern definition into the bottom of the header in postscript }
  475. procedure TPostScript.InsertPattern(APattern: TPSPattern);
  476. var
  477. I, J: Integer;
  478. MyStrings: TStringList;
  479. begin
  480. { I := 0;
  481. if FDocument.Count < 1 then begin
  482. // added pattern when no postscript exists - this shouldn't happen
  483. raise exception.create('Pattern inserted with no postscript existing');
  484. exit;
  485. end;
  486. for I := 0 to FDocument.count - 1 do begin
  487. if (FDocument[I] = '%%Page: 1 1') then begin
  488. // found it!
  489. // insert into just before that
  490. MyStrings := APattern.GetPS;
  491. for J := 0 to MyStrings.Count - 1 do begin
  492. FDocument.Insert(I-1+J, MyStrings[j]);
  493. end;
  494. exit;
  495. end;
  496. end;
  497. }
  498. end;
  499. constructor TPostScript.Create(AOwner : TComponent);
  500. begin
  501. inherited create(AOwner);
  502. // Set some defaults
  503. FHeight := 792; // 11 inches at 72 dpi
  504. FWidth := 612; // 8 1/2 inches at 72 dpi
  505. end;
  506. Procedure TPostScript.WritePS(const Cmd : String);
  507. begin
  508. If length(Cmd)>0 then
  509. FStream.Write(Cmd[1],Length(Cmd));
  510. FStream.Write(LineEnding,SizeOf(LineEnding));
  511. end;
  512. Procedure TPostScript.WritePS(Const Fmt : String; Args : Array of Const);
  513. begin
  514. WritePS(Format(Fmt,Args));
  515. end;
  516. Procedure TPostScript.WriteDocumentHeader;
  517. begin
  518. WritePS('%!PS-Adobe-3.0');
  519. WritePS('%%BoundingBox: 0 0 612 792');
  520. WritePS('%%Creator: '+Creator);
  521. WritePS('%%Title: '+FTitle);
  522. WritePS('%%Pages: (atend)');
  523. WritePS('%%PageOrder: Ascend');
  524. WriteStandardFont;
  525. end;
  526. Procedure TPostScript.WriteStandardFont;
  527. begin
  528. // Choose a standard font in case the user doesn't
  529. WritePS('/AvantGarde-Book findfont');
  530. WritePS('10 scalefont');
  531. WritePS('setfont');
  532. end;
  533. Procedure TPostScript.FreePatterns;
  534. Var
  535. i : Integer;
  536. begin
  537. If Assigned(FPatterns) then
  538. begin
  539. For I:=0 to FPatterns.Count-1 do
  540. TObject(FPatterns[i]).Free;
  541. FreeAndNil(FPatterns);
  542. end;
  543. end;
  544. destructor TPostScript.Destroy;
  545. begin
  546. Stream:=Nil;
  547. FreePatterns;
  548. inherited Destroy;
  549. end;
  550. { add a pattern to the array }
  551. procedure TPostScript.AddPattern(APSPattern: TPSPattern);
  552. begin
  553. If Not Assigned(FPatterns) then
  554. FPatterns:=Tlist.Create;
  555. FPatterns.Add(APSPattern);
  556. end;
  557. { Find a pattern object by it's name }
  558. function TPostScript.FindPattern(AName: String): TPSPattern;
  559. var
  560. I: Integer;
  561. begin
  562. Result := nil;
  563. If Assigned(FPatterns) then
  564. begin
  565. I:=Fpatterns.Count-1;
  566. While (Result=Nil) and (I>=0) do
  567. if TPSPattern(FPatterns[I]).Name = AName then
  568. result := TPSPattern(FPatterns[i])
  569. else
  570. Dec(i)
  571. end;
  572. end;
  573. function TPostScript.DelPattern(AName: String): Boolean;
  574. begin
  575. // can't do that yet...
  576. Result:=false;
  577. end;
  578. { Create a new pattern and inserts it into the array for safe keeping }
  579. function TPostScript.NewPattern(AName: String): TPSPattern;
  580. var
  581. MyPattern: TPSPattern;
  582. begin
  583. MyPattern := TPSPattern.Create;
  584. AddPattern(MyPattern);
  585. MyPattern.Name := AName;
  586. MyPattern.OnChange := @PatternChanged;
  587. MyPattern.OldName := '';
  588. // add this to the postscript now...
  589. InsertPattern(MyPattern);
  590. result := MyPattern;
  591. end;
  592. { Start a new document }
  593. procedure TPostScript.BeginDoc;
  594. var
  595. I: Integer;
  596. begin
  597. CheckStream;
  598. If FDocStarted then
  599. Raise Exception.Create(SErrDocumentAlreadyStarted);
  600. FCanvas:=TPostScriptCanvas.Create(FStream);
  601. FCanvas.Height:=Self.Height;
  602. FCanvas.Width:=Self.width;
  603. FreePatterns;
  604. WriteDocumentHeader;
  605. // start our first page
  606. FPageNumber := 1;
  607. WritePage;
  608. UpdateBoundingBox;
  609. end;
  610. Procedure TPostScript.WritePage;
  611. begin
  612. WritePS('%%Page: '+inttostr(FPageNumber)+' '+inttostr(FPageNumber));
  613. WritePS('newpath');
  614. end;
  615. { Copy current page into the postscript and start a new one }
  616. procedure TPostScript.NewPage;
  617. begin
  618. // dump the current page into our postscript first
  619. // put end page definition...
  620. WritePS('stroke');
  621. WritePS('showpage');
  622. FPageNumber := FPageNumber+1;
  623. WritePage;
  624. end;
  625. { Finish off the document }
  626. procedure TPostScript.EndDoc;
  627. begin
  628. // Start printing the document after closing out the pages
  629. WritePS('stroke');
  630. WritePS('showpage');
  631. WritePS('%%Pages: '+inttostr(FPageNumber));
  632. // okay, the postscript is all ready, so dump it to the text file
  633. // or to the printer
  634. FDocStarted:=False;
  635. FreeAndNil(FCanvas);
  636. end;
  637. Function TPostScript.GetCreator : String;
  638. begin
  639. If (FCreator='') then
  640. Result:=ClassName
  641. else
  642. Result:=FCreator;
  643. end;
  644. Procedure TPostScript.SetStream (Value : TStream);
  645. begin
  646. if (FStream<>Value) then
  647. begin
  648. If (FStream<>Nil) and FDocStarted then
  649. EndDoc;
  650. FStream:=Value;
  651. FDocStarted:=False;
  652. end;
  653. end;
  654. Procedure TPostScript.CheckStream;
  655. begin
  656. If Not Assigned(FStream) then
  657. Raise Exception.Create(SErrNoStreamAssigned);
  658. end;
  659. { TPSPen }
  660. procedure TPSPen.SetPattern(const AValue: TPSPattern);
  661. begin
  662. if FPattern<>AValue then
  663. begin
  664. FPattern:=AValue;
  665. // NotifyCanvas;
  666. end;
  667. end;
  668. destructor TPSPen.Destroy;
  669. begin
  670. // Do NOT free the pattern object from here...
  671. inherited Destroy;
  672. end;
  673. { Return the pen definition as a postscript string }
  674. function TPSPen.AsString: String;
  675. begin
  676. Result:='';
  677. if FPattern <> nil then
  678. begin
  679. if FPattern.PaintType = ptColored then
  680. Result:='/Pattern setcolorspace '+FPattern.Name+' setcolor '
  681. else
  682. begin
  683. Result:='[/Pattern /DeviceRGB] setcolorspace '+inttostr(Color.Red)+' '+inttostr(Color.Green)+' '+
  684. inttostr(Color.Blue)+' '+FPattern.Name+' setcolor ';
  685. end;
  686. end
  687. else // no pattern do this:
  688. Result:=inttostr(Color.Red)+' '+inttostr(Color.Green)+' '+
  689. inttostr(Color.Blue)+' setrgbcolor ';
  690. Result := Result + format('%f',[Width])+' setlinewidth ';
  691. end;
  692. { TPSPattern }
  693. { Returns the pattern definition as postscript }
  694. function TPSPattern.GetpostScript: TStringList;
  695. var
  696. I: Integer;
  697. S : String;
  698. begin
  699. // If nothing in the canvas, error
  700. if FStream.Size=0 then
  701. raise exception.create('Empty pattern');
  702. FPostScript.Clear;
  703. With FPostScript do
  704. begin
  705. add('%% PATTERN '+FName);
  706. add('/'+FName+'proto 12 dict def '+FName+'proto begin');
  707. add('/PatternType 1 def');
  708. add(Format('/PaintType %d def',[ord(FPaintType)+1]));
  709. add(Format('/TilingType %d def',[ord(FTilingType)+1]));
  710. add('/BBox ['+inttostr(FBBox.Left)+' '+inttostr(FBBox.Top)+' '+inttostr(FBBox.Right)+' '+inttostr(FBBox.Bottom)+'] def');
  711. add('/XStep '+format('%f',[FXStep])+' def');
  712. add('/YStep '+format('%f',[FYstep])+' def');
  713. add('/PaintProc { begin');
  714. // insert the canvas
  715. SetLength(S,FStream.Size);
  716. FStream.Seek(0,soFromBeginning);
  717. FStream.Read(S[1],FStream.Size);
  718. Add(S);
  719. // add support for custom matrix later
  720. add('end } def end '+FName+'proto [1 0 0 1 0 0] makepattern /'+FName+' exch def');
  721. add('%% END PATTERN '+FName);
  722. end;
  723. Result := FPostScript;
  724. end;
  725. procedure TPSPattern.SetBBox(const AValue: TRect);
  726. begin
  727. { if FBBox<>AValue then
  728. begin
  729. FBBox:=AValue;
  730. FPatternCanvas.Height := FBBox.Bottom - FBBox.Top;
  731. // NotifyCanvas;
  732. end;
  733. }
  734. end;
  735. procedure TPSPattern.SetName(const AValue: String);
  736. begin
  737. FOldName := FName;
  738. if (FName<>AValue) then
  739. begin
  740. FName:=AValue;
  741. // NotifyCanvas;
  742. end;
  743. end;
  744. procedure TPSPattern.Changed;
  745. begin
  746. if Assigned(FOnChange) then FOnChange(Self);
  747. end;
  748. procedure TPSPattern.SetPaintType(const AValue: TPSPaintType);
  749. begin
  750. if FPaintType=AValue then exit;
  751. FPaintType:=AValue;
  752. changed;
  753. end;
  754. procedure TPSPattern.SetTilingType(const AValue: TPSTileType);
  755. begin
  756. if FTilingType=AValue then exit;
  757. FTilingType:=AValue;
  758. changed;
  759. end;
  760. procedure TPSPattern.SetXStep(const AValue: Real);
  761. begin
  762. if FXStep=AValue then exit;
  763. FXStep:=AValue;
  764. changed;
  765. end;
  766. procedure TPSPattern.SetYStep(const AValue: Real);
  767. begin
  768. if FYStep=AValue then exit;
  769. FYStep:=AValue;
  770. changed;
  771. end;
  772. constructor TPSPattern.Create;
  773. begin
  774. FPostScript := TStringList.Create;
  775. FPaintType := ptColored;
  776. FTilingType := ttConstant;
  777. FStream:=TmemoryStream.Create;
  778. FPatternCanvas := TPostScriptCanvas.Create(FStream);
  779. FName := 'Pattern1';
  780. end;
  781. destructor TPSPattern.Destroy;
  782. begin
  783. FPostScript.Free;
  784. FPatternCanvas.Free;
  785. FStream.Free;
  786. inherited Destroy;
  787. end;
  788. { ---------------------------------------------------------------------
  789. TPSBrush
  790. ---------------------------------------------------------------------}
  791. Function TPSBrush.GetAsString : String;
  792. begin
  793. Result:='';
  794. end;
  795. end.