mirror of
https://github.com/fadden/ciderpress.git
synced 2024-12-22 20:29:51 +00:00
822 lines
21 KiB
C++
822 lines
21 KiB
C++
|
/*
|
||
|
* CiderPress
|
||
|
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
|
||
|
* See the file LICENSE for distribution terms.
|
||
|
*/
|
||
|
/*
|
||
|
* Support for printing.
|
||
|
*/
|
||
|
#include "stdafx.h"
|
||
|
#include "Print.h"
|
||
|
#include "Main.h"
|
||
|
#include "Preferences.h"
|
||
|
|
||
|
|
||
|
/*
|
||
|
* ==========================================================================
|
||
|
* PrintStuff
|
||
|
* ==========================================================================
|
||
|
*/
|
||
|
|
||
|
/*static*/ const char* PrintStuff::kCourierNew = _T("Courier New");
|
||
|
/*static*/ const char* PrintStuff::kTimesNewRoman = _T("Times New Roman");
|
||
|
|
||
|
/*
|
||
|
* Set up various values.
|
||
|
*/
|
||
|
void
|
||
|
PrintStuff::InitBasics(CDC* pDC)
|
||
|
{
|
||
|
ASSERT(pDC != nil);
|
||
|
ASSERT(fpDC == nil);
|
||
|
|
||
|
fpDC = pDC;
|
||
|
|
||
|
/* make sure we're in MM_TEXT mode */
|
||
|
pDC->SetMapMode(MM_TEXT);
|
||
|
|
||
|
/* get device capabilities; logPixels is e.g. 300 */
|
||
|
fVertRes = pDC->GetDeviceCaps(VERTRES);
|
||
|
fHorzRes = pDC->GetDeviceCaps(HORZRES);
|
||
|
fLogPixelsX = pDC->GetDeviceCaps(LOGPIXELSX);
|
||
|
fLogPixelsY = pDC->GetDeviceCaps(LOGPIXELSY);
|
||
|
WMSG4("+++ logPixelsX=%d logPixelsY=%d fHorzRes=%d fVertRes=%d\n",
|
||
|
fLogPixelsX, fLogPixelsY, fHorzRes, fVertRes);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Create a new font, based on the number of lines per page we need.
|
||
|
*/
|
||
|
void
|
||
|
PrintStuff::CreateFontByNumLines(CFont* pFont, int numLines)
|
||
|
{
|
||
|
ASSERT(pFont != nil);
|
||
|
ASSERT(numLines > 0);
|
||
|
ASSERT(fpDC != nil);
|
||
|
|
||
|
/* required height */
|
||
|
int reqCharHeight;
|
||
|
reqCharHeight = (fVertRes + numLines/2) / numLines;
|
||
|
|
||
|
/* magic fudge factor */
|
||
|
int fudge = reqCharHeight / 24;
|
||
|
WMSG2(" Reducing reqCharHeight from %d to %d\n",
|
||
|
reqCharHeight, reqCharHeight - fudge);
|
||
|
reqCharHeight -= fudge;
|
||
|
|
||
|
pFont->CreateFont(reqCharHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0,
|
||
|
DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS,
|
||
|
DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, kTimesNewRoman);
|
||
|
/*fpOldFont =*/ fpDC->SelectObject(pFont);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Returns the width of the string.
|
||
|
*/
|
||
|
int
|
||
|
PrintStuff::StringWidth(const CString& str)
|
||
|
{
|
||
|
CSize size;
|
||
|
size = fpDC->GetTextExtent(str);
|
||
|
return size.cx;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Trim a string to the specified number of pixels. If it's too large,
|
||
|
* ellipsis will be added on the left or right.
|
||
|
*/
|
||
|
int
|
||
|
PrintStuff::TrimString(CString* pStr, int width, bool addOnLeft)
|
||
|
{
|
||
|
static const char* kEllipsis = "...";
|
||
|
CString newStr;
|
||
|
int strWidth;
|
||
|
CSize size;
|
||
|
|
||
|
size = fpDC->GetTextExtent(kEllipsis);
|
||
|
|
||
|
if (width < size.cx) {
|
||
|
ASSERT(false);
|
||
|
return width;
|
||
|
}
|
||
|
|
||
|
newStr = *pStr;
|
||
|
|
||
|
/*
|
||
|
* Do a linear search. This would probably be better served with a
|
||
|
* binary search or at least a good textmetric-based guess.
|
||
|
*/
|
||
|
strWidth = StringWidth(newStr);
|
||
|
while (strWidth > width) {
|
||
|
if (pStr->IsEmpty()) {
|
||
|
ASSERT(false);
|
||
|
return width;
|
||
|
}
|
||
|
if (addOnLeft) {
|
||
|
*pStr = pStr->Right(pStr->GetLength() -1);
|
||
|
newStr = kEllipsis + *pStr;
|
||
|
} else {
|
||
|
*pStr = pStr->Left(pStr->GetLength() -1);
|
||
|
newStr = *pStr + kEllipsis;
|
||
|
}
|
||
|
|
||
|
if (!addOnLeft) {
|
||
|
WMSG1("Now trying '%s'\n", (LPCTSTR) newStr);
|
||
|
}
|
||
|
strWidth = StringWidth(newStr);
|
||
|
}
|
||
|
|
||
|
*pStr = newStr;
|
||
|
return strWidth;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* ==========================================================================
|
||
|
* PrintContentList
|
||
|
* ==========================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Calculate some constant values.
|
||
|
*/
|
||
|
void
|
||
|
PrintContentList::Setup(CDC* pDC, CWnd* pParent)
|
||
|
{
|
||
|
/* init base class */
|
||
|
InitBasics(pDC);
|
||
|
|
||
|
/* init our stuff */
|
||
|
CreateFontByNumLines(&fPrintFont, kTargetLinesPerPage);
|
||
|
|
||
|
fpParentWnd = pParent;
|
||
|
|
||
|
/* compute text metrics */
|
||
|
TEXTMETRIC metrics;
|
||
|
pDC->GetTextMetrics(&metrics);
|
||
|
fCharHeight = metrics.tmHeight + metrics.tmExternalLeading;
|
||
|
fLinesPerPage = fVertRes / fCharHeight;
|
||
|
|
||
|
WMSG2("fVertRes=%d, fCharHeight=%d\n", fVertRes, fCharHeight);
|
||
|
|
||
|
/* set up our slightly reduced lines per page */
|
||
|
ASSERT(fLinesPerPage > kHeaderLines+1);
|
||
|
fCLLinesPerPage = fLinesPerPage - kHeaderLines;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Compute the number of pages in fpContentList.
|
||
|
*/
|
||
|
void
|
||
|
PrintContentList::CalcNumPages(void)
|
||
|
{
|
||
|
/* set up our local goodies */
|
||
|
ASSERT(fpContentList != nil);
|
||
|
int numLines = fpContentList->GetItemCount();
|
||
|
ASSERT(numLines > 0);
|
||
|
|
||
|
fNumPages = (numLines + fCLLinesPerPage -1) / fCLLinesPerPage;
|
||
|
ASSERT(fNumPages > 0);
|
||
|
|
||
|
WMSG3("Using numLines=%d, fNumPages=%d, fCLLinesPerPage=%d\n",
|
||
|
numLines, fNumPages, fCLLinesPerPage);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Initiate printing of the specified list to the specified DC.
|
||
|
*
|
||
|
* Returns 0 if all went well, nonzero on cancellation or failure.
|
||
|
*/
|
||
|
int
|
||
|
PrintContentList::Print(const ContentList* pContentList)
|
||
|
{
|
||
|
fpContentList = pContentList;
|
||
|
CalcNumPages();
|
||
|
|
||
|
fFromPage = 1;
|
||
|
fToPage = fNumPages;
|
||
|
return StartPrint();
|
||
|
}
|
||
|
int
|
||
|
PrintContentList::Print(const ContentList* pContentList, int fromPage, int toPage)
|
||
|
{
|
||
|
fpContentList = pContentList;
|
||
|
CalcNumPages();
|
||
|
|
||
|
fFromPage = fromPage;
|
||
|
fToPage = toPage;
|
||
|
return StartPrint();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Kick off the print job.
|
||
|
*/
|
||
|
int
|
||
|
PrintContentList::StartPrint(void)
|
||
|
{
|
||
|
MainWindow* pMain = (MainWindow*)::AfxGetMainWnd();
|
||
|
BOOL bres;
|
||
|
int jobID;
|
||
|
int result = -1;
|
||
|
|
||
|
ASSERT(fFromPage >= 1);
|
||
|
ASSERT(fToPage <= fNumPages);
|
||
|
|
||
|
// clear the abort flag
|
||
|
pMain->SetAbortPrinting(false);
|
||
|
|
||
|
// obstruct input to the main window
|
||
|
fpParentWnd->EnableWindow(FALSE);
|
||
|
|
||
|
// create a print-cancel dialog
|
||
|
// PrintCancelDialog* pPCD = new PrintCancelDialog;
|
||
|
CancelDialog* pPCD = new CancelDialog;
|
||
|
bres = pPCD->Create(&pMain->fAbortPrinting, IDD_PRINT_CANCEL, fpParentWnd);
|
||
|
if (bres == FALSE) {
|
||
|
WMSG0("WARNING: PrintCancelDialog init failed\n");
|
||
|
} else {
|
||
|
fpDC->SetAbortProc(pMain->PrintAbortProc);
|
||
|
}
|
||
|
|
||
|
fDocTitle = pMain->GetPrintTitle();
|
||
|
|
||
|
// set up the print job
|
||
|
CString printTitle;
|
||
|
printTitle.LoadString(IDS_PRINT_CL_JOB_TITLE);
|
||
|
DOCINFO di;
|
||
|
::ZeroMemory(&di, sizeof(DOCINFO));
|
||
|
di.cbSize = sizeof(DOCINFO);
|
||
|
di.lpszDocName = printTitle;
|
||
|
|
||
|
jobID = fpDC->StartDoc(&di);
|
||
|
if (jobID <= 0) {
|
||
|
WMSG0("Got invalid jobID from StartDoc\n");
|
||
|
goto bail;
|
||
|
}
|
||
|
WMSG1("Got jobID=%d\n", jobID);
|
||
|
|
||
|
// do the printing
|
||
|
if (DoPrint() != 0) {
|
||
|
WMSG0("Printing was aborted\n");
|
||
|
fpDC->AbortDoc();
|
||
|
} else {
|
||
|
WMSG0("Printing was successful\n");
|
||
|
fpDC->EndDoc();
|
||
|
result = 0;
|
||
|
}
|
||
|
|
||
|
bail:
|
||
|
// destroy print-cancel dialog and restore main window
|
||
|
fpParentWnd->EnableWindow(TRUE);
|
||
|
//fpParentWnd->SetActiveWindow();
|
||
|
if (pPCD != nil)
|
||
|
pPCD->DestroyWindow();
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Print all pages.
|
||
|
*
|
||
|
* Returns 0 on success, nonzero on failure.
|
||
|
*/
|
||
|
int
|
||
|
PrintContentList::DoPrint(void)
|
||
|
{
|
||
|
WMSG2("Printing from page=%d to page=%d\n", fFromPage, fToPage);
|
||
|
|
||
|
for (int page = fFromPage; page <= fToPage; page++) {
|
||
|
if (fpDC->StartPage() <= 0) {
|
||
|
WMSG0("StartPage returned <= 0, returning -1\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
DoPrintPage(page);
|
||
|
|
||
|
// delay so we can test "cancel" button
|
||
|
// {
|
||
|
// MainWindow* pMain = (MainWindow*)::AfxGetMainWnd();
|
||
|
// pMain->EventPause(1000);
|
||
|
// }
|
||
|
|
||
|
|
||
|
if (fpDC->EndPage() <= 0) {
|
||
|
WMSG0("EndPage returned <= 0, returning -1\n");
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Print page N of the content list, where N is a 1-based count.
|
||
|
*/
|
||
|
void
|
||
|
PrintContentList::DoPrintPage(int page)
|
||
|
{
|
||
|
/*
|
||
|
* Column widths, on an arbitrary scale. These will be
|
||
|
* scaled appropriately for the page resolution.
|
||
|
*/
|
||
|
static const struct {
|
||
|
const char* name;
|
||
|
int width;
|
||
|
bool rightJust;
|
||
|
} kColumnWidths[kNumVisibleColumns] = {
|
||
|
{ "Pathname", 250, false }, // 200
|
||
|
{ "Type", 40, false }, // 44
|
||
|
{ "Auxtype", 47, false }, // 42
|
||
|
{ "Mod Date", 96, false }, // 99
|
||
|
{ "Format", 52, false }, // 54
|
||
|
{ "Size", 55, true }, // 60
|
||
|
{ "Ratio", 40, true }, // 41
|
||
|
{ "Packed", 55, true }, // 60
|
||
|
{ "Access", 39, false }, // 53 upper, 45 lower
|
||
|
};
|
||
|
const int kBorderWidth = 3;
|
||
|
|
||
|
/* normalize */
|
||
|
float widthMult;
|
||
|
int totalWidth;
|
||
|
|
||
|
totalWidth = 0;
|
||
|
for (int i = 0; i < NELEM(kColumnWidths); i++)
|
||
|
totalWidth += kColumnWidths[i].width;
|
||
|
|
||
|
widthMult = (float) fHorzRes / totalWidth;
|
||
|
WMSG3("totalWidth=%d, fHorzRes=%d, mult=%.3f\n",
|
||
|
totalWidth, fHorzRes, widthMult);
|
||
|
|
||
|
/*
|
||
|
* Calculate some goodies.
|
||
|
*/
|
||
|
int start, end;
|
||
|
start = (page-1) * fCLLinesPerPage;
|
||
|
end = start + fCLLinesPerPage;
|
||
|
if (end >= fpContentList->GetItemCount())
|
||
|
end = fpContentList->GetItemCount()-1;
|
||
|
|
||
|
int offset, nextOffset, cellWidth, border;
|
||
|
border = (int) (kBorderWidth * widthMult);
|
||
|
|
||
|
/*
|
||
|
* Print page header.
|
||
|
*/
|
||
|
fpDC->TextOut(0, 1 * fCharHeight, fDocTitle);
|
||
|
CString pageNum;
|
||
|
pageNum.Format("Page %d/%d", page, fNumPages);
|
||
|
int pageNumWidth = StringWidth(pageNum);
|
||
|
fpDC->TextOut(fHorzRes - pageNumWidth, 1 * fCharHeight, pageNum);
|
||
|
|
||
|
/*
|
||
|
* Print data.
|
||
|
*/
|
||
|
for (int row = -1; row < fCLLinesPerPage && start + row <= end; row++) {
|
||
|
CString text;
|
||
|
offset = 0;
|
||
|
|
||
|
for (int col = 0; col < kNumVisibleColumns; col++) {
|
||
|
cellWidth = (int) ((float)kColumnWidths[col].width * widthMult);
|
||
|
nextOffset = offset + cellWidth;
|
||
|
if (col != kNumVisibleColumns-1)
|
||
|
cellWidth -= border;
|
||
|
if (col == 0)
|
||
|
cellWidth -= (border*2); // extra border on pathname
|
||
|
|
||
|
int yOffset;
|
||
|
if (row == -1) {
|
||
|
text = kColumnWidths[col].name;
|
||
|
yOffset = (row+kHeaderLines-1) * fCharHeight;
|
||
|
} else {
|
||
|
text = fpContentList->GetItemText(start + row, col);
|
||
|
yOffset = (row+kHeaderLines) * fCharHeight;
|
||
|
}
|
||
|
|
||
|
int strWidth;
|
||
|
strWidth = TrimString(&text, cellWidth, col == 0);
|
||
|
|
||
|
if (kColumnWidths[col].rightJust)
|
||
|
fpDC->TextOut((offset + cellWidth) - strWidth, yOffset, text);
|
||
|
else
|
||
|
fpDC->TextOut(offset, yOffset, text);
|
||
|
|
||
|
offset = nextOffset;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Add some fancy lines.
|
||
|
*/
|
||
|
CPen penBlack(PS_SOLID, 1, RGB(0, 0, 0));
|
||
|
CPen* pOldPen = fpDC->SelectObject(&penBlack);
|
||
|
fpDC->MoveTo(0, (int) (fCharHeight * (kHeaderLines - 0.5)));
|
||
|
fpDC->LineTo(fHorzRes, (int) (fCharHeight * (kHeaderLines - 0.5)));
|
||
|
|
||
|
//fpDC->MoveTo(0, 0);
|
||
|
//fpDC->LineTo(fHorzRes, fVertRes);
|
||
|
//fpDC->MoveTo(fHorzRes-1, 0);
|
||
|
//fpDC->LineTo(0, fVertRes);
|
||
|
|
||
|
fpDC->SelectObject(pOldPen);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* ==========================================================================
|
||
|
* PrintRichEdit
|
||
|
* ==========================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Calculate some constant values.
|
||
|
*/
|
||
|
void
|
||
|
PrintRichEdit::Setup(CDC* pDC, CWnd* pParent)
|
||
|
{
|
||
|
/* preflighting can cause this to be initialized twice */
|
||
|
fpDC = nil;
|
||
|
|
||
|
/* init base class */
|
||
|
InitBasics(pDC);
|
||
|
|
||
|
if (!fInitialized) {
|
||
|
/*
|
||
|
* Find a nice font for the title area.
|
||
|
*/
|
||
|
const int kPointSize = 10;
|
||
|
int fontHeight;
|
||
|
BOOL result;
|
||
|
|
||
|
fontHeight = -MulDiv(kPointSize, fLogPixelsY, 72);
|
||
|
|
||
|
result = fTitleFont.CreateFont(fontHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0,
|
||
|
DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS,
|
||
|
DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, kTimesNewRoman);
|
||
|
ASSERT(result); // everybody has Times New Roman
|
||
|
}
|
||
|
|
||
|
fpParentWnd = pParent;
|
||
|
fInitialized = true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Pre-flight the print process to get the number of pages.
|
||
|
*/
|
||
|
int
|
||
|
PrintRichEdit::PrintPreflight(CRichEditCtrl* pREC, int* pNumPages)
|
||
|
{
|
||
|
fStartChar = 0;
|
||
|
fEndChar = -1;
|
||
|
fStartPage = 0;
|
||
|
fEndPage = -1;
|
||
|
return StartPrint(pREC, "(test)", pNumPages, false);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Print all pages.
|
||
|
*/
|
||
|
int
|
||
|
PrintRichEdit::PrintAll(CRichEditCtrl* pREC, const char* title)
|
||
|
{
|
||
|
fStartChar = 0;
|
||
|
fEndChar = -1;
|
||
|
fStartPage = 0;
|
||
|
fEndPage = -1;
|
||
|
return StartPrint(pREC, title, nil, true);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Print a range of pages.
|
||
|
*/
|
||
|
int
|
||
|
PrintRichEdit::PrintPages(CRichEditCtrl* pREC, const char* title,
|
||
|
int startPage, int endPage)
|
||
|
{
|
||
|
fStartChar = 0;
|
||
|
fEndChar = -1;
|
||
|
fStartPage = startPage;
|
||
|
fEndPage = endPage;
|
||
|
return StartPrint(pREC, title, nil, true);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Print the selected area.
|
||
|
*/
|
||
|
int
|
||
|
PrintRichEdit::PrintSelection(CRichEditCtrl* pREC, const char* title,
|
||
|
long startChar, long endChar)
|
||
|
{
|
||
|
fStartChar = startChar;
|
||
|
fEndChar = endChar;
|
||
|
fStartPage = 0;
|
||
|
fEndPage = -1;
|
||
|
return StartPrint(pREC, title, nil, true);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Start the printing process by posting a print-cancel dialog.
|
||
|
*/
|
||
|
int
|
||
|
PrintRichEdit::StartPrint(CRichEditCtrl* pREC, const char* title,
|
||
|
int* pNumPages, bool doPrint)
|
||
|
{
|
||
|
CancelDialog* pPCD = nil;
|
||
|
MainWindow* pMain = (MainWindow*)::AfxGetMainWnd();
|
||
|
int result;
|
||
|
|
||
|
/* set up the print cancel dialog */
|
||
|
if (doPrint) {
|
||
|
BOOL bres;
|
||
|
|
||
|
/* disable main UI */
|
||
|
fpParentWnd->EnableWindow(FALSE);
|
||
|
|
||
|
pPCD = new CancelDialog;
|
||
|
bres = pPCD->Create(&pMain->fAbortPrinting, IDD_PRINT_CANCEL,
|
||
|
fpParentWnd);
|
||
|
|
||
|
/* set up the DC's print abort callback */
|
||
|
if (bres != FALSE)
|
||
|
fpDC->SetAbortProc(pMain->PrintAbortProc);
|
||
|
}
|
||
|
|
||
|
result = DoPrint(pREC, title, pNumPages, doPrint);
|
||
|
|
||
|
if (doPrint) {
|
||
|
fpParentWnd->EnableWindow(TRUE);
|
||
|
if (pPCD != nil)
|
||
|
pPCD->DestroyWindow();
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Do some prep work before printing.
|
||
|
*/
|
||
|
void
|
||
|
PrintRichEdit::PrintPrep(FORMATRANGE* pFR)
|
||
|
{
|
||
|
CFont* pOldFont;
|
||
|
|
||
|
/* make sure we're in MM_TEXT mode */
|
||
|
fpDC->SetMapMode(MM_TEXT);
|
||
|
|
||
|
TEXTMETRIC metrics;
|
||
|
pOldFont = fpDC->SelectObject(&fTitleFont);
|
||
|
fpDC->GetTextMetrics(&metrics);
|
||
|
fCharHeight = metrics.tmHeight + metrics.tmExternalLeading;
|
||
|
fpDC->SelectObject(pOldFont);
|
||
|
//WMSG1("CHAR HEIGHT is %d\n", fCharHeight);
|
||
|
|
||
|
/* compute fLeftMargin and fRightMargin */
|
||
|
ComputeMargins();
|
||
|
|
||
|
/*
|
||
|
* Set up the FORMATRANGE values. The Rich Edit stuff likes to have
|
||
|
* measurements in TWIPS, whereas the printer is using DC pixel
|
||
|
* values. fLogPixels_ tells us how many pixels per inch.
|
||
|
*/
|
||
|
memset(pFR, 0, sizeof(FORMATRANGE));
|
||
|
pFR->hdc = pFR->hdcTarget = fpDC->m_hDC;
|
||
|
|
||
|
/*
|
||
|
* Set frame for printable area, in TWIPS. The printer DC will set
|
||
|
* its own "reasonable" margins, so the area here is not the entire
|
||
|
* sheet of paper.
|
||
|
*/
|
||
|
pFR->rcPage.left = pFR->rcPage.top = 0;
|
||
|
pFR->rcPage.right = (fHorzRes * kTwipsPerInch) / fLogPixelsX;
|
||
|
pFR->rcPage.bottom = (fVertRes * kTwipsPerInch) / fLogPixelsY;
|
||
|
|
||
|
long topOffset = (long) ((fCharHeight * 1.5 * kTwipsPerInch) / fLogPixelsY);
|
||
|
pFR->rc.top = pFR->rcPage.top + topOffset;
|
||
|
pFR->rc.bottom = pFR->rcPage.bottom;
|
||
|
pFR->rc.left = pFR->rcPage.left + (fLeftMargin * kTwipsPerInch) / fLogPixelsX;
|
||
|
pFR->rc.right = pFR->rcPage.right - (fRightMargin * kTwipsPerInch) / fLogPixelsX;
|
||
|
|
||
|
WMSG2("PRINTABLE AREA is %d wide x %d high (twips)\n",
|
||
|
pFR->rc.right - pFR->rc.left, pFR->rc.bottom - pFR->rc.top);
|
||
|
WMSG2("FRAME is %d wide x %d high (twips)\n",
|
||
|
pFR->rcPage.right - pFR->rcPage.left, pFR->rcPage.bottom - pFR->rcPage.top);
|
||
|
|
||
|
pFR->chrg.cpMin = fStartChar;
|
||
|
pFR->chrg.cpMax = fEndChar;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Compute the size of the left and right margins, based on the width of 80
|
||
|
* characters of 10-point Courier New on the current printer.
|
||
|
*
|
||
|
* Sets fLeftMargin and fRightMargin, in printer DC pixels.
|
||
|
*/
|
||
|
void
|
||
|
PrintRichEdit::ComputeMargins(void)
|
||
|
{
|
||
|
CFont tmpFont;
|
||
|
CFont* pOldFont;
|
||
|
int char80width, fontHeight, totalMargin;
|
||
|
BOOL result;
|
||
|
|
||
|
fontHeight = -MulDiv(10, fLogPixelsY, 72);
|
||
|
|
||
|
result = tmpFont.CreateFont(fontHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0,
|
||
|
DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS,
|
||
|
DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, kCourierNew);
|
||
|
ASSERT(result);
|
||
|
|
||
|
pOldFont = fpDC->SelectObject(&tmpFont);
|
||
|
// in theory we could compute one 'X' * 80; this seems more reliable
|
||
|
char str[81];
|
||
|
for (int i = 0; i < 80; i++)
|
||
|
str[i] = 'X';
|
||
|
str[i] = '\0';
|
||
|
char80width = StringWidth(str);
|
||
|
fpDC->SelectObject(pOldFont);
|
||
|
|
||
|
//WMSG1("char80 string width=%d\n", char80width);
|
||
|
|
||
|
/*
|
||
|
* If there's not enough room on the page, set the margins to zero.
|
||
|
* If the margins required exceed two inches, just set the margin
|
||
|
* to one inch on either side.
|
||
|
*/
|
||
|
totalMargin = fHorzRes - char80width;
|
||
|
if (totalMargin < 0) {
|
||
|
WMSG0(" Page not wide enough, setting margins to zero\n");
|
||
|
fLeftMargin = fRightMargin = 0;
|
||
|
} else if (totalMargin > fLogPixelsX * 2) {
|
||
|
WMSG0(" Page too wide, setting margins to 1 inch\n");
|
||
|
fLeftMargin = fRightMargin = fLogPixelsX;
|
||
|
} else {
|
||
|
// try to get leftMargin equal to 1/2"
|
||
|
fLeftMargin = totalMargin / 2;
|
||
|
if (fLeftMargin > fLogPixelsX / 2)
|
||
|
fLeftMargin = fLogPixelsX / 2;
|
||
|
fRightMargin = totalMargin - fLeftMargin -1;
|
||
|
WMSG3(" +++ Margins (in %d pixels/inch) are left=%ld right=%ld\n",
|
||
|
fLogPixelsX, fLeftMargin, fRightMargin);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Send the contents of the rich edit control to the printer DC.
|
||
|
*
|
||
|
* This was derived from Microsft KB article 129860.
|
||
|
*/
|
||
|
int
|
||
|
PrintRichEdit::DoPrint(CRichEditCtrl* pREC, const char* title,
|
||
|
int* pNumPages, bool doPrint)
|
||
|
{
|
||
|
FORMATRANGE fr;
|
||
|
DOCINFO di;
|
||
|
long textLength, textPrinted, lastTextPrinted;
|
||
|
int pageNum;
|
||
|
|
||
|
WMSG2("DoPrint: title='%s' doPrint=%d\n", title, doPrint);
|
||
|
WMSG4(" startChar=%d endChar=%d startPage=%d endPage=%d\n",
|
||
|
fStartChar, fEndChar, fStartPage, fEndPage);
|
||
|
|
||
|
/*
|
||
|
* Get the title font and fill out the FORMATRANGE structure.
|
||
|
*/
|
||
|
PrintPrep(&fr);
|
||
|
|
||
|
/* fill out a DOCINFO */
|
||
|
memset(&di, 0, sizeof(di));
|
||
|
di.cbSize = sizeof(DOCINFO);
|
||
|
di.lpszDocName = title;
|
||
|
|
||
|
if (doPrint)
|
||
|
fpDC->StartDoc(&di);
|
||
|
|
||
|
/*
|
||
|
* Here's the really strange part of the affair. The GetTextLength call
|
||
|
* shown in the MSFT KB article doesn't return the correct number of
|
||
|
* characters. The CRichEditView code in MFC uses a GetTextLengthEx
|
||
|
* call, which is documented as being part of the CRichEditCtrl but
|
||
|
* doesn't appear in the header files. Call it the hard way.
|
||
|
*
|
||
|
* If you print a "raw" file with carriage returns, and you use version
|
||
|
* 5.30.23.1200 of "riched20.dll", you get "9609" from GetTextLength and
|
||
|
* "9528" from GetTextLengthEx when the document's length is 9528 and
|
||
|
* there are 81 carriage returns. The value you want to use is 9528,
|
||
|
* because the print code doesn't double-up the count on CRs.
|
||
|
*
|
||
|
* If instead you use version 5.30.23.1215, you get the same answer
|
||
|
* from both calls, and printing works fine.
|
||
|
*
|
||
|
* GetTextLengthEx is part of "riched20.dll". Win9x uses "riched32.dll",
|
||
|
* which doesn't support the call.
|
||
|
*/
|
||
|
#ifdef _UNICODE
|
||
|
# error "should be code page 1200, not CP_ACP"
|
||
|
#endif
|
||
|
GETTEXTLENGTHEX exLenReq;
|
||
|
long basicTextLength, extdTextLength;
|
||
|
basicTextLength = pREC->GetTextLength();
|
||
|
exLenReq.flags = GTL_PRECISE | GTL_NUMCHARS;
|
||
|
exLenReq.codepage = CP_ACP;
|
||
|
extdTextLength = (long)::SendMessage(pREC->m_hWnd, EM_GETTEXTLENGTHEX,
|
||
|
(WPARAM) &exLenReq, (LPARAM) NULL);
|
||
|
WMSG2("RichEdit text length: std=%ld extd=%ld\n",
|
||
|
basicTextLength, extdTextLength);
|
||
|
|
||
|
if (fEndChar == -1) {
|
||
|
if (extdTextLength > 0)
|
||
|
textLength = extdTextLength;
|
||
|
else
|
||
|
textLength = basicTextLength;
|
||
|
} else
|
||
|
textLength = fEndChar - fStartChar;
|
||
|
|
||
|
WMSG1(" +++ starting while loop, textLength=%ld\n", textLength);
|
||
|
pageNum = 0;
|
||
|
lastTextPrinted = -1;
|
||
|
do {
|
||
|
bool skipPage = false;
|
||
|
pageNum++;
|
||
|
WMSG1(" +++ while loop: pageNum is %d\n", pageNum);
|
||
|
|
||
|
if (fEndPage > 0) {
|
||
|
if (pageNum < fStartPage)
|
||
|
skipPage = true;
|
||
|
if (pageNum > fEndPage)
|
||
|
break; // out of while, ending print
|
||
|
}
|
||
|
|
||
|
if (doPrint && !skipPage) {
|
||
|
fpDC->StartPage();
|
||
|
|
||
|
CFont* pOldFont = fpDC->SelectObject(&fTitleFont);
|
||
|
fpDC->TextOut(0, 0 * fCharHeight, title);
|
||
|
CString pageNumStr;
|
||
|
pageNumStr.Format("Page %d", pageNum);
|
||
|
int pageNumWidth = StringWidth(pageNumStr);
|
||
|
fpDC->TextOut(fHorzRes - pageNumWidth, 0 * fCharHeight, pageNumStr);
|
||
|
fpDC->SelectObject(pOldFont);
|
||
|
|
||
|
CPen penBlack(PS_SOLID, 1, RGB(0, 0, 0));
|
||
|
CPen* pOldPen = fpDC->SelectObject(&penBlack);
|
||
|
int ycoord = (int) (fCharHeight * 1.25);
|
||
|
fpDC->MoveTo(0, ycoord-1);
|
||
|
fpDC->LineTo(fHorzRes, ycoord-1);
|
||
|
fpDC->MoveTo(0, ycoord);
|
||
|
fpDC->LineTo(fHorzRes, ycoord);
|
||
|
|
||
|
fpDC->SelectObject(pOldPen);
|
||
|
}
|
||
|
|
||
|
//WMSG1(" +++ calling FormatRange(%d)\n", doPrint && !skipPage);
|
||
|
//LogHexDump(&fr, sizeof(fr));
|
||
|
|
||
|
/* print a page full of RichEdit stuff */
|
||
|
textPrinted = pREC->FormatRange(&fr, doPrint && !skipPage);
|
||
|
WMSG1(" +++ returned from FormatRange (textPrinted=%d)\n",
|
||
|
textPrinted);
|
||
|
if (textPrinted <= lastTextPrinted) {
|
||
|
/* the earlier StartPage can't be undone, so we'll get an
|
||
|
extra blank page at the very end */
|
||
|
WMSG3("GLITCH: no new text printed (printed=%ld, last=%ld, len=%ld)\n",
|
||
|
textPrinted, lastTextPrinted, textLength);
|
||
|
pageNum--; // fix page count estimator
|
||
|
break;
|
||
|
}
|
||
|
lastTextPrinted = textPrinted;
|
||
|
|
||
|
// delay so we can test "cancel" button
|
||
|
//((MainWindow*)::AfxGetMainWnd())->EventPause(1000);
|
||
|
|
||
|
if (doPrint && !skipPage) {
|
||
|
if (fpDC->EndPage() <= 0) {
|
||
|
/* the "cancel" button was hit */
|
||
|
WMSG0("EndPage returned <= 0 (cancelled)\n");
|
||
|
fpDC->AbortDoc();
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (textPrinted < textLength) {
|
||
|
fr.chrg.cpMin = textPrinted;
|
||
|
fr.chrg.cpMax = fEndChar; // -1 if nothing selected
|
||
|
}
|
||
|
} while (textPrinted < textLength);
|
||
|
|
||
|
//WMSG0(" +++ calling FormatRange(nil, FALSE)\n");
|
||
|
pREC->FormatRange(nil, FALSE);
|
||
|
//WMSG0(" +++ returned from final FormatRange\n");
|
||
|
|
||
|
if (doPrint)
|
||
|
fpDC->EndDoc();
|
||
|
|
||
|
if (pNumPages != nil)
|
||
|
*pNumPages = pageNum;
|
||
|
|
||
|
WMSG1("Printing completed (textPrinted=%ld)\n", textPrinted);
|
||
|
|
||
|
return 0;
|
||
|
}
|