|
25 Years of Programming
An open source source for C, C++, OWL, BASIC, MDB, XLS, DOT, and more... |
Home Projects Up Sitemap Search Blog Forum+Chat About Us Privacy Terms of Use Feedback FAQ Images Services Ads Donate Humor |
|
|
CPrint.cpp file format and print utilityA Borland C++ 4.0 MSDOS target program for printing program source code or other text files. It has numerous options for setting lines-per-inch, lines-per-page, margin settings, tab expansion, and optional line numbering. This was a handy program that I used a lot when no commercial program could provide the exact settings I wanted. It is intended for a "text-only" type printer, and has escape codes for my Toshiba P351 printer in it. These should be fairly easy to customize for other text printers. There is no zip file. Copy the text and paste it into files of the names shown. Links to the 3 additional source files it needs are in the code below.
|
|
/* cprint.cpp 10-19-02
Copyright (C)1996-2000, 2002 Steven Whitney.
Initially published by http://25yearsofprogramming.com.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License (GPL)
Version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
For printing C programs or other files to Toshiba printer in various
formats. Needed because CLIST can't omit line numbers, MSWord paginates
incorrectly, and DOSPrint only allows page widths of 80 or 132.
Creates a formatted .L file, which you can COPY to printer,
or optionally prints it immediately, and optionally deletes it.
Borland C++ 4.0, MSDOS target.
------
To Do:
lots of changes made 4/2000, most untested. Watch for problems.
plus DoubleStrike option 10/2002, untested.
Allow pausing DURING printing, just after a FF, to allow realigning paper at page top.
(To do it, you must READ the .L file, and print 1 line at a time, not copy to PRN.)
(Keep note, but this isn't necessary if creepadjust feature works.)
------
Notes:
--Toshiba P351 printer page creep is about 1/64" per page average, not consistent on
each page, and not always the same at the top and bottom of a page. Ideas:
1. Something has worn out, is not its original size, so it measures page length wrong.
2. Logic is confused by line spacing that isn't 6lpi (No)
3. I have a dip switch set wrong (probably not)
4. Paper holes are off (probably not, but would explain why it happened a long
time ago (I think), then stopped, then reappeared).
5. Paper isn't 11" long (No) (creep occurs on various paper types)
6. Logic translates FF into a # of lines instead of a distance # of inches
7. Temperature affects belt tightness or something else.
8. Stepper motor is worn
*/
#include <classlib\time.h>
#pragma hdrstop
#include "c:\bcs\my.h"
#include "c:\bcs\mylib.cpp"
#include "c:\bcs\library\filearay.cpp"
////////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv)
{
string::set_paranoid_check(TRUE);
string::set_case_sensitive(FALSE);
TTime::PrintDate(TRUE);
TDate::SetPrintOption(TDate::Numbers);
//----------------------------------------------------------------------------
// USER INSTRUCTIONS
// cout << endl;
cout << "Usage: CPRINT file1.ext file2.ext ... [wildcards ok]" << endl;
cout << "Output goes to file1.L file2.L ..." << endl;
cout << "Print the .L file with: COPY /B filename PRN" << endl;
cout << endl;
if(argc < 2)
exit(1);
//----------------------------------------------------------------------------
// GET THE FILES TO PROCESS, AND DISPLAY THE LIST
FileArray FileList;
for(int i = 1 ; i < argc ; i++)
FileList.AddFile(to_upper(string(argv[i])));
cout << "Files to be printed:" << endl << FileList << endl;
presskey();
//----------------------------------------------------------------------------
// VARIABLES
// printer initialization strings. Also see my.h, and think about how to handle Epson,
// too, although Epson resident fonts are so poor that the best method is to use cprint
// for formatting and tab expansion, but print the .L file from MSWord in a good font.
enum { ELITE12, COURIER10, CONDENSED };
string fontnames[3] = { "ELITE12", "COURIER10", "CONDENSED" };
char elite12[] = { ESC,'*','1',ESC,'E','1','0',0 };
char courier10[] = { ESC,'*','2',ESC,'E','1','2',0 };
char condensed[] = { ESC,'*','0',ESC,'[',0 };
char DoubleStrikeOn[] = { ESC,'K','2',0 }; // for use when ribbon is weak
char DoubleStrikeOff[] = { ESC,'M',0 };
char printerreset[] = { ESC,26,'I',0 };
char lpi4[] = { ESC,'L','1','2',0 };
char lpi6[] = { ESC,'L','0','8',0 };
char lpi7[] = { ESC,'L','0','7',0 }; // actually 2/15", 7.5lpi
char lpi8[] = { ESC,'L','0','6',0 };
char backup[] = { ESC,'V','@','@','C',0 }; // reverse carriage 3/48"
char settopofform[] = { ESC, 'F','6','6',0 }; // also sets length to 11"
// user-settable parameters
BOOL pageheaders = FALSE; // these starting default values are for a text file
BOOL linenumbers = FALSE;
BOOL pagecreepadjust = FALSE; // whether to adjust for Toshiba printer page creep
BOOL sendprintcodes = TRUE; // whether to embed printer escape codes
BOOL doublestrike = FALSE; // whether to print every line in bold (overstrike)
int font = COURIER10;
int tabexpand = 5;
int leftmargin = 0;
int rightmargin = 80;
int linesperpage = 60; // printable lines, not total form length
int pageno = 1; // starting # for first page, then counts from there
int logline = 1; // starting # for first line, then counts from there
int linesperinch = 6;
// variables used internally
string lastfileext; // extension of the previous file processed
//----------------------------------------------------------------------------
// PROCESS EACH FILE
while(FileList.GetItemsInContainer())
{
//----------------------------------------------------------------------------
// SET UP AND VERIFY FILE NAMES
string infilename = FileList.GetNext();
infilename.to_upper();
ifstream infile(infilename.c_str());
if(!infile)
{
cout << "\aInput file " << infilename << " not found." << endl;
presskey();
continue; // try next file in list
}
string infileext; // file name extension used later
if(infilename.contains("."))
infileext = infilename.substr(infilename.rfind("."));
if(infileext == ".L")
{
cout << infilename << ":" << endl;
cout << "\aFile with extension .L cannot be the input file." << endl;
presskey();
continue;
}
string outfilename = infilename; // output file same name
if(outfilename.contains(".")) // strip extension
outfilename.remove(outfilename.rfind("."));
outfilename += ".L"; // add .L
ofstream outfile;
outfile.open(outfilename.c_str(), ios::noreplace); // .L file already exists?
if(!outfile)
{
if(yesno("Output file " + outfilename + " already exists. Overwrite"))
{
outfile.close();
outfile.open(outfilename.c_str());
}
else
{
cout << "\aWill not create listing for " << infilename << "." << endl;
presskey();
continue;
}
}
//----------------------------------------------------------------------------
// PRE-READ THE FILE SO WE CAN REPORT # OF LINES
uint linesinfile = 0; // counts lines from the file, used later
string s;
while(getline(infile,s,'\n')) // the '\n' is required.
linesinfile++; // does this underreport by 1 line?
infile.close();
infile.open(infilename.c_str()); // reopen to start fresh
//----------------------------------------------------------------------------
// SET DEFAULT PRINT FORMAT, SOME DEPENDING ON WHAT TYPE OF FILE IS BEING PRINTED
// if the current fileext is same as the previous, carry those settings over.
pageno = 1; logline = 1; // these should always be reset
if(infileext != lastfileext)
{
if((infileext == ".c") || (infileext == ".cpp") || (infileext == ".h") ||
(infileext == ".rh") || (infileext == ".rc") || (infileext == ".bas"))
{
pageheaders = TRUE; linenumbers = FALSE; pagecreepadjust = FALSE;
sendprintcodes = TRUE; font = ELITE12; tabexpand = 4; leftmargin = 0;
rightmargin = 98; linesperpage = 71; linesperinch = 7;
}
else
if((infileext == ".asm") || (infileext == ".a"))
{
pageheaders = TRUE; linenumbers = FALSE; pagecreepadjust = FALSE;
sendprintcodes = TRUE; font = ELITE12; tabexpand = 9; leftmargin = 0;
rightmargin = 98; linesperpage = 71; linesperinch = 7;
}
else // text .txt file or other
{
pageheaders = FALSE; linenumbers = FALSE; pagecreepadjust = FALSE;
sendprintcodes = TRUE; font = COURIER10; tabexpand = 5; leftmargin = 0;
rightmargin = 80; linesperpage = 60; linesperinch = 6;
}
}
lastfileext = infileext; // we're through with it. current becomes previous.
//--------------------------------------------------------------------------
// DISPLAY AND ALLOW USER TO CHANGE FORMATTING SETTINGS
while(1)
{
char choice;
do
{
cout << "-----------------------------------------------------------" << endl;
cout << "Input file : " << infilename << endl;
cout << "Output file : " << outfilename << endl;
cout << "Lines in file: " << linesinfile << endl;
cout << endl;
cout << "0 " << "Done." << endl;
cout << "1 " << "Header each page : " << (pageheaders ? "Yes" : "No") << endl;
cout << "2 " << "Line numbers : " << (linenumbers ? "Yes" : "No") << endl;
cout << "3 " << "Tab expansion : " << tabexpand << endl;
cout << "4 " << "Left margin : " << leftmargin << endl;
cout << "5 " << "Right margin : " << rightmargin << endl;
cout << "6 " << "Lines per page : " << linesperpage << endl;
cout << "7 " << "Font : " << fontnames[font] << endl;
cout << "8 " << "Page #s start at : " << pageno << endl;
cout << "9 " << "Line #s start at : " << logline << endl;
cout << "A " << "Lines per INCH : " << linesperinch << endl;
cout << "B " << "Page creep adjust: " << (pagecreepadjust ? "Yes" : "No") << endl;
cout << "C " << "Use printer codes: " << (sendprintcodes ? "Yes" : "No") << endl;
cout << "D " << "Doublestrike Bold: " << (doublestrike ? "Yes" : "No") << endl;
cout << "H " << "View (or edit) HELP file" << endl;
cout << endl << "Enter choice: ";
choice = tolower(getche());
cout << endl << endl;
}
while(!string("0123456789abcdh").contains(choice));
if(choice == '0')
break; // Done. break from while(1)
switch(choice)
{
// case '0': return; // left over from when this was a separate fn
case '1': pageheaders = !pageheaders; break;
case '2': linenumbers = !linenumbers; break;
case '3': userinput("Tabs expand to how many spaces?",tabexpand); break;
case '4': userinput("Enter left margin",leftmargin); break;
case '5': userinput("Enter right margin",rightmargin); break;
case '6': userinput("Enter printable lines per page",linesperpage); break;
case '7':
do { userinput("Enter font, 0=ELITE12 1=COURIER10 2=CONDENSED",font); }
while((font < 0) || (font > 2));
break;
case '8':
pageheaders = TRUE; // assume we want headers
userinput("Enter page number for the first page",pageno);
break;
case '9':
linenumbers = TRUE; // assume we want line numbering
userinput("Enter line number for the first line in the file",logline);
break;
case 'a':
do
{
userinput("LPI: 4=GraphPaper, 6=Normal, 7(7.5)=Readable, 8=Barely",
linesperinch);
}
while((linesperinch < 4) || (linesperinch > 8) || (linesperinch == 5));
break;
case 'b': pagecreepadjust = !pagecreepadjust; break;
case 'c': sendprintcodes = !sendprintcodes; break;
case 'd': doublestrike = !doublestrike; break;
case 'h': system("edit.com c:\\util\\cprint.txt"); break;
}
} // while(1)
//----------------------------------------------------------------------------
// USE FILENAME ONLY (NO PATH) ON PAGE HEADERS?
if(pageheaders)
{
cout << "In page headers, file will show as: " << infilename << endl;
if(yesno("Strip path (use filename only)"))
infilename = FilePathParser(infilename).FileExt();
}
//----------------------------------------------------------------------------
// CALCULATE MARGINS AND PAGE HEADINGS
// column where file text begins.
// when adding line numbers, filename aligns with body text, not the line numbers.
// I allow ####__ for line numbers
int lefttextmargin = leftmargin + (linenumbers ? 6 : 0);
// build page title string
string timenow = TTime().AsString(); // time and date (we need its length)
// the 10 below allows for " Page ###"
int padding = rightmargin - lefttextmargin - infilename.length() - timenow.length() - 10;
padding = max(padding,0);
string title = string(' ',lefttextmargin) + infilename + string(' ',padding)
+ timenow + " Page ";
//----------------------------------------------------------------------------
// START SENDING DATA TO OUTPUT FILE HERE: PRINTER INITIALIZATION
if(sendprintcodes)
{
switch(font) // font and size info
{
case ELITE12: outfile << elite12; break;
case COURIER10: outfile << courier10; break;
case CONDENSED: outfile << condensed; break;
}
switch(linesperinch) // lines per inch
{
case 4: outfile << lpi4; break;
case 6: outfile << lpi6; break;
case 7: outfile << lpi7; break;
case 8: outfile << lpi8; break;
}
outfile << (doublestrike ? DoubleStrikeOn : DoubleStrikeOff);
}
//----------------------------------------------------------------------------
// READ AND PROCESS EACH LINE FROM THE FILE
int pagecount = 0; // # of actual physical pages in the printout
int physline = 1; // line count on the physical page
string fileline; // a line read from the input file
while(getline(infile,fileline,'\n'))
{
//----------------------------------------------------------------------------
// EXPAND TABS BEFORE ANYTHING ELSE SO TAB STOPS RELATE TO RAW FILELINE.LENGTH()
if(fileline.contains('\t'))
{
string temp;
int count = fileline.length();
for(int i = 0 ; i < count ; i++)
if(fileline[i] == '\t')
{
// if we're already on a tab stop, start towards the next one
if((temp.length() % tabexpand) == 0)
temp += ' ';
while((temp.length() % tabexpand) != 0)
temp += ' ';
}
else
temp += fileline[i];
fileline = temp;
}
//----------------------------------------------------------------------------
// ADD LINE NUMBER, IF NEEDED
if(linenumbers)
{
char buf[10]; // format the line number
ostrstream(buf,sizeof(buf)) << setw(4) << logline << " " << ends;
fileline.prepend(buf); // and insert it at beginning
logline++; // increment logical line count
}
fileline.prepend(string(' ',leftmargin)); // additional margin, if any
//----------------------------------------------------------------------------
// WRITE THE LINE TO THE OUTPUT FILE, WRAPPING AS OFTEN AS NEEDED
// wrapped lines have no line numbers, and align with the rest of the text
do
{
if(physline > linesperpage) // if at end of this page,
{
outfile << "\f"; // form feed to next page
// after every 4 pages, back up 3/48" and reset top of form to new pos
if(pagecreepadjust && sendprintcodes)
if((pagecount % 4) == 0)
outfile << backup << settopofform;
physline = 1;
pageno++;
}
if(physline == 1)
{
pagecount++;
if(pageheaders) // print page title header
{
outfile << title << setw(3) << pageno << endl << endl;
physline = 3;
}
}
string nextline; // any text that overran page width
if(fileline.length() > rightmargin)
{
nextline = fileline.substr(rightmargin); // save overrun portion,
nextline.prepend(string(' ',lefttextmargin)); // align with text
fileline.remove(rightmargin); // truncate current line,
}
outfile << fileline << endl; // and print it
physline++;
fileline = nextline; // loop to process overrun.
}
while(fileline.length());
} // while(getline)
outfile << "\f"; // eject
infile.close();
outfile.close();
//----------------------------------------------------------------------------
// END. OPTIONALLY PRINT, AND OPTIONALLY DELETE THE .L FILE
// Setting top-of-form manually makes it possible to print on single sheets.
// If you align at the physical top of page and count lines from there,
// you can't print on single sheets because the Toshiba pulls single sheets
// through until there is a 1" top margin.
cout << "--------------------------------------------------------" << endl;
cout << "Align printhead on the line where printing should start." << endl;
cout << "That will be the top-of-form position." << endl;
cout << endl;
cout << "Output file " << outfilename << " has " << pagecount << " pages. ";
if(yesno("Print it now"))
{
string cmd = "copy /b " + outfilename + " prn";
system(cmd.c_str());
if(yesno("Delete " + outfilename + " now"))
unlink(outfilename.c_str());
cout << endl;
}
else
{
cout << endl;
cout << "If printing is interrupted, edit the .L file to eliminate" << endl;
cout << "what's already been printed, then print the rest." << endl;
cout << "Don't use the IDE (or any auto-tabbing) editor," << endl;
cout << "and don't type tabs yourself. Use spaces." << endl;
cout << endl;
cout << "Copy " << outfilename << " to the printer as a binary file:" << endl;
cout << " COPY /B " << outfilename << " PRN" << endl;
cout << endl;
}
} // while(FileList.GetItemsInContainer())
return(0);
} // main
Save this text to a file called CPRINT.TXT. It is a simple help file for
referring to while
you specify the formatting settings. CPRINT opens the file and displays it to
you
using a text editor in edit mode, so you can make changes as you go. You might
need to specify a different text editor.
CPRINT Help:
Remember: Single Sheet 8.5 x 11 has top & bottom margins that fanfold doesn't.
IF you have set the # lines per page correctly, you can COPY a .L file to PRN
even for single sheets. The paper-out will automatically pause for inserting
the next sheet.
LINES PER PAGE:
At 7 (7.5) LPI:
Normal Paper single sheets = 71 lines per page (I've been using 70)
Long Paper single sheets = 92 lines per page
maybe 93 if you back up
paper after inserting.
WIDTH:
For 8.5" paper, Elite12 can fit 99.
Long Paper:
8" = right margin 94 (Elite)
8.25" = right margin 96 (Elite)
PAGECREEP:
The Toshiba printer has developed a page-creep such that the top of page as
reached by FF creeps down the page by about 1/2" per 30 pages. When printing
on fanfold (continuous) paper, you can use the pagecreep setting to compensate
for this.
For hole-punched, allow at least 1/3" left margin, although for most program
listings, the holes don't remove much.
USING EDITORS WITH .L FILES:
Borland IDE No Adds and removes tab chars
MSWord No Strips form feeds
Edit ? Looks ok for control codes, but adds CRLF after last FF
Edlin Yes I think this is what I used to use regularly.
But it does add CRLFCRLF^Z at end of file.
See.exe Yes Best choice. Control codes ok, and no extra chars.
|
|
|
|
|
|