pscanvas.pp 25 KB

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