fpreportpdfexport.pp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1. {
  2. This file is part of the Free Component Library.
  3. Copyright (c) WISA b.v.b.a
  4. FPReport PDF export filter.
  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. unit fpreportpdfexport;
  12. {$mode objfpc}{$H+}
  13. interface
  14. uses
  15. Classes,
  16. SysUtils,
  17. fpImage,
  18. fpreport,
  19. fpPDF;
  20. {$IF FPC_FULLVERSION>=30101}
  21. {$DEFINE PDF_HASISSTANDARDPDFFONT}
  22. {$DEFINE PDF_HASEXTERNALLINK}
  23. {$ENDIF}
  24. type
  25. TFPReportExportPDF = class(TFPReportExporter)
  26. private
  27. FCurrentPage: TPDFPage;
  28. FOptions: TPDFOptions;
  29. FPageLayout: TPDFPageLayout;
  30. FFileName: string;
  31. FDocument: TPDFDocument;
  32. FAutoSave: boolean;
  33. protected
  34. procedure RenderElement(pg: TPDFPage; ABand: TFPReportCustomBand; el: TFPReportElement); virtual;
  35. Procedure RenderImage(aRect : TFPReportRect; var AImage: TFPCustomImage) ; override;
  36. procedure DoExecute(const ARTObjects: TFPList); override;
  37. procedure SetupPDFDocument; virtual;
  38. procedure RenderFrame(const APage: TPDFPage; const ABand: TFPReportCustomBand; const AFrame: TFPReportFrame; const APos: TPDFCoord; const AWidth, AHeight: TFPReportUnits); virtual;
  39. procedure RenderMemo(const APage: TPDFPage; const ABand: TFPReportCustomBand; const AMemo: TFPReportCustomMemo); virtual;
  40. procedure RenderShape(const APage: TPDFPage; const ABand: TFPReportCustomBand; const AShape: TFPReportCustomShape); virtual;
  41. procedure RenderImage(const APage: TPDFPage; const ABand: TFPReportCustomBand; const AImage: TFPReportCustomImage); virtual;
  42. procedure RenderCheckbox(const APage: TPDFPage; const ABand: TFPReportCustomBand; const ACheckbox: TFPReportCustomCheckbox); virtual;
  43. procedure RenderShape(const APage: TPDFPage; const AOrigin: TPDFCoord; const AShape: TFPReportCustomShape); virtual;
  44. procedure RenderShapeCircle(const APage: TPDFPage; const lpt1: TPDFCoord; const ALayout: TFPReportLayout);
  45. procedure RenderShapeEllipse(const APage: TPDFPage; const lpt1: TPDFCoord; const ALayout: TFPReportLayout);
  46. procedure RenderShapeLine(const APage: TPDFPage; lpt1: TPDFCoord; const AOrientation: TFPReportOrientation; const ALayout: TFPReportLayout);
  47. procedure RenderShapeRect(const APage: TPDFPage; const lpt1: TPDFCoord; const ALayout: TFPReportLayout);
  48. procedure RenderShapeTriangle(const APage: TPDFPage; Alpt: TPDFCoord; const AOrientation: TFPReportOrientation; const ALayout: TFPReportLayout);
  49. procedure RenderShapeRoundedRect(const APage: TPDFPage; const lpt1: TPDFCoord; const ARadius: TFPReportUnits; const ALayout: TFPReportLayout);
  50. public
  51. constructor Create(AOwner: TComponent); override;
  52. destructor Destroy; override;
  53. Class Function Name : String; override;
  54. Class Function Description : String; override;
  55. Class Function DefaultExtension: String; override;
  56. Procedure SetFileName(const aFileName: String); override;
  57. function FindFontIndex(const ADoc: TPDFDocument; const AFontName: string): integer;
  58. procedure SaveToFile;
  59. property Document: TPDFDocument read FDocument;
  60. Property CurrentPage: TPDFPage Read FCurrentPage;
  61. published
  62. property AutoSave: boolean read FAutoSave write FAutoSave default True;
  63. property FileName: string read FFileName write FFileName;
  64. Property Options : TPDFOptions Read FOptions Write FOptions;
  65. property PageLayout : TPDFPageLayout read FPageLayout write FPageLayout default lSingle;
  66. end;
  67. implementation
  68. uses
  69. FPCanvas,
  70. fpTTF,
  71. fpparsettf;
  72. { TFPReportExportPDF }
  73. function TFPReportExportPDF.FindFontIndex(const ADoc: TPDFDocument; const AFontName: string): integer;
  74. function isStandardPdfFont: Boolean;
  75. begin
  76. {$IFDEF PDF_HASISSTANDARDPDFFONT}
  77. Result:=aDoc.IsStandardPDFFont(aFontName)
  78. {$ELSE}
  79. Result:=(AFontName='Courier') or (AFontName='Courier-Bold') or (AFontName='Courier-Oblique') or (AFontName='Courier-BoldOblique')
  80. or (AFontName='Helvetica') or (AFontName='Helvetica-Bold') or (AFontName='Helvetica-Oblique') or (AFontName='Helvetica-BoldOblique')
  81. or (AFontName='Times-Roman') or (AFontName='Times-Bold') or (AFontName='Times-Italic') or (AFontName='Times-BoldItalic')
  82. or (AFontName='Symbol')
  83. or (AFontName='ZapfDingbats');
  84. {$ENDIF}
  85. end;
  86. var
  87. i: integer;
  88. fnt: TFPFontCacheItem;
  89. begin
  90. Result := -1;
  91. for i := 0 to Document.Fonts.Count-1 do
  92. begin
  93. if Document.Fonts.FontDefs[i].Name = AFontName then
  94. begin
  95. Result := i;
  96. break;
  97. end;
  98. end; { for i ... }
  99. if Result = -1 then
  100. begin
  101. if IsStandardPDFFont then
  102. begin
  103. Result := Document.AddFont(AFontName);
  104. end
  105. else
  106. begin
  107. fnt := gTTFontCache.Find(AFontName); // we are doing a PostScript Name lookup (it contains Bold, Italic info)
  108. if not Assigned(fnt) then
  109. fnt:=gTTFontCache.FindFont(AFontName);
  110. if fnt=Nil then
  111. raise Exception.CreateFmt('fpreport: Could not find the font <%s> in the font cache.', [AFontName]);
  112. Result := Document.AddFont(fnt.FileName, AFontName)
  113. end;
  114. end;
  115. end;
  116. procedure TFPReportExportPDF.SetupPDFDocument;
  117. begin
  118. if Assigned(FDocument) then
  119. FDocument.Free;
  120. FDocument := TPDFDocument.Create(Nil);
  121. FDocument.Infos.Title := TFPReport(Report).Title;
  122. FDocument.Infos.Author := TFPReport(Report).Author;
  123. FDocument.Infos.ApplicationName := ApplicationName;
  124. FDocument.Infos.CreationDate := Now;
  125. FDocument.Options:=Self.Options;
  126. FDocument.PageLayout:=Self.PageLayout;
  127. FDocument.StartDocument;
  128. { we always need at least one section }
  129. FDocument.Sections.AddSection;
  130. end;
  131. procedure TFPReportExportPDF.SaveToFile;
  132. var
  133. F: TFileStream;
  134. begin
  135. if not Assigned(FDocument) then
  136. Exit;
  137. F := TFileStream.Create(FFileName, fmCreate);
  138. try
  139. FDocument.SaveToStream(F);
  140. finally
  141. F.Free;
  142. end;
  143. end;
  144. procedure TFPReportExportPDF.RenderFrame(const APage: TPDFPage; const ABand: TFPReportCustomBand;
  145. const AFrame: TFPReportFrame; const APos: TPDFCoord; const AWidth, AHeight: TFPReportUnits);
  146. var
  147. bStroke, bFill: boolean;
  148. begin
  149. bStroke := AFrame.Color <> clNone;
  150. bFill := AFrame.BackgroundColor <> clNone;
  151. // We only support TPDFPenStyle types. (ppsSolid,ppsDash,ppsDot,ppsDashDot,ppsDashDotDot)
  152. case AFrame.Pen of
  153. psSolid: APage.SetPenStyle(ppsSolid);
  154. psDash: APage.SetPenStyle(ppsDash);
  155. psDot: APage.SetPenStyle(ppsDot);
  156. psDashDot: APage.SetPenStyle(ppsDashDot);
  157. psDashDotDot: APage.SetPenStyle(ppsDashDotDot);
  158. // These FPCanvas pen styles are unsupported
  159. // psInsideFrame: ;
  160. // psPattern: ;
  161. // psClear: ;
  162. else
  163. // give a sane fallback for now
  164. APage.SetPenStyle(ppsSolid);
  165. end;
  166. if (AFrame.Shape = fsRectangle) and (bStroke or bFill) then
  167. begin
  168. APage.SetColor(AFrame.BackgroundColor, False); // fill color
  169. APage.SetColor(AFrame.Color, True); // stroke color
  170. APage.DrawRect(APos.X, APos.Y, AWidth, AHeight, AFrame.Width, bFill, bStroke);
  171. end;
  172. if AFrame.Shape = fsNone then
  173. begin
  174. if AFrame.Lines <> [] then
  175. begin
  176. APage.SetColor(AFrame.Color, True);
  177. APage.SetColor(AFrame.Color, False);
  178. end;
  179. { PDF origin coordinate is Bottom-Left, and Report Layout is Top-Left, so adjust them }
  180. if flTop in AFrame.Lines then
  181. APage.DrawLine(APos.X, APos.Y-AHeight, APos.X+AWidth, APos.Y-AHeight, AFrame.Width);
  182. if flBottom in AFrame.Lines then
  183. APage.DrawLine(APos.X, APos.Y, APos.X+AWidth, APos.Y, AFrame.Width);
  184. if flLeft in AFrame.Lines then
  185. APage.DrawLine(APos.X, APos.Y, APos.X, APos.Y-AHeight, AFrame.Width);
  186. if flRight in AFrame.Lines then
  187. APage.DrawLine(APos.X+AWidth, APos.Y, APos.X+AWidth, APos.Y-AHeight, AFrame.Width);
  188. end; { Frame.Shape = fsNone }
  189. end;
  190. procedure TFPReportExportPDF.RenderMemo(const APage: TPDFPage; const ABand: TFPReportCustomBand;
  191. const AMemo: TFPReportCustomMemo);
  192. var
  193. lPt1: TPDFCoord; // original Report point
  194. lFontIdx: integer;
  195. lMemo: TFPReportMemo;
  196. i: integer;
  197. lYPos: TPDFFloat;
  198. txtblk: TFPTextBlock;
  199. begin
  200. lMemo := TFPReportMemo(AMemo);
  201. { PDF origin coordinate is Bottom-Left, and Report Layout is Top-Left }
  202. lPt1.X := ABand.RTLayout.Left + AMemo.RTLayout.Left;
  203. lPt1.Y := ABand.RTLayout.Top + AMemo.RTLayout.Top + AMemo.RTLayout.Height;
  204. { Frame must be drawn before the text as it could have a fill color. }
  205. RenderFrame(APage, ABand, AMemo.Frame, lPt1, AMemo.RTLayout.Width, AMemo.RTLayout.Height);
  206. { Store the Top-Left coordinate of the Memo. We will be reusing this info. }
  207. lPt1.X := ABand.RTLayout.Left + AMemo.RTLayout.Left;
  208. lPt1.Y := ABand.RTLayout.Top + AMemo.RTLayout.Top;
  209. { render the TextBlocks as-is. }
  210. for i := 0 to lMemo.TextBlockList.Count-1 do
  211. begin
  212. txtblk := lMemo.TextBlockList[i];
  213. lFontIdx := FindFontIndex(Document, txtblk.FontName);
  214. APage.SetFont(lFontIdx, lMemo.Font.Size);
  215. { PDF origin coordinate is Bottom-Left, and Report Layout is Top-Left }
  216. lYPos := lPt1.Y + txtblk.Pos.Top + txtblk.Height;
  217. if txtblk.BGColor <> clNone then
  218. begin
  219. { draw highlighting background rectangle }
  220. APage.SetColor(txtblk.BGColor, false);
  221. APage.DrawRect(lPt1.X + txtblk.Pos.Left, lYPos+txtblk.Descender, txtblk.Width, txtblk.Height+(txtblk.Descender*2), 1.0, True, False);
  222. end;
  223. { Text color is always a fill color, hence the False parameter. }
  224. APage.SetColor(txtblk.FGColor, false);
  225. APage.WriteText(lPt1.X + txtblk.Pos.Left, lYPos, txtblk.Text);
  226. // process hyperlink if available
  227. if txtblk is TFPHTTPTextBlock then
  228. begin
  229. {$IFDEF PDF_HASEXTERNALLINK}
  230. APage.AddExternalLink(lPt1.X + txtblk.Pos.Left, lYPos, txtblk.Width, txtblk.Height, TFPHTTPTextBlock(txtblk).URL);
  231. {$ENDIF}
  232. end;
  233. end;
  234. end;
  235. procedure TFPReportExportPDF.RenderShape(const APage: TPDFPage; const ABand: TFPReportCustomBand;
  236. const AShape: TFPReportCustomShape);
  237. var
  238. lPt1: TPDFCoord; // original Report point
  239. begin
  240. APage.SetColor(clblack, True);
  241. APage.SetColor(clblack, False);
  242. { PDF origin coordinate is Bottom-Left, and Report Layout is Top-Left }
  243. lPt1.X := ABand.RTLayout.Left + AShape.RTLayout.Left;
  244. lPt1.Y := ABand.RTLayout.Top + AShape.RTLayout.Top + AShape.RTLayout.Height;
  245. { Frame must be drawn before the shape as it could have a fill color. }
  246. RenderFrame(APage, ABand, AShape.Frame, lPt1, AShape.RTLayout.Width, AShape.RTLayout.Height);
  247. { Only render shape when color is set and color is different to frame background color. }
  248. if (TFPReportShape(AShape).Color <> clNone) and
  249. (TFPReportShape(AShape).Color <> AShape.Frame.BackgroundColor) then
  250. RenderShape(APage, lPt1, AShape);
  251. end;
  252. type
  253. { for access to Protected methods }
  254. TReportImageFriend = class(TFPReportCustomImage);
  255. procedure TFPReportExportPDF.RenderImage(const APage: TPDFPage; const ABand: TFPReportCustomBand;
  256. const AImage: TFPReportCustomImage);
  257. var
  258. lPt: TPDFCoord;
  259. img: TReportImageFriend;
  260. idx, i: integer;
  261. pdfimg: TPDFImageItem;
  262. begin
  263. img := TReportImageFriend(AImage); { for access to Protected methods }
  264. lPt.X := ABand.RTLayout.Left + AImage.RTLayout.Left;
  265. { PDF origin coordinate is Bottom-Left, and Report Layout is Top-Left }
  266. lPt.Y := ABand.RTLayout.Top + AImage.RTLayout.Top + AImage.RTLayout.Height;
  267. { Frame must be drawn before the Image as it could have a fill color. }
  268. RenderFrame(APage, ABand, AImage.Frame, lPt, AImage.RTLayout.Width, AImage.RTLayout.Height);
  269. if not Assigned(img.Image) then
  270. Exit; { nothing further to do }
  271. idx := -1;
  272. for i := 0 to Document.Images.Count-1 do
  273. begin
  274. if Document.Images.Images[i].Equals(img.Image) then
  275. begin
  276. idx := i;
  277. break;
  278. end;
  279. end;
  280. if idx = -1 then
  281. begin
  282. pdfimg := Document.Images.AddImageItem;
  283. pdfimg.Image := img.Image;
  284. idx := Document.Images.Count-1;
  285. end;
  286. if img.Stretched then
  287. begin
  288. case APage.UnitOfMeasure of
  289. uomMillimeters:
  290. begin
  291. APage.DrawImage(lPt, AImage.RTLayout.Width, AImage.RTLayout.Height, idx);
  292. end;
  293. uomCentimeters:
  294. begin
  295. APage.DrawImage(lPt, AImage.RTLayout.Width, AImage.RTLayout.Height, idx);
  296. end;
  297. uomInches:
  298. begin
  299. APage.DrawImage(lPt, AImage.RTLayout.Width, AImage.RTLayout.Height, idx);
  300. end;
  301. uomPixels:
  302. begin
  303. APage.DrawImage(lPt, Integer(round(AImage.RTLayout.Width)), Integer(round(AImage.RTLayout.Height)), idx);
  304. end;
  305. end; { case UnitOfMeasure }
  306. end
  307. else
  308. APage.DrawImage(lPt, img.Image.Width, img.Image.Height, idx);
  309. end;
  310. procedure TFPReportExportPDF.RenderCheckbox(const APage: TPDFPage; const ABand: TFPReportCustomBand;
  311. const ACheckbox: TFPReportCustomCheckbox);
  312. var
  313. lPt: TPDFCoord;
  314. idx: integer;
  315. pdfimg: TPDFImageItem;
  316. lImage: TFPCustomImage;
  317. i: integer;
  318. begin
  319. lPt.X := ABand.RTLayout.Left + ACheckbox.RTLayout.Left;
  320. { PDF origin coordinate is Bottom-Left, and Report Layout is Top-Left }
  321. lPt.Y := ABand.RTLayout.Top + ACheckbox.RTLayout.Top + ACheckbox.RTLayout.Height;
  322. // { Frame must be drawn before the Image as it could have a fill color. }
  323. // RenderFrame(Document, APage, ABand, AImage.Frame, lPt, AImage.RTLayout.Width, AImage.RTLayout.Height);
  324. lImage:=ACheckBox.GetRTImage;
  325. idx := -1;
  326. for i := 0 to Document.Images.Count-1 do
  327. begin
  328. if Document.Images.Images[i].Equals(lImage) then
  329. begin
  330. idx := i;
  331. break;
  332. end;
  333. end;
  334. if idx = -1 then
  335. begin
  336. pdfimg := Document.Images.AddImageItem;
  337. pdfimg.Image := lImage;
  338. idx := Document.Images.Count-1;
  339. end;
  340. case APage.UnitOfMeasure of
  341. uomMillimeters:
  342. begin
  343. APage.DrawImage(lPt, ACheckBox.RTLayout.Width, ACheckBox.RTLayout.Height, idx);
  344. end;
  345. uomCentimeters:
  346. begin
  347. APage.DrawImage(lPt, ACheckBox.RTLayout.Width, ACheckBox.RTLayout.Height, idx);
  348. end;
  349. uomInches:
  350. begin
  351. APage.DrawImage(lPt, ACheckBox.RTLayout.Width, ACheckBox.RTLayout.Height, idx);
  352. end;
  353. uomPixels:
  354. begin
  355. APage.DrawImage(lPt, Integer(round(ACheckBox.RTLayout.Width)), Integer(round(ACheckBox.RTLayout.Height)), idx);
  356. end;
  357. end; { case UnitOfMeasure }
  358. end;
  359. procedure TFPReportExportPDF.RenderShape(const APage: TPDFPage; const AOrigin: TPDFCoord;
  360. const AShape: TFPReportCustomShape);
  361. begin
  362. APage.SetColor(TFPReportShape(AShape).Color, True);
  363. APage.SetColor(TFPReportShape(AShape).Color, False);
  364. case TFPReportShape(AShape).ShapeType of
  365. stEllipse: RenderShapeEllipse(APage, AOrigin, AShape.RTLayout);
  366. stCircle: RenderShapeCircle(APage, AOrigin, AShape.RTLayout);
  367. stLine: RenderShapeLine(APage, AOrigin, TFPReportShape(AShape).Orientation, AShape.RTLayout);
  368. stSquare: RenderShapeRect(APage, AOrigin, AShape.RTLayout);
  369. stTriangle: RenderShapeTriangle(APage, AOrigin, TFPReportShape(AShape).Orientation, AShape.RTLayout);
  370. stRoundedRect: RenderShapeRoundedRect(APage, AOrigin, TFPReportShape(AShape).CornerRadius, AShape.RTLayout);
  371. end;
  372. end;
  373. procedure TFPReportExportPDF.RenderShapeCircle(const APage: TPDFPage; const lpt1: TPDFCoord;
  374. const ALayout: TFPReportLayout);
  375. var
  376. lPt2: TPDFCoord;
  377. ldx, ldy, lw: TFPReportUnits;
  378. begin
  379. if ALayout.Width = ALayout.Height then
  380. begin
  381. ldx := 0;
  382. ldy := 0;
  383. lw := ALayout.Width;
  384. end
  385. else if ALayout.Width > ALayout.Height then
  386. begin
  387. ldx := (ALayout.Width - ALayout.Height) / 2;
  388. ldy := 0;
  389. lw := ALayout.Height;
  390. end
  391. else if ALayout.Width < ALayout.Height then
  392. begin
  393. ldx := 0;
  394. ldy := (ALayout.Height - ALayout.Width) / 2;
  395. lw := ALayout.Width;
  396. end;
  397. { PDF origin coordinate is Bottom-Left, and Report Layout is Top-Left }
  398. lPt2.X := lPt1.X + ldx;
  399. lPt2.Y := lPt1.Y - ldy;
  400. APage.DrawEllipse(lPt2, lw, lw, 1, False, True);
  401. end;
  402. procedure TFPReportExportPDF.RenderShapeEllipse(const APage: TPDFPage; const lpt1: TPDFCoord;
  403. const ALayout: TFPReportLayout);
  404. begin
  405. APage.DrawEllipse(lPt1, ALayout.Width, ALayout.Height, 1, False, True);
  406. end;
  407. procedure TFPReportExportPDF.RenderShapeLine(const APage: TPDFPage; lpt1: TPDFCoord;
  408. const AOrientation: TFPReportOrientation; const ALayout: TFPReportLayout);
  409. var
  410. lPt2: TPDFCoord;
  411. begin
  412. case AOrientation of
  413. orNorth, orSouth:
  414. begin // | (1)
  415. lPt1.X := lPt1.X + (ALayout.Width / 2); // |
  416. lPt2.X := lPt1.X ; // |
  417. lPt2.Y := lPt1.Y; // | (2)
  418. lPt1.Y := lPt1.Y - ALayout.Height;
  419. end;
  420. orNorthEast, orSouthWest:
  421. begin // / (1)
  422. lPt2.X := lPt1.X; // /
  423. lPt1.X := lPt1.X + ALayout.Width; // /
  424. lPt2.Y := lPt1.Y; // / (2)
  425. lPt1.Y := lPt1.Y - ALayout.Height;
  426. end;
  427. orEast, orWest:
  428. begin // (1) (2)
  429. lPt2.X := lPt1.X + ALayout.Width; // ----------
  430. lPt1.Y := lPt1.Y - (ALayout.Height / 2); //
  431. lPt2.Y := lPt1.Y; //
  432. end;
  433. orSouthEast, orNorthWest:
  434. begin
  435. lPt1.Y := lPt1.Y - ALayout.Height; // \ (1)
  436. lPt2.X := lPt1.X + ALayout.Width; // \
  437. lPt2.Y := lPt1.Y + ALayout.Height; // \
  438. end; // \ (2)
  439. end;
  440. APage.DrawLine(lPt1, lPt2, 1);
  441. end;
  442. procedure TFPReportExportPDF.RenderShapeRect(const APage: TPDFPage; const lpt1: TPDFCoord;
  443. const ALayout: TFPReportLayout);
  444. var
  445. ldx, ldy, lw: TFPReportUnits;
  446. P: TPDFCoord;
  447. begin
  448. if ALayout.Width = ALayout.Height then
  449. begin
  450. ldx := 0;
  451. ldy := 0;
  452. lw := ALayout.Width;
  453. end
  454. else if ALayout.Width > ALayout.Height then
  455. begin
  456. ldx := (ALayout.Width - ALayout.Height) / 2;
  457. ldy := 0;
  458. lw := ALayout.Height;
  459. end
  460. else if ALayout.Width < ALayout.Height then
  461. begin
  462. ldx := 0;
  463. ldy := (ALayout.Height - ALayout.Width) / 2;
  464. lw := ALayout.Width;
  465. end;
  466. P.X := lPt1.X + ldx;
  467. { PDF origin coordinate is Bottom-Left, and Report Layout is Top-Left }
  468. P.Y := lPt1.Y - ldy;
  469. APage.DrawRect(P, lw, lw, 1, False, True);
  470. end;
  471. procedure TFPReportExportPDF.RenderShapeTriangle(const APage: TPDFPage; Alpt: TPDFCoord;
  472. const AOrientation: TFPReportOrientation; const ALayout: TFPReportLayout);
  473. var
  474. lPt1, lPt2, lPt3: TPDFCoord; // original Report point
  475. lOrigin: TPDFCoord;
  476. W, H: TFPReportUnits;
  477. begin
  478. lOrigin.X := AlPt.X;
  479. lOrigin.Y := ALPT.Y - ALayout.Height;
  480. W:=ALayout.Width;
  481. H:=ALayout.Height;
  482. case AOrientation of
  483. orNorth:
  484. begin
  485. lPt1.X := lOrigin.X + (W / 2); // 1
  486. lPt1.Y := lOrigin.Y; // /\
  487. lPt2.X := lOrigin.X; // / \
  488. lPt2.Y := lOrigin.Y + H; // /____\
  489. lPt3.X := lOrigin.X + W; // 2 3
  490. lPt3.Y := lPt2.Y;
  491. end;
  492. orNorthEast:
  493. begin
  494. lPt1.X := lOrigin.X + (W ); // +-------1
  495. lPt1.Y := lOrigin.Y; // | |
  496. lPt2.X := lOrigin.X; // 2 |
  497. lPt2.Y := lOrigin.Y + H/2; // | |
  498. lPt3.X := lOrigin.X + W/2; // +---3---+
  499. lPt3.Y := lPt1.Y + H;
  500. end;
  501. orSouth:
  502. begin
  503. lPt1.X := lOrigin.X; // 1 ------ 2
  504. lPt1.Y := lOrigin.Y; // \ /
  505. lPt2.X := lOrigin.X+ W; // \ /
  506. lPt2.Y := lOrigin.Y; // \/
  507. lPt3.X := lOrigin.X + (W / 2); // 3
  508. lPt3.Y := lOrigin.Y+H;
  509. end;
  510. orSouthEast:
  511. begin
  512. lPt1.X := lOrigin.X + (W/2); // +---1---+
  513. lPt1.Y := lOrigin.Y; // | |
  514. lPt2.X := lOrigin.X; // 2 |
  515. lPt2.Y := lOrigin.Y + H/2; // | |
  516. lPt3.X := lOrigin.X + W; // +-------3
  517. lPt3.Y := lPt1.Y + H;
  518. end;
  519. orEast:
  520. begin
  521. lPt1.X := lOrigin.X; // 1
  522. lPt1.Y := lOrigin.Y ; // |\
  523. lPt2.X := lOrigin.X + W; // | \ 2
  524. lPt2.Y := lOrigin.Y + (H / 2); // | /
  525. lPt3.X := lOrigin.X; // |/
  526. lPt3.Y := lOrigin.Y + H; // 3
  527. end;
  528. orNorthWest:
  529. begin
  530. lPt1.X := lOrigin.X; // 1-------+
  531. lPt1.Y := lOrigin.Y; // | |
  532. lPt2.X := lOrigin.X+W; // | 2
  533. lPt2.Y := lOrigin.Y + H/2; // | |
  534. lPt3.X := lOrigin.X + W/2; // +---3---+
  535. lPt3.Y := lPt1.Y + H;
  536. end;
  537. orWest:
  538. begin
  539. lPt1.X := lOrigin.X + W; // 1
  540. lPt1.Y := lOrigin.Y; // /|
  541. lPt2.X := lOrigin.X; // 2 / |
  542. lPt2.Y := lOrigin.Y + H / 2; // \ |
  543. lPt3.X := lOrigin.X + W; // \|
  544. lPt3.Y := lOrigin.Y+ H; // 3
  545. end;
  546. orSouthWest:
  547. begin
  548. lPt1.X := lOrigin.X+ H/2; // +---1---+
  549. lPt1.Y := lOrigin.Y; // | |
  550. lPt2.X := lOrigin.X+W; // | 2
  551. lPt2.Y := lOrigin.Y + H/2; // | |
  552. lPt3.X := lOrigin.X ; // 3-------+
  553. lPt3.Y := lPt1.Y + H;
  554. end;
  555. end;
  556. APage.DrawLine(lPt1, lPt2, 1);
  557. APage.DrawLine(lPt2, lPt3, 1);
  558. APage.DrawLine(lPt3, lPt1, 1);
  559. end;
  560. procedure TFPReportExportPDF.RenderShapeRoundedRect(const APage: TPDFPage; const lpt1: TPDFCoord;
  561. const ARadius: TFPReportUnits; const ALayout: TFPReportLayout);
  562. begin
  563. end;
  564. constructor TFPReportExportPDF.Create(AOwner: TComponent);
  565. begin
  566. inherited Create(AOwner);
  567. FDocument := nil;
  568. FFileName := ApplicationName + '.pdf';
  569. FAutoSave := True;
  570. end;
  571. destructor TFPReportExportPDF.Destroy;
  572. begin
  573. FDocument.Free;
  574. inherited Destroy;
  575. end;
  576. class function TFPReportExportPDF.Name: String;
  577. begin
  578. Result:='PDF';
  579. end;
  580. class function TFPReportExportPDF.Description: String;
  581. begin
  582. Result:='PDF file';
  583. end;
  584. class function TFPReportExportPDF.DefaultExtension: String;
  585. begin
  586. Result:='.pdf';
  587. end;
  588. procedure TFPReportExportPDF.SetFileName(const aFileName: String);
  589. begin
  590. Filename:=aFileName;
  591. end;
  592. procedure TFPReportExportPDF.RenderImage(aRect: TFPReportRect; var AImage: TFPCustomImage);
  593. var
  594. LPT : TPDFCoord;
  595. Idx : Integer;
  596. pdfimg: TPDFImageItem;
  597. begin
  598. LPT.X:=aRect.Left;
  599. LPT.Y:=aRect.Top;
  600. idx:=Document.Images.Count-1;
  601. While (Idx>=0) and not Document.Images.Images[idx].Equals(AImage) do
  602. Dec(Idx);
  603. if idx = -1 then
  604. begin
  605. pdfimg := Document.Images.AddImageItem;
  606. pdfimg.Image := AImage;
  607. pdfimg.OwnsImage:=True;
  608. idx := Document.Images.Count-1;
  609. end;
  610. CurrentPage.DrawImage(lPt, aRect.Width, ARect.Height, idx);
  611. aImage:=Nil; // PDF now owns the image
  612. end;
  613. procedure TFPReportExportPDF.DoExecute(const ARTObjects: TFPList);
  614. var
  615. pg: TPDFPage;
  616. p, b, m: integer;
  617. rpage: TFPReportPage;
  618. rband: TFPReportCustomBand;
  619. lPt1: TPDFCoord; // original Report point
  620. lPDFPaper: TPDFPaper;
  621. begin
  622. SetupPDFDocument;
  623. for p := 0 to (ARTObjects.Count - 1) do
  624. begin
  625. rpage := TFPReportPage(ARTObjects[p]);
  626. pg := FDocument.Pages.AddPage;
  627. FCurrentPage:=pg;
  628. case rpage.PageSize.PaperName of
  629. 'A4': pg.PaperType := ptA4;
  630. 'A5': pg.PaperType := ptA5;
  631. 'Letter': pg.PaperType := ptLetter;
  632. 'Legal': pg.PaperType := ptLegal;
  633. 'DL': pg.PaperType := ptDL;
  634. 'C5': pg.PaperType := ptC5;
  635. 'B5': pg.PaperType := ptB5
  636. else
  637. begin
  638. lPDFPaper.W := Round(mmToPDF(rpage.PageSize.Width));
  639. lPDFPaper.H := Round(mmToPDF(rpage.PageSize.Height));
  640. pg.Paper := lPDFPaper;
  641. pg.PaperType := ptCustom;
  642. end;
  643. end; { case PaperName }
  644. pg.UnitOfMeasure := uomMillimeters; { report measurements are always in millimeter units }
  645. // This must appear before configuring the pg.Matrix
  646. if rpage.Orientation = poLandscape then
  647. pg.Orientation := ppoLandscape;
  648. // Convert from the Cartesian coordinate system to the Screen coordinate system
  649. pg.Matrix.SetYScalation(-1);
  650. pg.Matrix.SetYTranslation(pg.GetPaperHeight);
  651. for b := 0 to (rpage.BandCount - 1) do
  652. begin
  653. rband := rpage.Bands[b];
  654. lPt1.X := rband.RTLayout.Left;
  655. { PDF origin coordinate is Bottom-Left, and Report Layout is Top-Left }
  656. lPt1.Y := rband.RTLayout.Top + rband.RTLayout.Height;
  657. RenderFrame(pg, rband, rband.Frame, lPt1, rband.RTLayout.Width, rband.RTLayout.Height);
  658. for m := 0 to (rband.ChildCount - 1) do
  659. RenderElement(pg, rband, rband.Child[m]);
  660. end;
  661. Document.Sections[0].AddPage(pg);
  662. end;
  663. if FAutoSave then
  664. SaveToFile;
  665. end;
  666. procedure TFPReportExportPDF.RenderElement(pg : TPDFPage; ABand : TFPReportCustomBand; el : TFPReportElement);
  667. Var
  668. C : TFPReportPoint;
  669. lpt : TPDFCoord;
  670. begin
  671. if (el is TFPReportCustomMemo) then
  672. RenderMemo(pg, aband, TFPReportCustomMemo(el))
  673. else if (el is TFPReportCustomShape) then
  674. RenderShape(pg, aband, TFPReportCustomShape(el))
  675. else if (el is TFPReportCustomImage) then
  676. RenderImage(pg, aband, TFPReportCustomImage(el))
  677. else if (el is TFPReportCustomCheckbox) then
  678. RenderCheckbox(pg, aband, TFPReportCustomCheckbox(el))
  679. else
  680. begin
  681. // PDF coords
  682. lPt.X := ABand.RTLayout.Left + el.RTLayout.Left;
  683. lPt.Y := ABand.RTLayout.Top + el.RTLayout.Top + el.RTLayout.Height;
  684. RenderFrame(pg, ABand, el.Frame, lPt, el.RTLayout.Width, el.RTLayout.Height);
  685. C.Left:=aband.RTLayout.Left;
  686. // Compensate for add of height which RenderUnknownElement will do
  687. C.Top:=aband.RTLayout.Top + el.RTLayout.Height;
  688. RenderUnknownElement(C,El,72);
  689. end;
  690. end;
  691. initialization
  692. TFPReportExportPDF.RegisterExporter;
  693. end.