mirror of
https://github.com/fadden/ciderpress.git
synced 2024-11-26 17:49:21 +00:00
d42b9c6dc0
The static analyzer was annoyed that the return value from calls to CString::LoadString() was being ignored. This adds a wrapper function that checks the value and logs a failure message if the string can't be found.
738 lines
22 KiB
C++
738 lines
22 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 WCHAR PrintStuff::kCourierNew[] = L"Courier New";
|
|
/*static*/ const WCHAR PrintStuff::kTimesNewRoman[] = L"Times New Roman";
|
|
|
|
void PrintStuff::InitBasics(CDC* pDC)
|
|
{
|
|
ASSERT(pDC != NULL);
|
|
ASSERT(fpDC == NULL);
|
|
|
|
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);
|
|
LOGI("+++ logPixelsX=%d logPixelsY=%d fHorzRes=%d fVertRes=%d",
|
|
fLogPixelsX, fLogPixelsY, fHorzRes, fVertRes);
|
|
}
|
|
|
|
void PrintStuff::CreateFontByNumLines(CFont* pFont, int numLines)
|
|
{
|
|
ASSERT(pFont != NULL);
|
|
ASSERT(numLines > 0);
|
|
ASSERT(fpDC != NULL);
|
|
|
|
/* required height */
|
|
int reqCharHeight;
|
|
reqCharHeight = (fVertRes + numLines/2) / numLines;
|
|
|
|
/* magic fudge factor */
|
|
int fudge = reqCharHeight / 24;
|
|
LOGI(" Reducing reqCharHeight from %d to %d",
|
|
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);
|
|
}
|
|
|
|
int PrintStuff::StringWidth(const CString& str)
|
|
{
|
|
CSize size;
|
|
size = fpDC->GetTextExtent(str);
|
|
return size.cx;
|
|
}
|
|
|
|
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) {
|
|
LOGI("Now trying '%ls'", (LPCWSTR) newStr);
|
|
}
|
|
strWidth = StringWidth(newStr);
|
|
}
|
|
|
|
*pStr = newStr;
|
|
return strWidth;
|
|
}
|
|
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* PrintContentList
|
|
* ==========================================================================
|
|
*/
|
|
|
|
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;
|
|
|
|
LOGD("fVertRes=%d, fCharHeight=%d", fVertRes, fCharHeight);
|
|
|
|
/* set up our slightly reduced lines per page */
|
|
ASSERT(fLinesPerPage > kHeaderLines+1);
|
|
fCLLinesPerPage = fLinesPerPage - kHeaderLines;
|
|
}
|
|
|
|
void PrintContentList::CalcNumPages(void)
|
|
{
|
|
/* set up our local goodies */
|
|
ASSERT(fpContentList != NULL);
|
|
int numLines = fpContentList->GetItemCount();
|
|
ASSERT(numLines > 0);
|
|
|
|
fNumPages = (numLines + fCLLinesPerPage -1) / fCLLinesPerPage;
|
|
ASSERT(fNumPages > 0);
|
|
|
|
LOGD("Using numLines=%d, fNumPages=%d, fCLLinesPerPage=%d",
|
|
numLines, fNumPages, fCLLinesPerPage);
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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) {
|
|
LOGI("WARNING: PrintCancelDialog init failed");
|
|
} else {
|
|
fpDC->SetAbortProc(pMain->PrintAbortProc);
|
|
}
|
|
|
|
fDocTitle = pMain->GetPrintTitle();
|
|
|
|
// set up the print job
|
|
CString printTitle;
|
|
CheckedLoadString(&printTitle, 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) {
|
|
LOGI("Got invalid jobID from StartDoc");
|
|
goto bail;
|
|
}
|
|
LOGI("Got jobID=%d", jobID);
|
|
|
|
// do the printing
|
|
if (DoPrint() != 0) {
|
|
LOGI("Printing was aborted");
|
|
fpDC->AbortDoc();
|
|
} else {
|
|
LOGI("Printing was successful");
|
|
fpDC->EndDoc();
|
|
result = 0;
|
|
}
|
|
|
|
bail:
|
|
// destroy print-cancel dialog and restore main window
|
|
fpParentWnd->EnableWindow(TRUE);
|
|
//fpParentWnd->SetActiveWindow();
|
|
if (pPCD != NULL)
|
|
pPCD->DestroyWindow();
|
|
|
|
return result;
|
|
}
|
|
|
|
int PrintContentList::DoPrint(void)
|
|
{
|
|
LOGI("Printing from page=%d to page=%d", fFromPage, fToPage);
|
|
|
|
for (int page = fFromPage; page <= fToPage; page++) {
|
|
if (fpDC->StartPage() <= 0) {
|
|
LOGI("StartPage returned <= 0, returning -1");
|
|
return -1;
|
|
}
|
|
|
|
DoPrintPage(page);
|
|
|
|
// delay so we can test "cancel" button
|
|
// {
|
|
// MainWindow* pMain = (MainWindow*)::AfxGetMainWnd();
|
|
// pMain->EventPause(1000);
|
|
// }
|
|
|
|
|
|
if (fpDC->EndPage() <= 0) {
|
|
LOGI("EndPage returned <= 0, returning -1");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
LOGD("totalWidth=%d, fHorzRes=%d, mult=%.3f",
|
|
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(L"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
|
|
* ==========================================================================
|
|
*/
|
|
|
|
void PrintRichEdit::Setup(CDC* pDC, CWnd* pParent)
|
|
{
|
|
/* preflighting can cause this to be initialized twice */
|
|
fpDC = NULL;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
int PrintRichEdit::PrintPreflight(CRichEditCtrl* pREC, int* pNumPages)
|
|
{
|
|
fStartChar = 0;
|
|
fEndChar = -1;
|
|
fStartPage = 0;
|
|
fEndPage = -1;
|
|
return StartPrint(pREC, L"(test)", pNumPages, false);
|
|
}
|
|
|
|
int PrintRichEdit::PrintAll(CRichEditCtrl* pREC, const WCHAR* title)
|
|
{
|
|
fStartChar = 0;
|
|
fEndChar = -1;
|
|
fStartPage = 0;
|
|
fEndPage = -1;
|
|
return StartPrint(pREC, title, NULL, true);
|
|
}
|
|
|
|
int PrintRichEdit::PrintPages(CRichEditCtrl* pREC, const WCHAR* title,
|
|
int startPage, int endPage)
|
|
{
|
|
fStartChar = 0;
|
|
fEndChar = -1;
|
|
fStartPage = startPage;
|
|
fEndPage = endPage;
|
|
return StartPrint(pREC, title, NULL, true);
|
|
}
|
|
|
|
int PrintRichEdit::PrintSelection(CRichEditCtrl* pREC, const WCHAR* title,
|
|
long startChar, long endChar)
|
|
{
|
|
fStartChar = startChar;
|
|
fEndChar = endChar;
|
|
fStartPage = 0;
|
|
fEndPage = -1;
|
|
return StartPrint(pREC, title, NULL, true);
|
|
}
|
|
|
|
int PrintRichEdit::StartPrint(CRichEditCtrl* pREC, const WCHAR* title,
|
|
int* pNumPages, bool doPrint)
|
|
{
|
|
CancelDialog* pPCD = NULL;
|
|
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 != NULL)
|
|
pPCD->DestroyWindow();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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);
|
|
//LOGI("CHAR HEIGHT is %d", 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;
|
|
|
|
LOGI("PRINTABLE AREA is %d wide x %d high (twips)",
|
|
pFR->rc.right - pFR->rc.left, pFR->rc.bottom - pFR->rc.top);
|
|
LOGI("FRAME is %d wide x %d high (twips)",
|
|
pFR->rcPage.right - pFR->rcPage.left, pFR->rcPage.bottom - pFR->rcPage.top);
|
|
|
|
pFR->chrg.cpMin = fStartChar;
|
|
pFR->chrg.cpMax = fEndChar;
|
|
}
|
|
|
|
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
|
|
WCHAR str[81];
|
|
for (int i = 0; i < 80; i++)
|
|
str[i] = 'X';
|
|
str[80] = '\0';
|
|
char80width = StringWidth(str);
|
|
fpDC->SelectObject(pOldFont);
|
|
|
|
//LOGI("char80 string width=%d", 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) {
|
|
LOGI(" Page not wide enough, setting margins to zero");
|
|
fLeftMargin = fRightMargin = 0;
|
|
} else if (totalMargin > fLogPixelsX * 2) {
|
|
LOGI(" Page too wide, setting margins to 1 inch");
|
|
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;
|
|
LOGI(" +++ Margins (in %d pixels/inch) are left=%ld right=%ld",
|
|
fLogPixelsX, fLeftMargin, fRightMargin);
|
|
}
|
|
}
|
|
|
|
// This was derived from Microsoft KB article 129860.
|
|
int PrintRichEdit::DoPrint(CRichEditCtrl* pREC, const WCHAR* title,
|
|
int* pNumPages, bool doPrint)
|
|
{
|
|
FORMATRANGE fr;
|
|
DOCINFO di;
|
|
long textLength, textPrinted, lastTextPrinted;
|
|
int pageNum;
|
|
|
|
LOGI("DoPrint: title='%ls' doPrint=%d", title, doPrint);
|
|
LOGI(" startChar=%d endChar=%d startPage=%d endPage=%d",
|
|
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.
|
|
*/
|
|
GETTEXTLENGTHEX exLenReq;
|
|
long basicTextLength, extdTextLength;
|
|
basicTextLength = pREC->GetTextLength();
|
|
exLenReq.flags = GTL_PRECISE | GTL_NUMCHARS;
|
|
#ifdef _UNICODE
|
|
exLenReq.codepage = 1200; // UTF-16 little-endian
|
|
#else
|
|
exLenReq.codepage = CP_ACP;
|
|
#endif
|
|
extdTextLength = (long)::SendMessage(pREC->m_hWnd, EM_GETTEXTLENGTHEX,
|
|
(WPARAM) &exLenReq, (LPARAM) NULL);
|
|
LOGD("RichEdit text length: std=%ld extd=%ld",
|
|
basicTextLength, extdTextLength);
|
|
|
|
if (fEndChar == -1) {
|
|
if (extdTextLength > 0)
|
|
textLength = extdTextLength;
|
|
else
|
|
textLength = basicTextLength;
|
|
} else
|
|
textLength = fEndChar - fStartChar;
|
|
|
|
LOGD(" +++ starting while loop, textLength=%ld", textLength);
|
|
pageNum = 0;
|
|
lastTextPrinted = -1;
|
|
do {
|
|
bool skipPage = false;
|
|
pageNum++;
|
|
LOGD(" +++ while loop: pageNum is %d", 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(L"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);
|
|
}
|
|
|
|
//LOGI(" +++ calling FormatRange(%d)", doPrint && !skipPage);
|
|
//LogHexDump(&fr, sizeof(fr));
|
|
|
|
/* print a page full of RichEdit stuff */
|
|
textPrinted = pREC->FormatRange(&fr, doPrint && !skipPage);
|
|
LOGD(" +++ returned from FormatRange (textPrinted=%d)",
|
|
textPrinted);
|
|
if (textPrinted <= lastTextPrinted) {
|
|
/* the earlier StartPage can't be undone, so we'll get an
|
|
extra blank page at the very end */
|
|
LOGW("GLITCH: no new text printed (printed=%ld, last=%ld, len=%ld)",
|
|
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 */
|
|
LOGI("EndPage returned <= 0 (cancelled)");
|
|
fpDC->AbortDoc();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (textPrinted < textLength) {
|
|
fr.chrg.cpMin = textPrinted;
|
|
fr.chrg.cpMax = fEndChar; // -1 if nothing selected
|
|
}
|
|
} while (textPrinted < textLength);
|
|
|
|
LOGV(" +++ calling FormatRange(NULL, FALSE)");
|
|
pREC->FormatRange(NULL, FALSE);
|
|
LOGV(" +++ returned from final FormatRange");
|
|
|
|
if (doPrint)
|
|
fpDC->EndDoc();
|
|
|
|
if (pNumPages != NULL)
|
|
*pNumPages = pageNum;
|
|
|
|
LOGI("Printing completed (textPrinted=%ld)", textPrinted);
|
|
|
|
return 0;
|
|
}
|