home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 9 / 09.iso / l / l040 / 13.ddi / RTLWIN.ZIP / WINPRN.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-10-28  |  14.7 KB  |  532 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Turbo Pascal Runtime Library                    }
  5. {       Windows Simplified Printer Interface Unit       }
  6. {                                                       }
  7. {       Copyright (c) 1991,92 Borland International     }
  8. {                                                       }
  9. {*******************************************************}
  10.  
  11. unit WinPrn;
  12.  
  13. {$S-}
  14.  
  15. interface
  16.  
  17. uses WinTypes;
  18.  
  19. { AbortPrn will cause all unprinted portions of the writes to the  }
  20. { file to be thrown away. Note: the file still must be closed.     }
  21.  
  22. procedure AbortPrn(var F: Text);
  23.  
  24. { AssignPrn assigns a file to a printer.  The Device, Driver, and  }
  25. { Port can be retrieved from the WIN.INI file's [device] section   }
  26. { or from the [windows] sections 'device' string. If Device is nil }
  27. { the default printer is used.                                     }
  28.  
  29. procedure AssignPrn(var F: Text; Device, Driver, Port: PChar);
  30.  
  31. { AssignDefPrn calls AssignPrn with Device equal to nil.           }
  32.  
  33. procedure AssignDefPrn(var F: Text);
  34.  
  35. { SetPrnFont will cause the file to begin printing using the given }
  36. { font.  The old font is returned.                                 }
  37.  
  38. function SetPrnFont(var F: Text; Font: HFont): HFont;
  39.  
  40. { TitlePrn will give a title to the file being printed which is    }
  41. { displayed by Window's Print Manager.  For this routine to have   }
  42. { effect it needs to be called before ReWrite.                     }
  43.  
  44. procedure TitlePrn(var F: Text; Title: PChar);
  45.  
  46. { ProcessPrnMessage is called whenever a message is received by    }
  47. { WinPrn's abort procedure.  If the function returns false, the    }
  48. { message is translated and dispatched, otherwise it is ignored.   }
  49. { Use this variable if you wish modeless dialogs to continue to    }
  50. { operate while printing.  (Note: Since ObjectWindow automatically }
  51. { initializes this variable for KBHandler's, no special action is  }
  52. { necessary when using ObjectWindows).                             }
  53.  
  54. var
  55.   ProcessPrnMessage: function (var Msg: TMsg): Boolean;
  56.  
  57. implementation
  58.  
  59. uses
  60.   WinProcs, WinDos, Strings;
  61.  
  62. { ---------------------------------------------------------------- }
  63. { Internal helper routines --------------------------------------- }
  64. { ---------------------------------------------------------------- }
  65.  
  66. const
  67.   wpInvalidDevice  = 230;
  68.   wpTooManyAborted = 231;
  69.   wpPrintingError  = 232;
  70.  
  71. { Printer abort manager ------------------------------------------ }
  72.  
  73. var
  74.   AbortList: array[1..8] of HDC; { List of aborted printings }
  75. const
  76.   AbortListLen: Byte = 0;
  77.  
  78. { Abort ---------------------------------------------------------- }
  79. {  Add the given DC to the abort list                              }
  80.  
  81. procedure Abort(DC: HDC);
  82. begin
  83.   if AbortListLen < SizeOf(AbortList) then
  84.   begin
  85.     Inc(AbortListLen);
  86.     AbortList[AbortListLen] := DC;
  87.   end
  88.   else
  89.     InOutRes := wpTooManyAborted;
  90. end;
  91.  
  92. { UnAbort -------------------------------------------------------- }
  93. {  Remove a DC value from the abort list. If not in the list       }
  94. {  ignore it.                                                      }
  95.  
  96. procedure UnAbort(DC: HDC);
  97. var
  98.   I: Byte;
  99. begin
  100.   for I := 1 to AbortListLen do
  101.     if DC = AbortList[I] then
  102.     begin
  103.       if AbortListLen <> I then
  104.         Move(AbortList[I], AbortList[I + 1], AbortListLen - I - 1);
  105.       Dec(AbortListLen);
  106.       Exit;
  107.     end;
  108. end;
  109.  
  110. { IsAbort -------------------------------------------------------- }
  111. {  Is the given DC in the abort list?                              }                               
  112.  
  113. function IsAborted(DC: HDC): Bool;
  114. var
  115.   I: Byte;
  116. begin
  117.   for I := 1 to AbortListLen do
  118.     if DC = AbortList[I] then
  119.     begin
  120.       IsAborted := True;
  121.       Exit;
  122.     end;
  123.   IsAborted := False;
  124. end;
  125.  
  126. { PrnRec --------------------------------------------------------- }
  127. {  Printer data record                                             }
  128.  
  129. type
  130.   PrnRec = record
  131.     DC: HDC;                    { Printer device context }
  132.     case Integer of
  133.       0: (
  134.         Title: PChar);          { Title of the printout }
  135.       1: (
  136.         Cur: TPoint;            { Next position to write text }
  137.         Finish: TPoint;         { End of the pritable area }
  138.         Height: Word;           { Height of the current line }
  139.         Status: Word);          { Error status of the printer }
  140.       2: (
  141.         Tmp: array[1..14] of Char);
  142.   end;
  143.  
  144. { NewPage -------------------------------------------------------- }
  145. {  Start a new page.                                               }
  146.  
  147. procedure NewPage(var Prn: PrnRec);
  148. begin
  149.   with Prn do
  150.   begin
  151.     LongInt(Cur) := 0;
  152.     if not IsAborted(DC) and
  153.         (Escape(DC, NewFrame, 0, nil, nil) <= 0) then
  154.       Status := wpPrintingError;
  155.   end;
  156. end;
  157.  
  158. { NewLine -------------------------------------------------------- }
  159. {  Start a new line on the current page, if no more lines left     }
  160. {  start a new page.                                               }
  161.  
  162. procedure NewLine(var Prn: PrnRec);
  163.  
  164.   function CharHeight: Word;
  165.   var
  166.     Metrics: TTextMetric;
  167.   begin
  168.     GetTextMetrics(Prn.DC, Metrics);
  169.     CharHeight := Metrics.tmHeight;
  170.   end;
  171.  
  172. begin
  173.   with Prn do
  174.   begin
  175.     Cur.X := 0;
  176.  
  177.     if Height = 0 then
  178.       { two new lines in a row, use the current character height }
  179.       Inc(Cur.Y, CharHeight)
  180.     else
  181.       { Advance the height of the tallest font }
  182.       Inc(Cur.Y, Height);
  183.     if Cur.Y > (Finish.Y - (Height * 2)) then NewPage(Prn);
  184.     Height := 0;
  185.   end;
  186. end;
  187.  
  188. { PrnOutStr ------------------------------------------------------ }
  189. {  Print a string to the printer without regard to special         }
  190. {  characters.  These should handled by the caller.                }
  191.  
  192. procedure PrnOutStr(var Prn: PrnRec; Text: PChar; Len: Integer);
  193. var
  194.   Extent: TPoint;               { Size of the current text }
  195.   L: Integer;                   { Temporary used for printing }
  196. begin
  197.   with Prn do
  198.   begin
  199.     while Len > 0 do
  200.     begin
  201.       L := Len;
  202.       LongInt(Extent) := GetTextExtent(DC, Text, L);
  203.  
  204.       { Wrap the text to the line }
  205.       while (L > 0) and (Extent.X + Cur.X > Finish.X) do
  206.       begin
  207.         Dec(L);
  208.         LongInt(Extent) := GetTextExtent(DC, Text, L);
  209.       end;
  210.  
  211.       { Adjust the current line height }
  212.       if Extent.Y > Height then Height := Extent.Y;
  213.  
  214.       if not IsAborted(DC) then
  215.         TextOut(DC, Cur.X, Cur.Y, Text, L);
  216.  
  217.       Dec(Len, L);
  218.       Inc(Text, L);
  219.       if Len > 0 then NewLine(Prn)
  220.       else Cur.X := Extent.X;
  221.     end;
  222.   end;
  223. end;
  224.  
  225. { PrnString ------------------------------------------------------ }
  226. {  Print a string to the printer handling special characters.      }
  227.  
  228. procedure PrnString(var Prn: PrnRec; Text: PChar; Len: Integer);
  229. var
  230.   L: Integer;                   { Temporary used for printing }
  231.   TabWidth: Word;               { Width (in pixels) of a tab }
  232.  
  233.   { Flush to the printer the non-specal characters found so far }
  234.   procedure Flush;
  235.   begin
  236.     if L <> 0 then
  237.       PrnOutStr(Prn, Text, L);
  238.     Inc(Text, L + 1);
  239.     Dec(Len, L + 1);
  240.     L := 0;
  241.   end;
  242.  
  243.   { Calculate the average character width }
  244.   function AvgCharWidth: Word;
  245.   var
  246.     Metrics: TTextMetric;
  247.   begin
  248.     GetTextMetrics(Prn.DC, Metrics);
  249.     AvgCharWidth := Metrics.tmAveCharWidth;
  250.   end;
  251.  
  252. begin
  253.   L := 0;
  254.   with Prn do
  255.   begin
  256.     while L < Len do
  257.     begin
  258.       case Text[L] of
  259.         #9:
  260.           begin
  261.             Flush;
  262.             TabWidth := AvgCharWidth * 8;
  263.             Inc(Cur.X, TabWidth - ((Cur.X + TabWidth + 1)
  264.               mod TabWidth) + 1);
  265.             if Cur.X > Finish.X then NewLine(Prn);
  266.           end;
  267.         #13: Flush;
  268.         #10:
  269.           begin
  270.             Flush;
  271.             NewLine(Prn);
  272.           end;
  273.         ^L:
  274.           begin
  275.             Flush;
  276.             NewPage(Prn);
  277.           end;
  278.       else
  279.         Inc(L);
  280.       end;
  281.     end;
  282.   end;
  283.   Flush;
  284. end;
  285.  
  286. { PrnInput ------------------------------------------------------- }
  287. {  Called when a Read or Readln is applied to a printer file.      }
  288. {  Since reading is illegal this routine tells the I/O system that }
  289. {  no characters where read, which generates a runtime error.      }
  290.  
  291. function PrnInput(var F: TTextRec): Integer; far;
  292. begin
  293.   with F do
  294.   begin
  295.     BufPos := 0;
  296.     BufEnd := 0;
  297.   end;
  298.   PrnInput := 0;
  299. end;
  300.  
  301. { PrnOutput ------------------------------------------------------ }
  302. {  Called when a Write or Writeln is applied to a printer file.    }
  303. {  The calls PrnString to write the text in the buffer to the      }
  304. {  printer.                                                        }
  305.  
  306. function PrnOutput(var F: TTextRec): Integer; far;
  307. begin
  308.   with F do
  309.   begin
  310.     PrnString(PrnRec(UserData), PChar(BufPtr), BufPos);
  311.     BufPos := 0;
  312.     PrnOutput := PrnRec(UserData).Status;
  313.   end;
  314. end;
  315.  
  316. { PrnIgnore ------------------------------------------------------ }
  317. {  Will ignore certain requests by the I/O system such as flush    }
  318. {  while doing an input.                                           }
  319.  
  320. function PrnIgnore(var F: TTextRec): Integer; far;
  321. begin
  322.   PrnIgnore := 0;
  323. end;
  324.  
  325. { AbortProc ------------------------------------------------------ }
  326. {   Abort procedure used for printing.                             }
  327.  
  328. var
  329.   AbortProcInst: TFarProc;      { Instance of the abort proc }
  330.  
  331. function AbortProc(Prn: HDC; Code: Integer): Bool; export;
  332. var
  333.   Msg: TMsg;
  334.   UserAbort: Boolean;
  335. begin
  336.   UserAbort := IsAborted(Prn);
  337.   while not UserAbort and PeekMessage(Msg, 0, 0, 0, pm_Remove) do
  338.     if not ProcessPrnMessage(Msg) then
  339.     begin
  340.       TranslateMessage(Msg);
  341.       DispatchMessage(Msg);
  342.     end;
  343.   AbortProc := not UserAbort;
  344. end;
  345.  
  346. { PrnClose ------------------------------------------------------- }
  347. {  Deallocates the resources allocated to the printer file.        }
  348.  
  349. function PrnClose(var F: TTextRec): Integer; far;
  350. begin
  351.   with PrnRec(F.UserData) do
  352.   begin
  353.     if DC <> 0 then
  354.     begin
  355.       if not IsAborted(DC) then
  356.       begin
  357.         NewPage(PrnRec(F.UserData));
  358.         if Escape(DC, EndDoc, 0, nil, nil) <= 0 then
  359.           Status := wpPrintingError;
  360.       end;
  361.       DeleteDC(DC);
  362.       UnAbort(DC);
  363.     end;
  364.     PrnClose := Status;
  365.   end;
  366. end;
  367.  
  368. { PrnOpen -------------------------------------------------------- }
  369. {  Called to open I/O on a printer file.  Sets up the TTextFile to }
  370. {  point to printer I/O functions.                                 }
  371.  
  372. function PrnOpen(var F: TTextRec): Integer; far;
  373. const
  374.   Blank: array[0..0] of Char = '';
  375. begin
  376.   with F, PrnRec(UserData) do
  377.   begin
  378.     if Mode = fmInput then
  379.     begin
  380.       InOutFunc := @PrnInput;
  381.       FlushFunc := @PrnIgnore;
  382.       CloseFunc := @PrnIgnore;
  383.     end
  384.     else
  385.     begin
  386.       Mode := fmOutput;
  387.       InOutFunc := @PrnOutput;
  388.       FlushFunc := @PrnOutput;
  389.       CloseFunc := @PrnClose;
  390.  
  391.       { Setup the DC for printing }
  392.       Status := Escape(DC, SetAbortProc, 0, PChar(AbortProcInst), nil);
  393.       if Status > 0 then
  394.         if Title <> nil then
  395.         begin
  396.           Status := Escape(DC, StartDoc, StrLen(Title), Title, nil);
  397.           StrDispose(Title);
  398.         end
  399.         else
  400.           Status := Escape(DC, StartDoc, 0, @Blank, nil);
  401.  
  402.       if Status <= 0 then
  403.         Status := wpPrintingError
  404.       else
  405.         Status := 0;
  406.  
  407.       { Initialize the printer record }
  408.       LongInt(Cur) := 0;
  409.       Finish.X := GetDeviceCaps(DC, HorzRes);
  410.       Finish.Y := GetDeviceCaps(DC, VertRes);
  411.       Height := 0;
  412.     end;
  413.     PrnOpen := Status;
  414.   end;
  415. end;
  416.  
  417. { FetchStr ------------------------------------------------------- }
  418. {   Returns a pointer to the first comma delimited field pointed   }
  419. {   to by Str. It replaces the comma with a #0 and moves the Str   }
  420. {   to the beginning of the next string (skipping white space).    }
  421. {   Str will point to a #0 character if no more strings are left.  }
  422. {   This routine is used to fetch strings out of text retrieved    }
  423. {   from WIN.INI.                                                  }
  424.  
  425. function FetchStr(var Str: PChar): PChar;
  426. begin
  427.   FetchStr := Str;
  428.   if Str = nil then Exit;
  429.   while (Str^ <> #0) and (Str^ <> ',') do
  430.     Str := AnsiNext(Str);
  431.   if Str^ = #0 then Exit;
  432.   Str^ := #0;
  433.   Inc(Str);
  434.   while Str^ = ' ' do
  435.     Str := AnsiNext(Str);
  436. end;
  437.  
  438. { ---------------------------------------------------------------- }
  439. { External interface routines ------------------------------------ }
  440. { ---------------------------------------------------------------- }
  441.  
  442. { AbortPrn ------------------------------------------------------- }
  443.  
  444. procedure AbortPrn(var F: Text);
  445. begin
  446.   Abort(PrnRec(TTextRec(F).UserData).DC);
  447. end;
  448.  
  449. { AssignPrn ------------------------------------------------------ }
  450.  
  451. procedure AssignPrn(var F: Text; Device, Driver, Port: PChar);
  452. var
  453.   DeviceStr: array[0..80] of Char;
  454.   P: PChar;
  455. begin
  456.   if Device = nil then
  457.   begin
  458.     { Get the default printer device }
  459.     GetProfileString('windows', 'device', '', DeviceStr,
  460.       SizeOf(DeviceStr) - 1);
  461.     P := DeviceStr;
  462.     Device := FetchStr(P);
  463.     Driver := FetchStr(P);
  464.     Port := FetchStr(P);
  465.   end;
  466.   with TTextRec(F), PrnRec(UserData) do
  467.   begin
  468.     Mode := fmClosed;
  469.     BufSize := SizeOf(Buffer);
  470.     BufPtr := @Buffer;
  471.     OpenFunc := @PrnOpen;
  472.     Name[0] := #0;
  473.     DC := CreateDC(Driver, Device, Port, nil);
  474.     if DC = 0 then
  475.     begin
  476.       InOutRes := wpInvalidDevice;
  477.       Exit;
  478.     end;
  479.     Title := nil;
  480.   end;
  481. end;
  482.  
  483. { AssignDefPrn --------------------------------------------------- }
  484.  
  485. procedure AssignDefPrn(var F: Text);
  486. begin
  487.   AssignPrn(F, nil, nil, nil);
  488. end;
  489.  
  490. { SetPrnFont ----------------------------------------------------- }
  491.  
  492. function SetPrnFont(var F: Text; Font: HFont): HFont;
  493. begin
  494.   SetPrnFont := SelectObject(PrnRec(TTextRec(F).UserData).DC, Font);
  495. end;
  496.  
  497. { TitlePrn ------------------------------------------------------- }
  498.  
  499. procedure TitlePrn(var F: Text; Title: PChar);
  500. var
  501.   S: array[0..31] of Char;
  502. begin
  503.   { Limit title size to 31 characters plus 0 }
  504.   StrLCopy(S, Title, SizeOf(S));
  505.   PrnRec(TTextRec(F).UserData).Title := StrNew(S);
  506. end;
  507.  
  508. { ---------------------------------------------------------------- }
  509.  
  510. function DummyMsg(var Msg: TMsg): Boolean; far;
  511. begin
  512.   DummyMsg := False;
  513. end;
  514.  
  515. var
  516.   SaveExit: Pointer;                    { Saves the old ExitProc }
  517.  
  518. procedure ExitWinPrn; far;
  519. begin
  520.   FreeProcInstance(AbortProcInst);
  521.   ExitProc := SaveExit;
  522. end;
  523.  
  524. begin
  525.   ProcessPrnMessage := DummyMsg;
  526.  
  527.   AbortProcInst := MakeProcInstance(@AbortProc, hInstance);
  528.  
  529.   SaveExit := ExitProc;
  530.   ExitProc := @ExitWinPrn;
  531. end.
  532.