|
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 Payments Humor Music |
Text file format and print program for Visual C++Microsoft Visual C++ CLR console application for formatted printing of 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. It is intended for a "text-only" type printer for which print modes can be changed by embedding escape codes in the character stream. It has escape codes for my Toshiba P351 printer defined in it, but they 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. It makes reference to an optional textual "help" file in which you can keep instructions to yourself about which settings to use with which printers or paper sizes, etc. Sample text for that file (CPrint.txt) is on the page for the earlier Borland C++ 4.0 version from which this was converted. A few lines of sample output are at the bottom of the page for the GNU g++ version for Linux. |
/* cprint.cpp 6-3-07
MS Visual C++ CLR console application version
Copyright (C)1996-2000, 2002, 2007 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 3 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 P351 printer in various
formats. Should be easily modified for other text only printers.
Needed because CLIST.EXE 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.
------
TO DO:
DoubleStrike option added 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 "stdafx.h"
#include "..\..\mylib\mylib.cpp"
#include "..\..\mylib\filearay.cpp"
#define ESC (27)
using namespace System;
using namespace std;
int main(array<System::String ^> ^args)
{
//----------------------------------------------------------------------------
// 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;
// Quit if no command line arguments.
// Apparently, the program's exe is no longer passed as argv[0] (args[0]).
// The first array element actually is the first command line argument.
if(args->Length < 1)
{
Console::Write("No files to process. Press <Enter> to close the window...");
Console::ReadLine();
return(0);
}
//----------------------------------------------------------------------------
// GET THE FILES TO PROCESS, AND DISPLAY THE LIST.
// (During testing in the VCPP IDE, Go to Project > Properties,
// and add a file name (e.g. TEST.CPP) as the Command Arguments.)
FileArray FileList;
for(int i = 0 ; i < args->Length ; i++)
{
string s;
SystemStringToBasicString(args[i],s);
FileList.AddFile(toupper(s));
}
cout << "Files to be printed:" << endl << FileList << endl;
presskey();
//----------------------------------------------------------------------------
// VARIABLES
// printer initialization strings for Toshiba P351.
// 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; // default Toshiba P351 font
uint tabexpand = 5;
int leftmargin = 0;
uint rightmargin = 80;
uint 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();
toupper(infilename); // string search fns used below are now case sensitive
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(stringcontains(infilename,"."))
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(stringcontains(outfilename,".")) // strip extension
outfilename.erase(outfilename.rfind("."));
outfilename += ".L"; // add .L
ifstream testfile(outfilename.c_str()); // .L file already exists?
if(testfile)
if(!yesno("Output file " + outfilename + " already exists. Overwrite"))
{
cout << endl << "\aWill not create listing for " << infilename << "." << endl;
presskey();
continue;
}
testfile.close();
ofstream outfile(outfilename.c_str());
//----------------------------------------------------------------------------
// 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.clear(); // MUST clear flags before reusing infile.
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(!stringcontains(string("0123456789abcdh"),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':
// This opens a text file in which you can keep any reference notes
// you want concerning your various printers, their settings, etc.
_flushall(); //Help says: "You must _flushall() before calling system()"
system("notepad.exe 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 the path and use the 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; // time and date (we need its length)
SystemStringToBasicString(DateTime::Now.ToString(),timenow);
// the 10 below allows for " Page ###"
int padding = rightmargin - lefttextmargin - infilename.length() - timenow.length() - 10;
padding = max(padding,0); // negative padding not allowed
string title = string(lefttextmargin,' ') + infilename + string(padding,' ')
+ timenow + " Page ";
//----------------------------------------------------------------------------
// SENDING DATA TO OUTPUT FILE STARTS 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
uint 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(stringcontains(fileline,'\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 // if it wasn't tab, just append the char
temp += fileline[i];
fileline = temp; // we built it in temp; copy back to fileline.
}
//----------------------------------------------------------------------------
// ADD LINE NUMBER, IF NEEDED
if(linenumbers)
{
char buf[10]; // format the line number
ostrstream(buf,sizeof(buf)) << setw(4) << logline << " " << ends;
fileline.insert(0,buf); // and insert it at beginning
logline++; // increment LOGICAL line count
}
fileline.insert(0,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 they 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.insert(0,string(lefttextmargin,' ')); // align with text
fileline.erase(rightmargin); // truncate current line,
}
outfile << fileline << endl; // and print it
physline++;
fileline = nextline; // loop to process overrun.
}
while(fileline.length());
} // while(getline)
outfile << "\f"; // form-feed 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())
Console::Write("Done. Press <Enter> to close the window...");
Console::ReadLine();
return 0;
}
|
|
|
|
|
|
Copyright ©2010 Steven Whitney. Last modified Thu 10/21/2010 02:08:03 -0700. |
||