home *** CD-ROM | disk | FTP | other *** search
/ Mastering Visual Basic 5 / MasteringVisualBasic5.iso / olympus / ik32_15t / delphi2.shr / UPRNTPVW.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-08-01  |  18.9 KB  |  474 lines

  1. {'Printer  Preview' .Pas Module.
  2.  
  3. CopyRight Belmont Imaging 1996.
  4.  
  5. Demo Program by :- Andrew Hutchison 100022,1047@Compuserve.com
  6.  
  7. All rights reserved.  No part of this program may be photocopied, reproduced,
  8. translated to another programming  language or transported to any computer system
  9. without the prior written consent of Belmont Imaging.
  10.  
  11. This Demo is provided for use by Media Architects in there Delphi Demo's only.
  12. The unit must not be re-used without the permission of Media Architects or Belmont
  13. Imaging. Copyright Belmont Imaging 1996. It is supplied AS-IS. Use at your
  14. own risk.}
  15.  
  16.  
  17. unit UPrntPvw;
  18.  
  19. interface
  20.  
  21. uses
  22.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  23.   ExtCtrls, OleCtrls, ImageKnife32, Printers, Buttons, StdCtrls, ComCtrls,
  24.   Spin;
  25.  
  26. type
  27.   TTE_PrintPreview = class(TForm)
  28.     TE_PP_Paper_Boundary: TPanel;
  29.     TE_PP_PPrint: TPrintDialog;
  30.     TE_PP_PSetup: TPrinterSetupDialog;
  31.     TE_PP_Paper: TPanel;
  32.     TE_PP_PreviewImage: TPicbuf;
  33.     TE_PP_Location_Group: TGroupBox;
  34.     TE_PP_Label_2: TLabel;
  35.     TE_PP_Label_1: TLabel;
  36.     TE_PP_Label_3: TLabel;
  37.     TE_PP_Label_4: TLabel;
  38.     TE_PP_Label_ImageTop: TLabel;
  39.     TE_PP_Label_ImageLeft: TLabel;
  40.     TE_PP_Label_ImageWidth: TLabel;
  41.     TE_PP_Label_ImageHeight: TLabel;
  42.     TE_PP_Print_Group: TGroupBox;
  43.     TE_PP_Print_Icon: TSpeedButton;
  44.     TE_PP_PrintSetup_Icon: TSpeedButton;
  45.     TE_PP_Copies: TSpinEdit;
  46.     Label1: TLabel;
  47.     TE_PP_Header: TLabel;
  48.     TE_PP_Footer: TLabel;
  49.     TE_PP_Caption: TLabel;
  50.     TE_PP_Captions: TGroupBox;
  51.     TE_PP_HeaderText: TEdit;
  52.     TE_PP_CaptionText: TEdit;
  53.     TE_PP_FooterText: TEdit;
  54.     TE_PP_HeaderText_Check: TCheckBox;
  55.     TE_PP_CaptionText_Check: TCheckBox;
  56.     TE_PP_FooterText_Check: TCheckBox;
  57.     TE_PP_QuickPrint_Icon: TSpeedButton;
  58.     Label2: TLabel;
  59.     Label3: TLabel;
  60.     Label4: TLabel;
  61.     TE_PP_Font: TFontDialog;
  62.     TE_PP_PrinterFont_Icon: TSpeedButton;
  63.     TE_PP_Reset: TSpeedButton;
  64.     LoadButton: TSpeedButton;
  65.     procedure FormCreate(Sender: TObject);
  66.     procedure FormActivate(Sender: TObject);
  67.     procedure TE_PP_PreviewImageMouseDown(Sender: TObject;
  68.       Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  69.     procedure TE_PP_PreviewImageMouseMove(Sender: TObject;
  70.       Shift: TShiftState; X, Y: Integer);
  71.     procedure TE_PP_PrintSetup_IconClick(Sender: TObject);
  72.     procedure TE_PP_Print_IconClick(Sender: TObject);
  73.     procedure TE_PP_Caption_Down(Sender: TObject; Button: TMouseButton;
  74.       Shift: TShiftState; X, Y: Integer);
  75.     procedure TE_PP_Caption_Move(Sender: TObject; Shift: TShiftState; X,
  76.       Y: Integer);
  77.     procedure TE_PP_QuickPrint_IconClick(Sender: TObject);
  78.     procedure FormClose(Sender: TObject; var Action: TCloseAction);
  79.     procedure TE_PP_PrinterFont_IconClick(Sender: TObject);
  80.     procedure TE_PP_ResetClick(Sender: TObject);
  81.     procedure LoadButtonClick(Sender: TObject);
  82.   private
  83.   {Private declarations }
  84.  
  85.   {Make Sure Paper View Stays within Bounds of Form}
  86.   procedure CheckandScale;
  87.  
  88.   {Update Print Location Labels}
  89.   procedure UpdateLabels;
  90.  
  91.   {Print Captions}
  92.   procedure PrintCaptions;
  93.  
  94.   public
  95.   {Public declarations}
  96.  
  97.   {Used to Hold Global Print Location Values}
  98.   PrintImageHeight,PrintImageWidth,PrintImageLeft,PrintImageTop:Integer;
  99.   end;
  100.  
  101. var
  102. TE_PrintPreview: TTE_PrintPreview;
  103.  
  104. {Variables - This Unit}
  105. PrinterXres,PrinterYres,PrinterPageX,PrinterPageY,OffsetMouseX,OffsetMouseY,
  106. MouseDragDownX,MouseDragDownY,ExceedHeight,ExceedWidth:Integer;
  107. RatioXY,CorrectionRatio:Real;
  108.  
  109. implementation
  110.  
  111. uses UPBDISK;
  112. {$R *.DFM}
  113.  
  114. {--------------------------------------------------------------------------------}
  115. {Creation - Set areas and sizes in case someone does not use the Preview Screen,
  116. in effect these are the 'Design Time Defaults'}
  117. procedure TTE_PrintPreview.FormCreate(Sender: TObject);
  118. begin
  119. Application.HintPAuse:=10;
  120. Application.HintColor:=clAqua;
  121. {Do an update by forcing an form 'Activate Event' - See Below}
  122. TE_PrintPreview.FormActivate(Sender);
  123. end;
  124.  
  125. {--------------------------------------------------------------------------------}
  126. {Set Paper Area and Size to Reflect Printer Paper Size. When the Activate event
  127. is called use API calls to get the current settings for the installed default
  128. Printers. Store the results in global variables. Note the division by 25.4 this
  129. is for conversion from inches to metric mm's. All Paper View are updated within
  130. the 'CheckandScale' procedure - See Below}
  131. procedure TTE_PrintPreview.FormActivate(Sender: TObject);
  132. begin
  133. PrinterXres:= round((GetDeviceCaps(Printer.Handle,LOGPIXELSX))/25.4);       {DPI}
  134. PrinterYres:= round((GetDeviceCaps(Printer.Handle,LOGPIXELSY))/25.4);       {DPI}
  135. PrinterPageX:=GetDeviceCaps(Printer.Handle,HORZSIZE);                {Paper Size}
  136. PrinterPageY:=GetDeviceCaps(Printer.Handle,VERTSIZE);                {Paper Size}
  137. CheckandScale;{See Below - Size 'Preview Paper' to printer paper size}
  138. end;
  139.  
  140. {--------------------------------------------------------------------------------}
  141. {Make Sure Preview Paper View Stays within form Bounds - Set Preview Paper
  142. view to size and location.  Refering to the main form the 'paper area' is named
  143. 'TE_PP_Paper', the blue background area is called 'TE_PP_Paper_Boundary'. Basically
  144. waht the following procedure does is to size the 'Paper area' to reflect the size
  145. of the installed printers page.  The API calls above return the Printers Page
  146. Width - X and Height - Y in Pixels.  All that is required is to set the TE_PP-Paper
  147. width and Height to the same dimensions - no conversion is required since Delphi
  148. forms are sized in pixel units as well.  The reason for the additional code below
  149. is because we wish to limit the size of the 'Preview Screen'.  If we did not do this
  150. then printers supporting large paper sizes, would cause the preview window to fill
  151. your monitor. This is the reason for having the TE_PP_Paper_Boundary area. What we
  152. do is check to see if the sized preview paper exceeds this blue area, if it does
  153. we apply a scale factor to the Paper Size so it does not exceed the Blue area. In
  154. reality you will find the only paper sizes above A3 will require scaleing. Sizes
  155. less than this will fit the area.}
  156. procedure TTE_PrintPreview.CheckandScale;
  157. Var
  158. HeightDifference,WidthDifference:Integer;
  159. begin
  160.  
  161. {With/Height - No correction required -  default  - papaer smaller than boundary}
  162. TE_PP_Paper.Height := PrinterPageY;        {Set Paper Area to PrinterPageY - API}
  163. TE_PP_Paper.Width  := PrinterPageX ;       {Set Paper Area to PrinterPageY - API}
  164. CorrectionRatio := 1;{No Correction required so set global ratio to 1}
  165.  
  166. {Find Out if the Preview Paper needs shrunk to fit blue boundary area - height}
  167. If PrinterPageY > TE_PP_Paper_Boundary.Height then  {Printer does exceed boundary}
  168. HeightDifference:= PrinterPageY - TE_PP_Paper_Boundary.Height   {Store the Excess}
  169. else
  170. HeightDifference:= 0;                            {As Defualt no Height Difference}
  171.  
  172. {Find Out if the Preview Paper needs shrunk to fit blue boundary area - Width}
  173. If PrinterPageX > TE_PP_Paper_Boundary.Width then  {Printer does exceed boundary}
  174. WidthDifference := PrinterPageX - TE_PP_Paper_Boundary.Width
  175. else
  176. WidthDifference := 0;                            {As Defualt no width Difference}
  177.  
  178. {Scale Preview Image depending on which value above is the Largest - use which
  179. ever is the larger of the above to scale the 'paper' area}
  180.  
  181. {Height the largest}
  182. If HeightDifference > WidthDifference then
  183. begin
  184. {How Much does the Printer Excced the Height}
  185. ExceedHeight := PrinterPageY - TE_PP_Paper_Boundary.Height;
  186. {Set the Paper Height to the Max Possible ie boundary height}
  187. TE_PP_Paper.Height := TE_PP_Paper_Boundary.Height;
  188. {Scale the Width to the same RATIO as the new Height/Actual Height}
  189. TE_PP_Paper.Width :=  TE_PP_Paper_Boundary.Width - ((ExceedHeight * TE_PP_Paper.Height) div
  190. TE_PP_Paper.Width);
  191. {Store Scale Ratio so we can correctly print the Image}
  192. CorrectionRatio:= PrinterPageY / TE_PP_Paper.Height;
  193. end;
  194.  
  195. {As above}
  196. {Width the largest}
  197. If WidthDifference > HeightDifference then
  198. begin
  199. ExceedWidth := PrinterPageX - TE_PP_Paper_Boundary.Width;
  200. TE_PP_Paper.Width := TE_PP_Paper_Boundary.Width;
  201. TE_PP_Paper.Height :=  TE_PP_Paper.Height - ((ExceedWidth * TE_PP_Paper.Height) div
  202. TE_PP_Paper.Width);
  203. {Store Scale Ratio}
  204. CorrectionRatio:= PrinterPageX / TE_PP_Paper.Width;
  205. end;
  206.  
  207. {Centre Paper View to Resulting Size and Location. All we do here is to make sure
  208. the Paper Area appears at the middle of the blue boundary area.}
  209. TE_PP_Paper.Top:=(TE_PP_Paper_Boundary.Height div 2) - (TE_PP_Paper.Height div 2);
  210. TE_PP_Paper.Left:=(TE_PP_Paper_Boundary.Width div 2) - (TE_PP_Paper.Width div 2);
  211.  
  212. {Set Print Location Globals and Apply to 'Picbuf Image' - TE_PP_PreviewImage is the
  213. name of the Picbuf Control - it is this Image that is printed on the paper. Note:-
  214. you are setting the Globals to the Size and Location of the Picbuf Image.  Since
  215. all units are in Pixels then all that is required is to use the Picbufs Top, Left
  216. Width, and Height Properties. Notice how we apply the Correction Ratio then Multipy
  217. the result by the Printers DPI value.  This results in the correct Image Size and
  218. Location when printed.}
  219. with TE_PP_PreviewImage do{Reference the Picbuf}
  220. begin{Start With}
  221. PrintImageHeight := round((Height * CorrectionRatio) * PrinterYres);
  222. PrintImageWidth := round((Width * CorrectionRatio)* PrinterXres);
  223. PrintImageLeft := round ((Left * CorrectionRatio) * PrinterXres);  { ADD CORRECTION FACTOR }
  224. PrintImageTop :=  round ((Top  * CorrectionRatio)* PrinterYres);  { ADD CORRECTION FACTOR }
  225. end;{End With}
  226.  
  227. {Update Labels}
  228. UpdateLabels;{See Below - Basically this gets the Location of the Labels and
  229. correctly locates them on the finished page}
  230. end;
  231.  
  232. {--------------------------------------------------------------------------------}
  233. {Get Mouse Down Locations - Used for Moving the Picture around the Screen}
  234. procedure TTE_PrintPreview.TE_PP_PreviewImageMouseDown(Sender: TObject;
  235.   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  236. begin
  237. with sender as TPicbuf do
  238. begin
  239. OffsetMouseX := (Width - X);{Calculates the X/Y location relative to Objects 0,0}
  240. OffsetMouseY := (Height - Y);
  241. RatioXY := (Height / Width);                        {Used so user can Size Image}
  242. MouseDragDownX := X;                             {Store X for MouseDown location}
  243. MouseDragDownY := Y;                             {Store Y for MouseDown location}
  244. end;
  245. end;
  246.  
  247. {--------------------------------------------------------------------------------}
  248. {Move and Size Image}
  249. procedure TTE_PrintPreview.TE_PP_PreviewImageMouseMove(Sender: TObject;
  250.   Shift: TShiftState; X, Y: Integer);
  251. begin
  252. With Sender as TPicbuf do                                      {Reference Picbuf}
  253. begin
  254. if ssLeft in Shift then                                 {Left button moves Image}
  255. begin {End Left Loop}
  256. Top := Top - (MouseDragDownY - Y);
  257. Left:= Left -(MouseDragDownX - X);
  258. end;  {End Left Loop}
  259. if ssRight in Shift then                            {Right and Shift Sizes Image}
  260. begin
  261. if ssShift in Shift then
  262. begin
  263. if (X + (OffsetmouseX)) < 10 then Exit;
  264. if round((Width * RatioXY)) < 10 then exit;
  265. Width := (X + (OffsetmouseX));                                       {Set Height}
  266. Height := round((Width * RatioXY));                    {Set Width based on ratio}
  267. end {End Shift}
  268. else
  269. begin
  270. if (X + (OffsetmouseX)) < 10 then Exit;
  271. if (Y + (OffsetMouseY)) < 10 then Exit;
  272. Width := (X + (OffsetmouseX));                                       {Set Height}
  273. Height:= (Y + (OffsetmouseY));                         {Set Width - no Set Ratio}
  274. end;
  275. end;
  276. {Update Labels}
  277. UpdateLabels;                                            {Update Location Labels}
  278. end;
  279. end;
  280.  
  281. {--------------------------------------------------------------------------------}
  282. {Update Print Location Labels - Just use the Labels Location * Correction Ration}
  283. procedure TTE_PrintPreview.UpdateLabels;
  284. begin
  285. with TE_PrintPreview.TE_PP_PreviewImage do                     {Reference Picbuf}
  286. begin
  287. TE_PrintPreview.TE_PP_Label_ImageHeight.Caption := inttostr(round((Height * CorrectionRatio)));
  288. TE_PrintPreview.TE_PP_Label_ImageWidth.Caption := inttostr(round((Width * CorrectionRatio)));
  289. TE_PrintPreview.TE_PP_Label_ImageTop.Caption := inttostr(round((Top * CorrectionRatio)));
  290. TE_PrintPreview.TE_PP_Label_ImageLeft.Caption := inttostr(round((Left * CorrectionRatio)));
  291. end;
  292. end;
  293.  
  294. {--------------------------------------------------------------------------------}
  295. {Print Options - Display 'Printer Setup' Common Dialog Control}
  296. procedure TTE_PrintPreview.TE_PP_PrintSetup_IconClick(Sender: TObject);
  297. begin
  298. TE_PP_PSetup.execute;
  299. {After it has executed, force Paper Display to update itself - in case a new Printer
  300. has been picked by the user}
  301. TE_PrintPreview.FormActivate(Sender);                    {Recall Activate method}
  302. end;
  303.  
  304. {--------------------------------------------------------------------------------}
  305. {Print Image - This does the Printing}
  306. procedure TTE_PrintPreview.TE_PP_Print_IconClick(Sender: TObject);
  307. Var
  308. CopyCount:Integer;
  309. begin
  310. if TE_PP_PreviewImage.contents <> 1 then exit; {No Image Exit}
  311.  
  312. {Update user copies - Update Copies number in dialog control}
  313. TE_PP_PPrint.Copies := TE_PP_Copies.Value;
  314.  
  315. {User Select Print ie 'OK'}
  316. if TE_PP_PPrint.Execute then
  317. begin
  318. Application.ProcessMessages;
  319.  
  320. {Update Copy Display on main form to Dialog Copy Count}
  321. TE_PP_Copies.Value := TE_PP_PPrint.Copies;
  322.  
  323. {Update View Screen in case user altered anything}
  324. TE_PrintPreview.FormActivate(Sender);
  325.  
  326. {Set Locations - use our derived location and set Picbuf's properties to them}
  327. TE_PP_PreviewImage.PrintHeight:=PrintImageHeight;
  328. TE_PP_PreviewImage.PrintWidth:=PrintImageWidth;
  329. TE_PP_PreviewImage.PrintLeft:=PrintImageLeft;
  330. TE_PP_PreviewImage.PrintTop:=PrintImageTop;
  331. Application.ProcessMessages;
  332. {Set up a loop equal to the number of copies required}
  333. for CopyCount := 1 to TE_PP_Copies.Value do                      {Edit Box Value}
  334. begin
  335. {Start Process}
  336. Printer.BeginDoc;
  337. {Set Caption Locations and Print}
  338. PrintCaptions;{See Below - Set Labels to location and then print text}
  339. TE_PP_PreviewImage.PrinterHDC:=Printer.Handle;                         {Do Print}
  340. Printer.Enddoc;                                                       {End Print}
  341. end;
  342. end;
  343. end;
  344.  
  345. {--------------------------------------------------------------------------------}
  346. {Print Captions - Header / Caption / Footer. Same rules as above. Get labels
  347. locations, adjust to Ratio if required, then output text to printer.}
  348. procedure TTE_PrintPreview.PrintCaptions;
  349. Var
  350. TextX,TextY : integer;
  351. begin
  352. {Set Printer Font}
  353. Printer.Canvas.Font:= TE_PP_Font.Font;    {Get Font from Common Dialogue Control}
  354. {Header Label}
  355. if TE_PP_HeaderText_Check.Checked then
  356. begin
  357. TextX:= trunc(((TE_PP_Header.Left * CorrectionRatio) * PrinterXres)) ;
  358. TextY:= trunc(((TE_PP_Header.Top  * CorrectionRatio) * PrinterYres));
  359. Printer.Canvas.TextOut(TextX, TextY, TE_PP_HeaderText.Text);         {Print Text}
  360. end;
  361. {Caption}
  362. if TE_PP_CaptionText_Check.Checked then
  363. begin
  364. TextX:= trunc (((TE_PP_Caption.Left * CorrectionRatio) * PrinterXres)) ;
  365. TextY:= trunc(((TE_PP_Caption.Top * CorrectionRatio) *  PrinterYres)) ;
  366. Printer.Canvas.TextOut(TextX, TextY, TE_PP_CaptionText.Text);        {Print Text}
  367. end;
  368. {Footer}
  369. if TE_PP_FooterText_Check.Checked then
  370. begin
  371. TextX:= trunc (((TE_PP_Footer.Left * CorrectionRatio) * PrinterXres)) ;
  372. TextY:= trunc(((TE_PP_Footer.Top * CorrectionRatio) * PrinterYres)) ;
  373. Printer.Canvas.TextOut(TextX, TextY,TE_PP_FooterText.Text);          {Print Text}
  374. end;
  375. end;
  376.  
  377.  
  378. {--------------------------------------------------------------------------------}
  379. {Caption Moves - One event does Header,Footer and Caption. Tie all label events to
  380. one procedure}
  381. procedure TTE_PrintPreview.TE_PP_Caption_Down(Sender: TObject;
  382. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  383. begin
  384. with Sender as TLabel do
  385. begin
  386. MouseDragDownX := X;
  387. MouseDragDownY := Y;
  388. end;
  389. end;
  390.  
  391. {--------------------------------------------------------------------------------}
  392. {Caption Moves - One event does Header,Footer and Caption}
  393. procedure TTE_PrintPreview.TE_PP_Caption_Move(Sender: TObject;
  394. Shift: TShiftState; X, Y: Integer);
  395. begin
  396. With Sender as TLabel do                                {Reference Sending Label}
  397. begin
  398. if ssLeft in Shift then
  399. begin {End Left Loop}
  400. Top := Top - (MouseDragDownY - Y);
  401. Left:= Left -(MouseDragDownX - X);
  402. end;  {End Left Loop}
  403. end;
  404. end;
  405.  
  406. {--------------------------------------------------------------------------------}
  407. {Quick Print - 1 Copy. Simply calls activate to update screen in case of any
  408. alterations, then prints single copy, with default printer [last] settings}
  409. procedure TTE_PrintPreview.TE_PP_QuickPrint_IconClick(Sender: TObject);
  410. begin
  411. {Update Screen In case user altered anything}
  412. TE_PrintPreview.FormActivate(Sender);
  413. {Set Locations}
  414. TE_PP_PreviewImage.PrintHeight:=PrintImageHeight;
  415. TE_PP_PreviewImage.PrintWidth:=PrintImageWidth;
  416. TE_PP_PreviewImage.PrintLeft:=PrintImageLeft;
  417. TE_PP_PreviewImage.PrintTop:=PrintImageTop;
  418. Printer.BeginDoc;
  419. {Set Caption Locations and Print}
  420. PrintCaptions;{See Above}
  421. TE_PP_PreviewImage.PrinterHDC:=Printer.Handle;
  422. Printer.Enddoc;
  423. end;
  424.  
  425. {--------------------------------------------------------------------------------}
  426. {Empty Preview Image on Exit}
  427. procedure TTE_PrintPreview.FormClose(Sender: TObject;
  428.   var Action: TCloseAction);
  429. begin
  430. TE_PP_PreviewImage.Clear;
  431. end;
  432.  
  433. {--------------------------------------------------------------------------------}
  434. {Set Printer Font}
  435. procedure TTE_PrintPreview.TE_PP_PrinterFont_IconClick(Sender: TObject);
  436. begin
  437. TE_PP_Font.Execute;                               {Show font common dialogue box}
  438. end;
  439.  
  440. {--------------------------------------------------------------------------------}
  441. {Home Labels and Refresh - This simply aligns the object on the preview page to
  442. the upper left hand area - stops the controls getting lost if you swap between
  443. paper sizes}
  444. procedure TTE_PrintPreview.TE_PP_ResetClick(Sender: TObject);
  445. begin
  446. TE_PP_Header.Top := 15;
  447. TE_PP_Header.Left := 15;
  448. TE_PP_Caption.Top := 30;
  449. TE_PP_Caption.Left:= 15;
  450. TE_PP_PreviewImage.Top := 50;
  451. TE_PP_PreviewImage.LEft := 15;
  452. TE_PP_Footer.Top := 145;
  453. TE_PP_Footer.Left := 15;
  454. {Update Screen in case user altered anything}
  455. TE_PrintPreview.FormActivate(Sender);
  456. end;
  457.  
  458.  
  459. {-------------------------------------------------------------------------------}
  460. {For MAI Demo only allow user to load Image into Print Preview from Disk. Use
  461. Custom I/O 'BI' Disk Control. Note:- You call the I/O dialog with a 'ShowModal'.}
  462. procedure TTE_PrintPreview.LoadButtonClick(Sender: TObject);
  463. begin
  464. {Uses Hybrid I/O Control called PB_DiskControl32V1}
  465. If PB_DiskControl32V1.ShowModal = MrOK then
  466. begin
  467. Application.Processmessages;                                           {Catch Up}
  468. TE_PP_PreviewImage.FileName:=PB_DiskControl32V1.FileName;              {Set Name}
  469. TE_PP_PreviewImage.Load;                                             {Load Image}
  470. end;
  471. end;
  472.  
  473. end.
  474.