pscanvas.pp 25 KB

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