VB BOOK - Utility to print ASCII text files to LaserJet Series II, IIp or III in booklet format. Based on the program, PC BOOK, by Jay Munro, Copyright 1990 PC Magazine - Ziff Davis - Jay Munro. Original Copyright still applicable (as far as I'm concerned). Originally written by Jay Munro and converted to Visual Basic by Dennis Scott on 1 September 1991. Documentation adapted from original PCBOOK.DOC file: ---------------------------------------------------- Purpose: VB BOOK prints README and similar ASCII files in booklet format on an HP LaserJet II, IIp, or III printer. Uses two-sided printing in landscape mode, with Line Printer font so that four standard pages of 80 characters per line, 66 lines per page are printed on each sheet without formatting changes. Remarks: The filename can designate any ASCII text file in which each line is terminated with a Carriage Return/Line Feed combination. Page Feed characters can be added to force breaks in addition to those implicit in the 60-line (68-line if a header is used) page length. The original switches in PC Book have been replaced with Windows Checkboxes. Any or all of the Checkboxes can be used to create a one-line header that prints the filename, page number, and date, on all but blank pages. The Wrap Checkbox causes lines exceeding 80 characters to wrap; by default such lines are wrapped. Output can now be directed to LPT1, LPT2, COM1, COM2, or File. Noted that you must set the proper parameters for your port before printing to a COM port. If File is selected, you will be prompted to enter an output filename. VB BOOK sequences the page printing so that all side-one pages are printed in one pass. The sheets are then put back in the LaserJet paper tray and all side-two pages are printed. The finished booklet can then be created with a single fold. When using the normal (top) LaserJet output bin the pass-one sheets should be reinserted in the paper tray without any relative change in orientation. If rear output from the printer is used, the sheets must be turned over as a group before reinsertion. Requirements: VB Book requires Windows 3. The Visual Basic Run Time file, VBRUN100.DLL, must be in the path (recommend putting it in your Windows directory). Modifications will require Microsoft Visual Basic Version 1.0. Both the executable and the source files for VB Book are provided. Comments to QuickBASIC programmers: The conversion of PC Book from QuickBASIC to Visual Basic (VB) was undertaken as an exercise in converting original QuickBASIC (QB) source code into VB code. It was surprisingly easy! The hardest part was deciding HOW the screens (forms in VB) should look. Actually designing the forms is very easy. Once the forms were designed, I just used "load text" to get the original QB code into the main form. VB does almost everything automatically. For example, all SUBs are automatically created and the old code placed in them. Some things will need to be changed, of course. Any input from the user (QBs Input, Line Input, and Inkey commands) needs to converted to "InputBox$" functions. Similarly, the "print" statements (with corresponding Locate's) were converted to "MsgBox" functions or a form created to place them onto. The QB code that performed initialization was placed in the main form's Form_Load procedure. All Declare's were removed. Most of the above changes were accomplished by pressing F5 (run) and letting VB tell me what it didn't like. I just used the ol' cut & paste to put things in the right place. I then defined what should happen when each of the controls on the forms were activated (usually clicked) and wrote the code into those areas. Putting the drive, directory, and file selection boxes on the screen and activating them is phenomenally easy! The overall concepts of VB are a little difficult to comprehend at first but then it clicks and it all becomes quite simple. I would highly recommend that you take the tutorial before doing anything. If you don't, you will not comprehend what's going on and will probably get disgusted and quit. This conversion took about a day to do because of having to learn VB. If I had to do it again I could probably complete it in an hour or less. Please note that the code has not been "cleaned up" - some debugging code may still be there. Recommended additions: Want some practice with VB? Why not add: A screen preview of output while printing (or before printing or instead of printing) and status display of percent completed. If anyone adds this capability or changes the code in some other way, please be kind enough to send me a copy. Please send comments or suggestions to: Dennis Scott CompuDirect 9102 Ocean Gate San Antonio, TX 78242 (512)623-6856 Disclaimer: The standard disclaimer applies: Use this program at you own risk. Myself, and the original Copyright holder, will not be held responsible for anything! The Source Code: ---------------- The following is a complete listing of the VB source code, included here for those to see that do not yet own VB. For those that do own VB, the forms and code are also included in the archive. Just place all files in their own directory then open the VBBOOK project. Note that almost all of the following code was originally PC Book code - very little had to be added for VB. The Global form (VBBOOK.BAS): ----------------------------- Type Flags 'Misc flag variables CurDate As Integer DoHeader As Integer FileTitle As Integer LineLen As Integer LineWrap As Integer PgNumber As Integer End Type 'The VBBOOK.FRM form: (used for the first little box that goes away in 5 sec's.) '-------------------- '(No code in this form. Just displays a message for 5 secs) 'The VBBOUT.FRM form: '-------------------- '(No code in this form. It's a blank, full-page form used to cover up the 'desktop. There should be a better way to do this!) 'The Main module, VBBINP.FRM: (This is where all the selections are done.) '---------------------------- 'Declarations section: 'Note that Dim Shared is not really needed but VB done it during the automatic 'conversion from QuickBasic code so I left it that way. Dim Shared ESC$, FF$, LF$, filename$, OUTFILE$, NL$ Dim Shared page%, num$, tune% Dim Shared PC As Flags Dim lastchange As Integer Const fileboxclick = 0, dirsboxclick = 1 'Used by file selection routine Const true = -1, false = 0 'Now the Subs start: Static Sub BuildArray (ptrarray&(), pgcount%) MaxLines% = 66 'Maximum number of lines Offset& = 1 'Start of file (seek point) Open filename$ For Binary Access Read As #1 Len = 1 'Open file to check TotalSize& = LOF(1) 'Get LEN of file so we don't read too far FileLeft& = TotalSize& 'Setup a counter to show whats left 'FRE is not supported by VB. Just set it to 64K MemAvail& = 65536 'FRE(FileName$) - 2048 'Check available string memory If MemAvail& < 2048 Then Error 14 'Force out of memory error SixteenK% = 16384 If TotalSize& > SixteenK% Then 'Set a buffer size If MemAvail& > SixteenK% Then 'If the file is larger than 16K BufAvail& = SixteenK% 'Set it to 16k Else BufAvail& = MemAvail& End If Else If TotalSize& < MemAvail& Then 'Otherwise set it to file size BufAvail& = TotalSize& End If BuffSize% = BufAvail& End If pgcount% = 1 'Initialize page count ptrarray&(pgcount%) = 1 'First pointer is always 1 LnCount% = 0 'Initialize line count GetPage: 'Read the file If FileLeft& < BufAvail& Then 'Check amount left to read Buffer$ = Space$(FileLeft&) 'If less than our buffer, use lessor Else Buffer$ = Space$(BufAvail&) 'Otherwise use full buffer size End If Get #1, Offset&, Buffer$ 'Read in a buffers worth stptr% = 1 'Pointer into buffer$ LastLine% = 0 'remember last position PageCheck: TempLn% = InStr(stptr%, Buffer$, LF$) 'Position of next linefeed temppg% = InStr(stptr%, Buffer$, FF$) 'Position of next pagefeeds If temppg% Then 'If there was a page feed If temppg% < TempLn% Or TempLn% = 0 Then ' was it before our linefeed? pgcount% = pgcount% + 1 ' yes then bump page count ptrarray&(pgcount%) = Offset& + temppg% ' set next array element stptr% = temppg% + 1 ' set instr pointer LnCount% = 0 ' reset linecount If stptr% < Len(Buffer$) Then GoTo PageCheck 'and loop back for more End If End If If TempLn% Then 'Linefeed If PC.LineWrap Then 'If Line Wrap, check length If TempLn% - stptr% > PC.LineLen Then 'Greater than 80? Do 'check for line wrap LnCount% = LnCount% + 1 'increment line If LnCount% = MaxLines% Then GoTo PageBreak '> 66 lines End If stptr% = stptr% + PC.LineLen Loop While TempLn% - stptr% > PC.LineLen End If End If LnCount% = LnCount% + 1 'Increment page count PageBreak: If LnCount% = MaxLines% Then pgcount% = pgcount% + 1 If pgcount% > 512 Then msg$ = "Too may pages - printing only 512." MsgBox msg$, 0, "Notice" GoTo EndBuild End If ptrarray&(pgcount%) = Offset& + TempLn% 'point to next in point in file LnCount% = 0 End If stptr% = TempLn% + 1 'point ahead 1 byte for next scan If stptr% <= Len(Buffer$) Then GoTo PageCheck 'keep checking End If End If Offset& = Offset& + Len(Buffer$) 'Pointer into file (tally) stptr% = 1 'Reset Buffer pointer FileLeft& = TotalSize& - Offset& 'Calculate how much is left If Offset& < TotalSize& Then GoTo GetPage 'If more text in file, keep going EndBuild: ptrarray&(pgcount% + 1) = TotalSize& 'Set last pointer to end of file Close #1 'Close input file End Sub 'End of BuildArray Sub Sub Label1_load () 'This section is not used at this time Print String$(80, 61) Print "PCBook - PC Magazine Booklet Printing Utility" Print "Copyright 1990 PC Magazine Ziff Davis Jay Munro" Print Print "Converted to Visual Basic by Dennis Scott" Print String$(80, 61) End Sub Static Sub DoMacro (num$) Print #2, ESC$; "&f"; num$; "y2X"; 'execute the macro End Sub Static Sub EndMacro (num$) Print #2, ESC$; "&f"; num$; "y1X"; 'Send end of macro command Print #2, ESC$; "&f"; num$; "y9X"; 'Make it temporary (10 to be permanent) End Sub Static Sub Header (page%) hdr$ = Space$(PC.LineLen) 'Create a string to print If PC.FileTitle Then 'Print the filename Mid$(hdr$, 40 - Len(filename$) \ 2) = UCase$(filename$) End If If PC.PgNumber Then 'Print the current page PTemp$ = "Page" + Str$(page%) If page% Mod 2 Then Mid$(hdr$, PC.LineLen - Len(PTemp$)) = PTemp$ 'odd page, right side Else Mid$(hdr$, 1) = PTemp$ 'even page, left side End If End If If PC.CurDate Then 'Print the current date If page% Mod 2 Then Mid$(hdr$, 1) = Date$ 'even page, left side Else Mid$(hdr$, PC.LineLen - Len(Date$)) = Date$ 'odd page, right side End If End If Print #2, hdr$ 'Print the Header Print #2, ' and skip a line for readability End Sub Static Sub LJLocate (X%, Y%) 'Laser Jet cursor locate Temp$ = ESC$ + "&a" + LTrim$(Str$(Y%)) + "r" + LTrim$(Str$(X%)) + "C" Print #2, Temp$; End Sub Static Sub printlogo () 'Banner logo (About VB Box!) msg$ = " VB Book" + NL$ msg$ = msg$ + " Converted to Visual Basic" + NL$ msg$ = msg$ + " by Dennis Scott." + NL$ msg$ = msg$ + NL$ msg$ = msg$ + "Send Comments/Suggestions to:" + NL$ msg$ = msg$ + " CompuDirect" + NL$ msg$ = msg$ + " 9102 Ocean Gate" + NL$ msg$ = msg$ + " San Antonio, TX" + NL$ msg$ = msg$ + " (512)623-6856" + NL$ MsgBox msg$, 0, "About VB Book" End Sub Sub PrintSetup () 'Send codes to prepare printer Print #2, ESC$; "E"; 'Reset laserjet (simple isn't it!) Print #2, ESC$; "&l1o5.45C"; 'Select lineprinter font" Print #2, ESC$; "(s0p16.66H"; ' and pitch Print #2, ESC$; "&l0L"; 'Turn off page feed at 66 lines If PC.LineWrap Then 'Wrap lines > 80 chars Print #2, ESC$; "&s0C"; End If Print #2, ESC$; "&l2E"; 'Top margin 2 lines Call StartMacro("1") 'Left side macro Print #2, ESC$; "9"; 'Reset left - right margins Print #2, ESC$; "&a0l80M"; 'set left margin 0, right 80 Call EndMacro("1") Call StartMacro("2") 'Right side macro Print #2, ESC$; "9"; 'Reset left - right margins Print #2, ESC$; "&a95l175M"; 'set left margin 95, right 175 Call EndMacro("2") End Sub Static Sub StartMacro (num$) Print #2, ESC$; "&f"; num$; "Y"; 'Macro will have an id of Num$ Print #2, ESC$; "&f0X"; 'Start the macro now End Sub Sub Form_Click () 'If user clicks anywhere on the form, call the about box Call printlogo End Sub 'This is the main code - everything is actually called from here and this 'is where most of the VB changes are located Sub go_click () 'VB Code for Drive, Directory, and File selections If index >= 3 Then End If lastchange = dirsboxclick Then dir1.path = dir1.list(dir1.listindex) Else If file1.filename <> "" Then ChDrive drive1.drive ChDir file1.path filename$ = file1.filename Else msg$ = "Sorry! You must first select a file." abort% = MsgBox(msg$, 49, "No application chosen.") If abort% = 2 Then 'cancel button End End If End If End If lastchange = fileboxclick ReDim ptrarray&(513) 'total number of pages (512) On Error GoTo ErrorDept 'Error trapping 'Ensure that we have a file name (user may have clicked DoIt without 'entering a filename) GetName: If Len(filename$) = 0 Then If tune% Then Beep msg$ = "Enter a file name to print: " Title$ = "Filename" ' Set title. Default$ = "" NewName$ = InputBox$(msg$, Title$, Default$) ' Get user input. If Len(NewName$) = 0 Then ' Check if valid. msg$ = "You did not input a valid Filename." + NL$ msg$ = msg$ + "Click on OK to End Program" MsgBox msg$, 0, Title$ ' Display message. GoTo OutHere End If End If 'Build index array for pages in FileName$ 'Have not converted status display 'Print 'Print "Reading file "; filename$ Call BuildArray(ptrarray&(), page%) 'Built pointer array 'Figure number of pages needed If page% Mod 4 Then 'Even multiples of 4 only page% = page% + (4 - page% Mod 4) ' correct for less End If 'Have not converted status display 'Print 'Print "You will print "; Page% \ 4; "sheets" 'Report total number of pages 'Print 'JustCount% is set to false always due to status section not being 'converted to VB If JustCount% Then Print "Press any key to continue, or ESC to cancel printing" GoSub KeyIn End If Open OUTFILE$ For Output As #2 'Open printer or output file Call PrintSetup 'Set up printer 'Page parsing variables LeftSide% = page% RightSide% = 1 FirstPass% = -1 Open filename$ For Binary As #1 'Open the input file 'Have not converted status display 'Print "Printing Side 1 to "; outfile$; 'Track what is going on 'Start of print routine DoPass: Bookmark% = (page% \ 4) 'Flag for halfway through If Bookmark% = 0 Then Bookmark% = 1 'Force 1 if too small 'Read text and send to printer or file Do 'Print the right side of the page first If ptrarray&(RightSide% + 1) = 0 Then 'If blank, then skip it GoTo NextPage End If Call DoMacro("2") 'Start on right side LJLocate 95, 0 'Home the cursor If PC.DoHeader Then Call Header(RightSide%) 'Header if needed Buffer$ = Space$(ptrarray&(RightSide% + 1) - ptrarray&(RightSide%)) Get #1, ptrarray&(RightSide%), Buffer$ 'Read in a page If InStr(Buffer$, FF$) Then 'If the last character is a PF Print #2, Left$(Buffer$, InStr(Buffer$, FF$) - 1); 'print only text Else Print #2, Buffer$; 'Otherwise print full line End If NextPage: If ptrarray&(LeftSide% + 1) = 0 Then 'Don't print blank pages GoTo NextPage1 End If Call DoMacro("1") 'Reset margins for left side LJLocate 0, 0 'Home the cursor If PC.DoHeader Then Call Header(LeftSide%) 'Header if needed Buffer$ = Space$(ptrarray&(LeftSide% + 1) - ptrarray&(LeftSide%)) 'Setup buffer for input If LeftSide% = 0 Then 'If pointing at blank page, skip GoTo NextPage1 End If Get #1, ptrarray&(LeftSide%), Buffer$ 'Read in a page If InStr(Buffer$, FF$) Then 'if the last character is a PF Print #2, Left$(Buffer$, InStr(Buffer$, FF$) - 1); 'print only text Else 'print only text Print #2, Buffer$; 'otherwise print all End If NextPage1: Print #2, FF$; 'Page feed LeftSide% = LeftSide% - 2 'Calculate next page in series RightSide% = RightSide% + 2 Bookmark% = Bookmark% - 1 'Track our progress Loop Until Bookmark% = 0 'Print pages until halfway through 'Pause between sides to allow for paper reinsertion If FirstPass% Then 'If side one, prompt and get 2nd side msg$ = "First Pass has been Completed." + NL$ msg$ = msg$ + "Insert paper back in tray and Click OK" If tune% Then Beep WaitKey: 'Press any key to continue loop MsgBox msg$, 0, "Waiting" FirstPass% = 0 'Flag for second pass 'Have not converted status display 'msg$ = "Printing Side 2 to " + outfile$ 'Print msg$ 'Report on progress GoTo DoPass End If 'End of first pass 'Printing is done now msg$ = "Printing completed." If tune% Then Beep MsgBox msg$, 64, "Done" PrtReset: Print #2, ESC$; "E"; 'Reset laserjet OutHere: Close 'Close all files End 'Thats all for now 'Error handler. Converted to VB errors. ErrorDept: Beep msg$ = "*** Error ***" + NL$ Select Case Err Case 482 msg$ = msg$ + "Printer error." Case 68 msg$ = msg$ + "Device is unavailable." Case 71 msg$ = msg$ + "Insert a disk in the drive and close the door." Case 57 msg$ = msg$ + "Device Input/Output Error (Check Printer!)." Case 61 msg$ = msg$ + "Disk is full." Case 64, 52 msg$ = msg$ + "That filename is illegal." Case 76 msg$ = msg$ + "That path doesn't exist." Case 54 msg$ = msg$ + "Can't open your file for that type of access." Case 55 msg$ = msg$ + "This file is already open." Case 62 msg$ = msg$ + "This file has a nonstandard end-of-file marker" + NL$ msg$ = msg$ + "or an attempt was made to read beyond the end-" + NL$ msg$ = msg$ + "of-file marker." Case Else msg$ = msg$ + "Error number " + Str$(Err) End Select GoSub AWayOut Resume AWayOut: abort% = MsgBox(msg$, 17, "ERROR") KeyIn: If abort% = 2 Then 'If user presses Cancel, Exit Close End End If Return 'End of main module End Sub Sub Dir1_Change () file1.path = dir1.path file1.SetFocus End Sub Sub Dir1_Click () lastchange = dirsboxclick End Sub Sub File1_Click () 'use the following line to put filename in frame 'if using a frame: 'inname.caption = "Load " + file1.filename lastchange = fileboxclick End Sub Sub File1_DblClick () 'Allow the user to double-click on an input file and start printing Call go_click End Sub 'CLKx Subs are the Check Boxes for selecting whether to use speaker, etc Sub clk1_Click () 'Toggle on/off If PC.FileTitle = 0 Then PC.FileTitle = -1 PC.DoHeader = -1 Else PC.FileTitle = 0 'Still have to do the Header if clk2 or clk3 buttons are checked If (clk2.value = -1) Or (clk3.value = -1) Then PC.DoHeader = -1 Else PC.DoHeader = 0 End If End If End Sub Sub clk2_Click () 'Toggle on/off If PC.CurDate = 0 Then PC.CurDate = -1 PC.DoHeader = -1 Else PC.CurDate = 0 'Still have to do the Header if clk1 or clk3 buttons are checked If (clk1.value = -1) Or (clk3.value = -1) Then PC.DoHeader = -1 Else PC.DoHeader = 0 End If End If End Sub Sub clk3_Click () 'Toggle on/off If PC.PgNumber = 0 Then PC.PgNumber = -1 PC.DoHeader = -1 Else PC.PgNumber = 0 'Still have to do the Header if clk1 or clk2 buttons are checked If (clk1.value = -1) Or (clk2.value = -1) Then PC.DoHeader = -1 Else PC.DoHeader = 0 End If End If End Sub Sub clk4_Click () 'Toggle on/off tune% = Not tune% End Sub Sub clk5_Click () 'Toggle on/off PC.LineWrap = Not PC.LineWrap End Sub Sub Drive1_Change () dir1.path = drive1.drive End Sub Sub Cancel_Click () 'If user clicks on the Cancel button then ... Close End End Sub 'This Sub is ran when the VBBINP.FRM is loaded (ie, Showed) Sub Form_Load () 'Put the options in the output port/filename Combobox comboutname.AddItem "LPT1" comboutname.AddItem "LPT2" comboutname.AddItem "COM1" comboutname.AddItem "COM2" comboutname.AddItem "file" comboutname.text = comboutname.list(0) 'default to LPT1 OUTFILE$ = "LPT1" 'set default check-box values tune% = -1 PC.FileTitle = -1 PC.DoHeader = -1 PC.CurDate = -1 PC.PgNumber = -1 PC.LineWrap = -1 'set some variables ESC$ = Chr$(27) 'Standard ESC code FF$ = Chr$(12) 'Page Feed LF$ = Chr$(10) 'Line Feed NL$ = Chr$(13) + Chr$(10) 'CR and LF (New Line) JustCount% = 0 'Not allowing "just counting" PC.LineLen = 80 'Maximum length of line End Sub 'User clicks on the Combobox Sub comboutname_Click () 'Select where to send the output Select Case comboutname.text Case "LPT1" OUTFILE$ = "LPT1" Case "LPT2" OUTFILE$ = "LPT2" Case "COM1" OUTFILE$ = "COM1" Case "COM2" OUTFILE$ = "COM2" Case "file" If file1.filename = "" Then 'If no input filename is selected comboutname.text = "LPT1" ' default back to LPT1 OUTFILE$ = "LPT1" msg$ = "You must select an input filename first!" MsgBox msg$, 32 file1.SetFocus 'set focus to file list box Exit Sub End If 'Now make up a default output filename with same 'name and PRN as the extension OUTFILE$ = UCase$(Left$(file1.filename, InStr(file1.filename, ".")) + "PRN") msg$ = "WAIT" + NL$ + "Enter filename to print to:" OUTFILE$ = InputBox$(msg$, "Output File Name", OUTFILE$) 'Get a filename If OUTFILE$ <> "" Then comboutname.text = UCase$(OUTFILE$) 'put filename in combo box go.SetFocus Else 'Insist on a filename comboutname.text = "LPT1" OUTFILE$ = "LPT1" file1.SetFocus 'set focus to file list box End If End Select End Sub Sub Picture1_Click () Call printlogo 'Show the "about" box End Sub