fplazreport.pp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. {
  2. This file is part of the Free Component Library.
  3. Copyright (c) 2017 Michael Van Canneyt, member of the Free Pascal development team
  4. TFPReport descendent that stores it's design in a JSON structure.
  5. Can be used in an IDE
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit fplazreport;
  13. {$mode objfpc}{$H+}
  14. interface
  15. uses
  16. Classes, SysUtils, fpreport, DOM, FPCanvas, fpTTF, fpreportdb, fpreportbarcode;
  17. Type
  18. TCustomPropEvent = procedure(Sender: TObject;Data : TDOMNode) of object;
  19. TConvertLogEvent = Procedure(Sender: TOBject;Const Msg : String) of Object;
  20. TNameConvertEvent = Procedure(Sender: TOBject;Const aName : UnicodeString; Var aNewName : String) of Object;
  21. TFontSubstitutionEvent = Procedure(Sender: TOBject;Const aFontName : String; Const aBold,aItalic: Boolean; var aFont : TFPFontCacheItem) of Object;
  22. { TFPLazReport }
  23. TFPLazReport = class(TFPReport)
  24. private
  25. FData: TComponent;
  26. FMasterData: TFPReportDataBand;
  27. FDetailHeader : TFPReportDataHeaderBand;
  28. FDetailFooter : TFPReportDataFooterBand;
  29. FDetailBand: TFPReportDataBand;
  30. FMemoClass: TFPReportElementClass;
  31. FOnConvertName: TNameConvertEvent;
  32. FOnLog: TConvertLogEvent;
  33. FOnSetCustomProps: TCustomPropEvent;
  34. FOnSubstituteFont: TFontSubstitutionEvent;
  35. FCounter : Integer;
  36. FNullBand : TFPReportChildBand;
  37. Protected
  38. class function Red(rgb: Integer): BYTE; virtual;
  39. class function Green(rgb: Integer): BYTE; virtual;
  40. class function Blue(rgb: Integer): BYTE; virtual;
  41. function FindBand(aPage: TFPReportCustomPage; aTop: double; AElement: TFPReportElement=Nil): TFPReportCustomBand; virtual;
  42. class function GetProperty(aNode: TDOMNode; const aName: String; const aValue: string='Value'): UTF8String; virtual;
  43. function ApplyFrame(aDataNode: TDOMNode; aFrame: TFPReportFrame): Boolean; virtual;
  44. procedure ApplyObjectProperties(ObjNode: TDOMNode; aObj: TFPReportElement); virtual;
  45. procedure ConvertPageProperties(aPage: TFPReportPage; aPageNode: TDOMNode); virtual;
  46. procedure SetData(AValue: TComponent);virtual;
  47. procedure SizeToLayout(aDataNode: TDOMNode; aObj: TFPReportElement);virtual;
  48. Function ConvertComponentName(Const aName : UnicodeString;Const AClassName : String) : String; virtual;
  49. function ConvertFont(aDataNode: TDomNode): TFPFontCacheItem; virtual;
  50. function ConvertBand(aBandNode: TDomNode;aPage: TFPReportCustomPage): TFPReportCustomBand; virtual;
  51. function ConvertMemo(ObjNode: TDOMNode; aPage: TFPReportCustomPage): TFPReportMemo; virtual;
  52. function ConvertPage(aPageNode: TDOMNode): TFPReportPage; virtual;
  53. function ConvertLine(ObjNode: TDOMNode; APage: TFPReportCustomPage): TFPReportShape; virtual;
  54. function ConvertImage(ObjNode: TDOMNode; APage: TFPReportCustomPage): TFPReportImage; virtual;
  55. function ConvertBarcode(ObjNode : TDOMNode; APage : TFPReportCustomPage) : TFPReportBarcode; virtual;
  56. Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  57. Procedure DoLog(Const Msg : String);
  58. Procedure DoLog(Const Fmt : String; Const Args : Array of const);
  59. Public
  60. constructor Create(AOwner: TComponent); override;
  61. function FixDataFields(aFieldName : string) : string;
  62. property MemoClass : TFPReportElementClass read FMemoClass write FmemoClass;
  63. Procedure LoadFromXML(LazReport : TXMLDocument);virtual;
  64. Procedure LoadFromFile(const aFileName : String);
  65. Published
  66. property DataContainer : TComponent read FData write SetData;
  67. property OnSetCustomproperties : TCustomPropEvent read FOnSetCustomProps write FOnSetCustomProps;
  68. Property OnLog : TConvertLogEvent Read FOnLog Write FOnLog;
  69. Property OnSubstituteFont : TFontSubstitutionEvent Read FOnSubstituteFont Write FOnSubstituteFont;
  70. Property OnConvertName : TNameConvertEvent Read FOnConvertName Write FOnConvertName;
  71. end;
  72. function MMToPixels(Const Dist: double) : Integer;
  73. function PixelsToMM(Const Dist: double) : TFPReportUnits;
  74. implementation
  75. uses dateutils, XMLRead,FPReadPNG,FPimage,FPReadGif,FPReadJPEG,fpbarcode;
  76. Resourcestring
  77. SLogUnknownClass = 'Ignoring unknown lazreport class type for object "%s": "%s".';
  78. SErrUnknownBandType = 'Unknown band type: "%s", substituting child band';
  79. SErrWrongEncoding = 'Unknown image encoding at pos %d : %s';
  80. SFontSubstitution = 'FontSubstitution';
  81. SErrUnknownImageType = 'Unknown image type encountered: "%s"';
  82. SWarnConvertName = 'Name conversion: "%s" to "%s"';
  83. SUnknownBarcodeType = 'Unknown barcode type: "%s"';
  84. SIgnoringAngleOnBarcode = 'Igoring angle on barcode';
  85. SIgnoringShowTextOnBarcode = 'Igoring showtext on barcode';
  86. SLogNullBandAssigned = 'Null (child) band assigned for element of type "%s" at pos %5.3f';
  87. function PixelsToMM(Const Dist: double) : TFPReportUnits;
  88. begin
  89. Result:=Dist*(1/3.76);
  90. end;
  91. function MMToPixels(Const Dist: double) : Integer;
  92. begin
  93. Result:=round(Dist*(3.76));
  94. end;
  95. function PageToMM(Const Dist: double) : TFPReportUnits;
  96. begin
  97. Result:=Dist*(1/2.83);
  98. end;
  99. { TFPLazReport }
  100. procedure TFPLazReport.SetData(AValue: TComponent);
  101. begin
  102. if FData=AValue then Exit;
  103. if Assigned(FData) then
  104. FData.RemoveFreeNotification(Self);
  105. FData:=AValue;
  106. if Assigned(FData) then
  107. FData.FreeNotification(Self);
  108. end;
  109. procedure TFPLazReport.Notification(AComponent: TComponent; Operation: TOperation);
  110. begin
  111. inherited Notification(AComponent, Operation);
  112. if Operation=opRemove then
  113. if (AComponent=FData) then
  114. FData:=Nil;
  115. end;
  116. procedure TFPLazReport.DoLog(const Msg: String);
  117. begin
  118. If Assigned(FOnLog) then
  119. FOnLog(Self,Msg);
  120. end;
  121. procedure TFPLazReport.DoLog(const Fmt: String; const Args: array of const);
  122. Var
  123. S : String;
  124. begin
  125. try
  126. S:=Format(Fmt,Args);
  127. except
  128. on E : Exception do
  129. S:=Format('Failed to format error message "%s" with %d arguments',[Fmt,Length(Args)]);
  130. end;
  131. DoLog(S);
  132. end;
  133. constructor TFPLazReport.Create(AOwner: TComponent);
  134. begin
  135. inherited Create(AOwner);
  136. MemoClass := TFPReportMemo;
  137. DataContainer := Owner;
  138. end;
  139. function TFPLazReport.FixDataFields(aFieldName: string): string;
  140. var
  141. k : Integer = 0;
  142. begin
  143. Result := aFieldName;
  144. if Assigned(FData) then
  145. while k < FData.ComponentCount do
  146. begin
  147. if FData.Components[k] is TFPReportDatasetData then
  148. Result := StringReplace(Result,TFPReportDatasetData(FData.Components[k]).Name+'.',TFPReportDatasetData(FData.Components[k]).Name+'.',[rfReplaceAll,rfIgnoreCase]);
  149. inc(k);
  150. end;
  151. Result := StringReplace(Result,'PAGE#','PageNo',[rfReplaceAll,rfIgnoreCase]);
  152. Result := StringReplace(Result,'[DATE]','[TODAY]',[rfReplaceAll,rfIgnoreCase]);
  153. end;
  154. Class function TFPLazReport.Blue(rgb: Integer): BYTE;
  155. begin
  156. Result := (rgb shr 16) and $000000ff;
  157. end;
  158. Class function TFPLazReport.Green(rgb: Integer): BYTE;
  159. begin
  160. Result := (rgb shr 8) and $000000ff;
  161. end;
  162. Class function TFPLazReport.Red(rgb: Integer): BYTE;
  163. begin
  164. Result := rgb and $000000ff;
  165. end;
  166. procedure TFPLazReport.LoadFromXML(LazReport: TXMLDocument);
  167. var
  168. i: Integer;
  169. BaseNode,lPages : TDOMNode;
  170. aPage: TFPReportPage;
  171. begin
  172. BaseNode := LazReport.DocumentElement.FindNode('LazReport');
  173. if not Assigned(BaseNode) then
  174. exit;
  175. lPages := BaseNode.FindNode('Pages');
  176. if Not Assigned(lPages) then
  177. exit;
  178. TwoPass:= GetProperty(lPages,'DoublePass') = 'True';
  179. with lPages.ChildNodes do
  180. for i := 0 to (Count - 1) do
  181. if (copy(Item[i].NodeName,0,4)='Page') and (Item[i].NodeName<>'PageCount') then
  182. begin
  183. aPage:=ConvertPage(Item[i]);
  184. AddPage(aPage);
  185. end;
  186. end;
  187. Class function TFPLazReport.GetProperty(aNode : TDOMNode;Const aName : String; Const aValue : string = 'Value') : UTF8String;
  188. var
  189. bNode: TDOMNode;
  190. begin
  191. Result := '';
  192. bNode := aNode.FindNode(aName);
  193. if Assigned(bNode) then
  194. if Assigned(bNode.Attributes.GetNamedItem(aValue)) then
  195. Result := UTF8Encode(bNode.Attributes.GetNamedItem(aValue).NodeValue);
  196. end;
  197. function TFPLazReport.FindBand(aPage : TFPReportCustomPage;aTop : double; AElement : TFPReportElement = Nil) : TFPReportCustomBand;
  198. var
  199. b : Integer;
  200. S : String;
  201. begin
  202. Result := nil;
  203. for b := 0 to aPage.BandCount-1 do
  204. begin
  205. if (aTop>=aPage.Bands[b].Layout.Top)
  206. and (aTop<=aPage.Bands[b].Layout.Top+aPage.Bands[b].Layout.Height) then
  207. begin
  208. Result := aPage.Bands[b];
  209. break;
  210. end;
  211. end;
  212. if (Result=Nil) then
  213. begin
  214. if (FNullBand=Nil) then
  215. begin
  216. FNullBand:=TFPReportChildBand.Create(Self);
  217. FNullBand.Name:='NullBand';
  218. FNullBand.Layout.Height:=aPage.Layout.Height;
  219. FNullBand.Parent:=aPage;
  220. end;
  221. Result:=FNullBand;
  222. end;
  223. if (Result=FNullBand) and Assigned(FOnLog) then
  224. begin
  225. if aElement = Nil then
  226. S:='<unknown>'
  227. else
  228. S:=aElement.ClassName;
  229. DoLog(SLogNullBandAssigned,[S,aTop]);
  230. end;
  231. end;
  232. Function TFPLazReport.ConvertBand(aBandNode : TDomNode;aPage: TFPReportCustomPage) : TFPReportCustomBand;
  233. Var
  234. Tmp : String;
  235. aBand : TFPReportCustomBand;
  236. aData : TFPreportData;
  237. begin
  238. tmp := GetProperty(aBandNode,'BandType');
  239. case tmp of
  240. 'btReportTitle':
  241. aBand := TFPReportTitleBand.Create(Self);
  242. 'btMasterData':
  243. begin
  244. aBand := TFPReportDataBand.Create(Self);
  245. tmp := GetProperty(aBandNode,'DatasetStr');
  246. if copy(tmp,1,1)='P' then
  247. tmp := copy(tmp,2,system.length(tmp));
  248. if Assigned(FData) then
  249. aData := TFPreportData(FData.FindComponent(tmp));
  250. if Assigned(aData) then
  251. TFPReportDataBand(aBand).Data := aData;
  252. FMasterData := TFPReportDataBand(aBand);
  253. end;
  254. 'btMasterHeader':
  255. begin
  256. aBand := TFPReportDataHeaderBand.Create(Self);
  257. end;
  258. 'btMasterFooter':
  259. begin
  260. aBand := TFPReportDataFooterBand.Create(Self);
  261. end;
  262. 'btDetailData':
  263. begin
  264. aBand := TFPReportDataBand.Create(Self);
  265. tmp := GetProperty(aBandNode,'DatasetStr');
  266. if copy(tmp,1,1)='P' then
  267. tmp := copy(tmp,2,system.length(tmp));
  268. if Assigned(FData) and (FData.FindComponent(tmp) <> nil) then
  269. aData:=TFPreportData(FData.FindComponent(tmp));
  270. if Assigned(aData) and (ReportData.FindReportDataItem(aData)=Nil) then
  271. ReportData.AddReportData(aData);
  272. TFPReportDataBand(aBand).Data:=aData;
  273. TFPReportDataBand(aBand).MasterBand := FMasterData;
  274. FDetailBand := TFPReportDataBand(aBand);
  275. if Assigned(FDetailHeader) then
  276. begin
  277. FDetailHeader.Data := AData;
  278. FDetailHeader := nil;
  279. end;
  280. if Assigned(FDetailFooter) then
  281. begin
  282. FDetailFooter.Data:=aData;
  283. FDetailFooter:=nil;
  284. end;
  285. end;
  286. 'btDetailHeader':
  287. begin
  288. aBand:=TFPReportDataHeaderBand.Create(Self);
  289. if Assigned(FDetailBand) then
  290. TFPReportDataHeaderBand(aBand).Data := FDetailBand.Data
  291. else
  292. FDetailHeader:=TFPReportDataHeaderBand(aBand);
  293. end;
  294. 'btDetailFooter':
  295. begin
  296. aBand := TFPReportDataFooterBand.Create(Self);
  297. if Assigned(FDetailBand) then
  298. TFPReportDataFooterBand(aBand).Data := FDetailBand.Data
  299. else
  300. FDetailFooter := TFPReportDataFooterBand(aBand);
  301. end;
  302. 'btPageHeader':
  303. aBand := TFPReportPageHeaderBand.Create(Self);
  304. 'btPageFooter':
  305. aBand := TFPReportPageFooterBand.Create(Self);
  306. 'btGroupHeader':
  307. begin
  308. aBand:=TFPReportGroupHeaderBand.Create(Self);
  309. tmp := GetProperty(aBandNode,'Condition');
  310. if copy(tmp,0,1)='[' then
  311. tmp := copy(tmp,2,system.length(tmp)-2);//remove []
  312. tmp := FixDataFields(tmp);
  313. TFPReportGroupHeaderBand(aBand).GroupCondition:=tmp;
  314. end;
  315. 'btGroupFooter':
  316. aBand := TFPReportGroupFooterBand.Create(Self);
  317. else
  318. begin
  319. DoLog(SErrUnknownBandType,[Tmp]);
  320. aBand := TFPReportChildBand.Create(Self);
  321. end;
  322. end;
  323. if Assigned(aBand) then
  324. begin
  325. TFPReportDataBand(aBand).StretchMode:=smActualHeight;
  326. aBand.Parent:=aPage;
  327. end;
  328. Result:=aBand;
  329. end;
  330. Function TFPLazReport.ConvertFont(aDataNode : TDomNode) : TFPFontCacheItem;
  331. Var
  332. i : Integer;
  333. FontFound, aBold, aItalic : Boolean;
  334. aFont : TFPFontCacheItem;
  335. RealFont,FontName : String;
  336. begin
  337. aBold := pos('fsBold',GetProperty(aDataNode,'Style'))>0;
  338. aItalic := pos('fsItalic',GetProperty(aDataNode,'Style'))>0;
  339. FontName:=GetProperty(aDataNode,'Name');
  340. aFont := gTTFontCache.Find(FontName,aBold,aItalic);
  341. FontFound := not Assigned(aFont);
  342. if not Assigned(aFont) then
  343. aFont := gTTFontCache.Find('LiberationSans',aBold,aItalic);
  344. if not Assigned(aFont) then
  345. aFont := gTTFontCache.Find('Arial',aBold,aItalic);
  346. if not Assigned(aFont) then
  347. aFont := gTTFontCache.Find('DejaVu',aBold,aItalic);
  348. with gTTFontCache do
  349. begin
  350. i:=0;
  351. While (aFont=Nil) and (i<Count) do
  352. begin
  353. aFont := Items[i];
  354. if Not ((pos('sans',lowercase(aFont.FamilyName)) > 0) and (aFont.IsItalic = AItalic)
  355. and (aFont.IsBold = ABold)) then
  356. aFont:=nil;
  357. Inc(i);
  358. end;
  359. end;
  360. if Not FontFound then
  361. begin
  362. // Allow user to override
  363. If Assigned(FOnSubstituteFont) then
  364. FOnSubstituteFont(Self,FontName,aBold,aItalic,aFont);
  365. // Log it
  366. if Assigned(FOnLog) then
  367. begin
  368. if Assigned(aFont) then
  369. RealFont:=aFont.FamilyName
  370. else
  371. RealFont:='<nil>';
  372. if aBold then
  373. RealFont:=RealFont+'[Bold]';
  374. if aItalic then
  375. RealFont:=RealFont+'[Italic]';
  376. DoLog(SFontSubstitution,[FOntName,RealFont]);
  377. end;
  378. end;
  379. Result:=aFont;
  380. end;
  381. Function TFPLazReport.ConvertMemo(ObjNode : TDOMNode;aPage : TFPReportCustomPage) : TFPReportMemo;
  382. Var
  383. aDataNode: TDOMNode;
  384. aBand: TFPReportCustomBand;
  385. aColor,aSize,aFlag : Integer;
  386. aFont: TFPFontCacheItem;
  387. begin
  388. aDataNode := ObjNode.FindNode('Size');
  389. aBand := FindBand(aPage,PixelsToMM(StrToFloatDef(GetProperty(aDataNode,'Top'),0)));
  390. Result := MemoClass.Create(Self) as TFPReportMemo;
  391. Result.Parent:=aBand;
  392. aDataNode := ObjNode.FindNode('Data');
  393. if Assigned(FOnSetCustomProps) then
  394. FOnSetCustomProps(Result,aDataNode);
  395. aDataNode := ObjNode.FindNode('Size');
  396. case GetProperty(ObjNode,'Alignment') of
  397. 'taRightJustify': Result.TextAlignment.Horizontal:=taRightJustified;
  398. 'taCenter': Result.TextAlignment.Horizontal:=taCentered;
  399. end;
  400. case GetProperty(ObjNode,'Layout') of
  401. 'tlCenter': Result.TextAlignment.Vertical:=tlCenter;
  402. 'tlTop': Result.TextAlignment.Vertical:=tlTop;
  403. 'tlBottom': Result.TextAlignment.Vertical:=tlBottom;
  404. end;
  405. Result.StretchMode:=smActualHeight;
  406. aFlag := StrToIntDef(GetProperty(ObjNode,'Flags'),0);
  407. if aFlag and 3 = 3 then
  408. Result.StretchMode:=smMaxHeight;
  409. Result.TextAlignment.TopMargin:=1;
  410. aDataNode := ObjNode.FindNode('Data');
  411. Result.Text:=FixDataFields(GetProperty(aDataNode,'Memo'));
  412. Result.UseParentFont := False;
  413. aDataNode := ObjNode.FindNode('Font');
  414. if Assigned(aDataNode) then
  415. aFont:=ConvertFont(aDataNode);
  416. if Assigned(aFont) then
  417. Result.Font.Name:=aFont.PostScriptName
  418. else
  419. Result.UseParentFont := true;
  420. aSize := StrToIntDef(GetProperty(aDataNode,'Size'),Result.Font.Size);
  421. if aSize>5 then
  422. dec(aSize);
  423. Result.Font.Size:=aSize;
  424. aColor := StrToIntDef(GetProperty(aDataNode,'Color'),0);
  425. Result.Font.Color:= RGBToReportColor(Red(aColor),Green(aColor),Blue(aColor));
  426. end;
  427. Function TFPLazReport.ConvertLine(ObjNode : TDOMNode; APage : TFPReportCustomPage) : TFPReportShape;
  428. Var
  429. aDataNode: TDOMNode;
  430. aBand: TFPReportCustomBand;
  431. begin
  432. aDataNode := ObjNode.FindNode('Size');
  433. aBand := FindBand(aPage,PixelsToMM(StrToFloatDef(GetProperty(aDataNode,'Top'),0)));
  434. Result := TFPReportShape.Create(Self);
  435. Result.Parent:=aBand;
  436. Result.ShapeType:=stLine;
  437. Result.Orientation:=orEast;
  438. end;
  439. Function TFPLazReport.ConvertImage(ObjNode : TDOMNode; APage : TFPReportCustomPage) : TFPReportImage;
  440. Var
  441. aDataNode: TDOMNode;
  442. aBand: TFPReportCustomBand;
  443. tmp,e : String;
  444. SS: TStream;
  445. aReaderClass : TFPCustomImageReaderClass;
  446. B : Byte;
  447. I,CD : Integer;
  448. begin
  449. aDataNode := ObjNode.FindNode('Size');
  450. aBand := FindBand(aPage,PixelsToMM(StrToFloatDef(GetProperty(aDataNode,'Top'),0)));
  451. Result := TFPReportImage.Create(aBand);
  452. aDataNode := ObjNode.FindNode('Picture');
  453. aReaderClass:=nil;
  454. tmp:=lowercase(GetProperty(aDataNode,'Type','Ext'));
  455. case tmp of
  456. 'jpeg','jpg': aReaderClass := TFPReaderJPEG;
  457. 'png': aReaderClass := TFPReaderPNG;
  458. 'gif': aReaderClass := TFPReaderGif;
  459. end;
  460. if Not Assigned(aReaderClass) then
  461. begin
  462. DoLog(SErrUnknownImageType,[tmp]);
  463. exit;
  464. end;
  465. tmp:=GetProperty(aDataNode,'Data');
  466. if Tmp='' then
  467. Exit;
  468. ss:=TStringStream.Create('');
  469. try
  470. for i:=1 to (system.length(tmp) div 2) do
  471. begin
  472. e:=tmp[i*2-1]+tmp[i*2];
  473. Val('$'+E, B, cd);
  474. if cd<>0 then
  475. DoLog(SErrWrongEncoding,[i*2-1,E]);
  476. ss.Write(B, 1);
  477. end;
  478. ss.Position:=0;
  479. Result.LoadFromStream(ss,aReaderClass);
  480. Result.Stretched:=True;
  481. Finally
  482. ss.Free;
  483. end;
  484. end;
  485. function TFPLazReport.ConvertBarcode(ObjNode: TDOMNode; APage: TFPReportCustomPage): TFPReportBarcode;
  486. Function StringToEncoding (s : String): TBarcodeEncoding;
  487. begin
  488. Case s of
  489. 'bcCode39' : Result:=be39;
  490. 'bcCode93' : Result:=be93;
  491. 'bcCodeCodabar' : Result:=beCodabar;
  492. 'bcCode39Extended' : Result:=be39Extended;
  493. 'bcCode128A' : Result:=be128A;
  494. 'bcCode128B' : Result:=be128B;
  495. 'bcCode128C' : Result:=be128C;
  496. 'bcCodeEAN13' : Result:=beEAN13;
  497. 'bcCodeEAN8' : Result:=beEAN8;
  498. 'bcCode_2_5_interleaved' : Result:=be2of5interleaved;
  499. 'bcCodeMSI' : Result:=beMSI;
  500. else
  501. DoLog(SUnknownBarcodeType,[s]);
  502. end;
  503. end;
  504. Var
  505. aDataNode : TDomNode;
  506. cd : integer;
  507. D :double;
  508. begin
  509. Result:=TFPReportBarcode.Create(Self);
  510. aDataNode:=ObjNode.FindNode('Size');
  511. Result.Parent:=FindBand(APage,PixelsToMM(StrToFloatDef(GetProperty(aDataNode,'Top'),0)));
  512. Result.Encoding:=StringToEncoding(GetProperty(ObjNode,'BarCode','BarType'));
  513. if GetProperty(ObjNode,'BarCode','Angle')<>'0' then
  514. DoLog(SIgnoringAngleOnBarcode);
  515. if GetProperty(ObjNode,'BarCode','ShowText')<>'0' then
  516. DoLog(SIgnoringShowTextOnBarcode);
  517. val(GetProperty(ObjNode,'BarCode','Zoom'),D,CD);
  518. if CD=0 then
  519. Result.Weight:=D
  520. else
  521. Result.Weight:=1;
  522. aDataNode:=ObjNode.FindNode('Data');
  523. if ADataNode<>Nil then
  524. Result.Expression:=GetProperty(aDataNode,'Memo');
  525. end;
  526. Procedure TFPLazReport.SizeToLayout(aDataNode : TDOMNode; aObj: TFPReportElement);
  527. Var
  528. OffsetTop: TFPReportUnits;
  529. OffsetLeft: TFPReportUnits;
  530. begin
  531. if Assigned(aObj.Band) then
  532. OffsetTop := aObj.Band.Layout.Top
  533. else
  534. OffsetTop := 0;
  535. OffsetLeft :=0;
  536. if not (aObj is TFPReportCustomBand) then
  537. if Assigned(aObj.Page) then
  538. OffsetLeft := aObj.Page.Margins.Left;
  539. With aObj.Layout do
  540. begin
  541. Top:=PixelsToMM(StrToFloatDef(GetProperty(aDataNode,'Top'),Top))-OffsetTop;
  542. Left:=PixelsToMM(StrToFloatDef(GetProperty(aDataNode,'Left'),Left))-OffsetLeft;
  543. Width:=PixelsToMM(StrToFloatDef(GetProperty(aDataNode,'Width'),Width));
  544. Height:=PixelsToMM(StrToFloatDef(GetProperty(aDataNode,'Height'),Height));
  545. end;
  546. end;
  547. function TFPLazReport.ConvertComponentName(const aName: UnicodeString; const AClassName: String): String;
  548. begin
  549. if IsValidIdent(aName) then
  550. Result:=aName
  551. else
  552. begin
  553. Repeat
  554. Inc(FCounter);
  555. Result:=aClassName+IntToStr(FCounter);
  556. Until FindComponent(Result)=Nil;
  557. if Assigned(FOnConvertName) then
  558. FOnConvertName(Self,aName,Result);
  559. DoLog(SWarnConvertName,[aName,Result]);
  560. end;
  561. end;
  562. Function TFPLazReport.ApplyFrame(aDataNode : TDOMNode; aFrame: TFPReportFrame) : Boolean;
  563. Var
  564. tmp : String;
  565. aColor : Integer;
  566. begin
  567. Result:=False;
  568. aFrame.Shape:=fsNone;
  569. if GetProperty(aDataNode,'FrameColor')<>'' then
  570. begin
  571. aColor := StrToIntDef(GetProperty(aDataNode,'FrameColor'),0);
  572. aFrame.Color:= RGBToReportColor(Red(aColor),Green(aColor),Blue(aColor));
  573. end;
  574. aFrame.Width := Round(StrToIntDef(GetProperty(aDataNode,'FrameWidth'),0)/2);
  575. aFrame.Lines:=[];
  576. tmp := GetProperty(aDataNode,'FrameBorders');
  577. if tmp = '' then
  578. exit;
  579. if pos('frbBottom',tmp)>0 then
  580. aFrame.Lines := aFrame.Lines+[flBottom];
  581. if pos('frbTop',tmp)>0 then
  582. aFrame.Lines := aFrame.Lines+[flTop];
  583. if pos('frbLeft',tmp)>0 then
  584. aFrame.Lines := aFrame.Lines+[flLeft];
  585. if pos('frbRight',tmp)>0 then
  586. aFrame.Lines := aFrame.Lines+[flRight];
  587. Result:=aFrame.Lines<>[];
  588. end;
  589. Procedure TFPLazReport.ApplyObjectProperties(ObjNode : TDOMNode; aObj: TFPReportElement);
  590. Var
  591. HasFrame : Boolean;
  592. FC: String;
  593. M : TFPReportMemo;
  594. aDataNode : TDOMNode;
  595. aColor : Integer;
  596. begin
  597. aObj.Name:=ConvertComponentName(GetProperty(ObjNode,'Name'),aObj.ClassName);
  598. aDataNode := ObjNode.FindNode('Size');
  599. if Assigned(aDataNode) then
  600. SizeToLayout(aDataNode,aObj);
  601. HasFrame:=False;
  602. aDataNode := ObjNode.FindNode('Frames');
  603. if Assigned(aDataNode) then
  604. hasFrame:=ApplyFrame(aDataNode,aObj.Frame);
  605. if Not (aObj is TFPReportMemo) then
  606. exit;
  607. FC:=GetProperty(ObjNode,'FillColor');
  608. if (FC='clNone') or (FC='') then
  609. exit;
  610. M:=TFPReportMemo(aObj);
  611. aColor := StrToIntDef(FC,0);
  612. M.Frame.Pen:=psClear;
  613. M.Frame.BackgroundColor:= RGBToReportColor(Red(aColor),Green(aColor),Blue(aColor));
  614. M.Frame.Shape:=fsRectangle;
  615. if not HasFrame then
  616. begin
  617. M.Frame.Color:=RGBToReportColor(Red(aColor),Green(aColor),Blue(aColor));
  618. M.Frame.Pen:=psClear;
  619. end;
  620. end;
  621. procedure TFPLazReport.ConvertPageProperties(aPage: TFPReportPage; aPageNode: TDOMNode);
  622. Var
  623. aDataNode: TDOMNode;
  624. begin
  625. aPage.PageSize.PaperName:='A4';
  626. aPage.Font.Name:='ArialMT';
  627. if GetProperty(aPageNode,'Width')<>'' then
  628. aPage.PageSize.Width := round(PageToMM(StrToFloatDef(GetProperty(aPageNode,'Width'),aPage.PageSize.Width)));
  629. if GetProperty(aPageNode,'Height')<>'' then
  630. aPage.PageSize.Height := round(PageToMM(StrToFloatDef(GetProperty(aPageNode,'Height'),aPage.PageSize.Width)));
  631. if GetProperty(aPageNode,'Orientation') = 'poLandscape' then
  632. aPage.Orientation:=poLandscape;
  633. aDataNode := aPageNode.FindNode('Margins');
  634. if Assigned(aDataNode) then
  635. begin
  636. aPage.Margins.Top:=PixelsToMM(StrToFloatDef(GetProperty(aDataNode,'Top'),aPage.Margins.Top));
  637. aPage.Margins.Left:=PixelsToMM(StrToFloatDef(GetProperty(aDataNode,'left'),aPage.Margins.Left));
  638. aPage.Margins.Right:=PixelsToMM(StrToFloatDef(GetProperty(aDataNode,'Right'),aPage.Margins.Right));
  639. aPage.Margins.Bottom:=PixelsToMM(StrToFloatDef(GetProperty(aDataNode,'Bottom'),aPage.Margins.Bottom));
  640. end;
  641. end;
  642. Function TFPLazReport.ConvertPage(aPageNode : TDOMNode) : TFPReportPage;
  643. var
  644. aPage: TFPReportPage;
  645. ObjNode : TDOMNode;
  646. aObj: TFPReportElement;
  647. J : Integer;
  648. NodeName,CT : String;
  649. begin
  650. FMasterData := nil;
  651. FDetailBand := nil;
  652. FDetailHeader := nil;
  653. FDetailFooter := nil;
  654. aPage := TFPReportPage.Create(Self);
  655. Result:=aPage;
  656. ConvertPageProperties(aPage,aPageNode);
  657. for j := 0 to aPageNode.ChildNodes.Count-1 do
  658. begin
  659. ObjNode:=aPageNode.ChildNodes.Item[j];
  660. NodeName:=ObjNode.NodeName;
  661. if (copy(NodeName,0,6)='Object') and (NodeName<>'ObjectCount') then
  662. begin
  663. CT:=GetProperty(ObjNode,'ClassName');
  664. case CT of
  665. 'TfrBandView':
  666. aObj:=ConvertBand(ObjNode,aPage);
  667. 'TfrMemoView':
  668. aObj:=ConvertMemo(ObjNode,aPage);
  669. 'TfrLineView':
  670. aObj:=ConvertLine(ObjNode,aPage);
  671. 'TfrPictureView':
  672. aObj:=ConvertImage(ObjNode,aPage);
  673. 'TfrBarCodeView':
  674. aObj:=ConvertBarcode(ObjNode,aPage);
  675. else
  676. DoLog(SLogUnknownClass,[NodeName,CT]);
  677. aObj:=Nil;
  678. end;
  679. if Assigned(aObj) then
  680. ApplyObjectProperties(ObjNode,aObj);
  681. end;
  682. end;
  683. end;
  684. procedure TFPLazReport.LoadFromFile(const aFileName: String);
  685. var
  686. LazReport: TXMLDocument;
  687. begin
  688. ReadXMLFile(LazReport, aFileName);
  689. try
  690. LoadFromXML(LazReport);
  691. finally
  692. LazReport.Free;
  693. end;
  694. end;
  695. end.