VC5.0 為Windows 的程序員提供了一個很好的C++ 開發環境,減少了很多編程負擔,但同時也為我們在程序中加入自己的思想增加了難度。本人在一軟件開發中,想控制文字打印時的字體,使字體大小縮小一倍,以節省打印紙。經過一段時間的摸索,終于解決了這一問題,下面分幾步向大家做一介紹。
---- 一、對VC5 自動生成的程序框架進行改進
---- 這里用VC5 自動創建一個例子程序Test,單文檔界面,注意在最后一步修改view 的繼承類為CEditView。
---- 在view 類中,VC5 已經自動創建了三個用于支持打印的函數:OnPreparePrinting,OnBeginPrinting,OnEndPrinting。為了實現我們的功能,需要再繼承以下幾個函數:OnPrepareDC,OnPrint。并將OnPrepareDC 和OnEndPrinting 改為如下實現:
// OnPrepareDC() void CTestView::OnPrepareDC (CDC* pDC, CPrintInfo* pInfo) { CView::OnPrepareDC(pDC, pInfo); }
// OnEndPrinting() void CTestView::OnEndPrinting (CDC* pDC, CPrintInfo* pInfo) { CView::OnEndPrinting(pDC, pInfo); } ---- 用CView 來替代原來的CEditView,用以避免CEidtView 對打印的控制?刂谱煮w及輸出的功能主要在OnBeginPrinting 和OnPrint 兩個函數來實現。
---- 二、實現OnBeginPrinting 函數
---- 根據VC5 編程機制,在OnBeginPrinting 函數實現打印前的準備工作,包括設置打印字體,根據打印機當前頁面尺寸計算所需頁數等。下面的程序是對打印字體的重新設置和計算所需打印紙頁數。
---- 程序中首先取得打印機的橫向和縱向分辨率,再得到當前打印字體的大小,然后計算出新的字體大小,為默認字體的一半。讀者可以根據需要設定自己的打印字體大小。
---- 接著,取得當前打印紙的寬度和高度,再根據新字體的寬度和高度計算出每行的最大字符數和每頁的最大行數。
---- 由于打印文件中有些行的寬度可能超過每行的最大字符數,所以程序中調用函數RedealTextData() 對打印文件進行重新整理,函數的實現在下面介紹。
---- 最后,程序中計算并設置所需的打印頁數。
OnBeginPrinting()函數實現如下: //==================================== // OnBeginPrinting //==================================== void CTestView::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo) { //設置新的? 字體////////////////
//取打印機的橫方向和縱方向的分辨率 //即每英寸點數 short cxInch = pDC- >GetDeviceCaps(LOGPIXELSX); short cyInch = pDC- >GetDeviceCaps(LOGPIXELSY);
// 取當前字體大小 CFont *curFont = pDC- >GetCurrentFont(); LOGFONT curLogFont; LOGFONT newLogFont;
curFont- >GetLogFont( &curLogFont ); long NewFontWidth = curLogFont.lfWidth; long NewFontHeight = curLogFont.lfHeight; newLogFont = curLogFont;
//計算新的字體大小--縮小一倍 newLogFont.lfWidth =(long)((float)NewFontWidth/2.0 * ((float)cxInch / 72.0)); newLogFont.lfHeight =(long)((float)NewFontHeight/2.0 * ((float)cyInch / 72.0));
//創建并設置新的字體,保留以前的字體 CFont newFont; CFont *oldFont;
newFont.CreateFontIndirect(&newLogFont); oldFont = pDC- >SelectObject(&newFont ); ///////////////////////////////// //根據字體寬度、高度計算 //每行最大字數及每頁最大行數
//取打印紙張高度和寬度 int nPageHeight, nPageWidth; nPageHeight = pDC- >GetDeviceCaps(VERTRES); nPageWidth = pDC- >GetDeviceCaps(HORZRES);
TEXTMETRIC TextM; pDC- >GetTextMetrics(&TextM); //字體高度 m_LineHeight = (unsigned short)TextM.tmHeight; //字體平均寬度 m_CharWidth=(unsigned short) TextM.tmAveCharWidth;
//每行最大字數 m_MaxLineChar = nPageWidth / m_CharWidth - 8; //每頁最大行數 m_LinesPerPage = nPageHeight/ m_LineHeight; //根據每行最大字數對文字進行重新調整 RedealTextData(); ////////////////////////////////////// //計算所需打印紙張數目 int nPrintableLineCount = INT_MAX/m_LineHeight;
// m_lines為文件總行數 if (m_lines < nPrintableLineCount) nPrintableLineCount = m_lines; unsigned short MaxPage = (nPrintableLineCount + m_LinesPerPage - 1) / m_LinesPerPage; //設置所需打印紙張數目 pInfo- >SetMaxPage(MaxPage); pInfo- >m_nCurPage = 1;
////////////////////////////////////////// //最后不要忘記將字體還原,這一句是必需的 pDC- >SelectObject(oldFont ); } ---- RedealTextData 函數根據每行最大寬度對文件進行重新調整。主要是計算文件中每行的寬度,如果超過最大寬度則加入換行符(0x0d,0x0a)。函數實現如下:
//======================================= // RedealTextData //注: //pDoc- >buffer為文件緩沖區 //pDoc- >file_length為文件字節長度 //pDoc- >TextLines為文件原行數 //pDoc- >MaxLineLength為文件原最大行字節寬度 //======================================= void CTextView::RedealTextData() { CDocViewDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc);
short LineLengthMax = m_MaxLineChar; unsigned short lines=0; unsigned long i,j;
//申請新的緩沖區保存調整后的文件 long size = pDoc- >file_length + pDoc- >TextLines* (pDoc- >MaxLineLength/m_MaxLineChar+1); m_newBuffer = new char [size ]; LPSTR newTempPtr = m_newBuffer; m_file_length =pDoc- >file_length; //保存文件新的行數 m_lines = 1; i = 0; //記錄當前行的寬度 short theLineLength=0; //記錄當前行中漢字字節數, //以防止將一半漢字分為兩行 unsigned short halfChinese=0;
while(i < pDoc- >file_length) { *newTempPtr++ = pDoc- >buffer[i];
j=i+1; if( (pDoc- >buffer[i] == 0x0d && pDoc- >buffer[j] == 0x0a)) { m_lines++; theLineLength = 0; } else { //如果是TAB字符,寬度加8 if(pDoc- >buffer[i] == VK_TAB) theLineLength += 8; else { //大于0xa1的字節為漢字字節 if((unsigned char)pDoc- >buffer[i] >= 0xa1) halfChinese++; theLineLength++; } //如果行寬大于每行最大寬度,進行特殊處理 if(theLineLength > LineLengthMax) { char buff[256]; short m=255; newTempPtr--; if((unsigned char )*newTempPtr < 0xa1) { //如果當前字符的前一個字符是數字、 //字母或一些特殊的前置符號時, //指針循環向前取, //以防止將一個單詞分為兩行。 while((*newTempPtr >=0 && *newTempPtr< =9)|| (*newTempPtr >=a && *newTempPtr < = z) || (*newTempPtr >=A && *newTempPtr < = Z) || *newTempPtr == _ || *newTempPtr == * || *newTempPtr == ^ || *newTempPtr == ~ ) buff[m--] = *newTempPtr--; } else //漢字 { //防止將一個漢字分為兩行。 if(halfChinese%2) buff[m--] = *newTempPtr--; } newTempPtr++; //加入換行符,分為兩行 *newTempPtr++ = 0x0d; *newTempPtr++ = 0x0a; for(short k=m+1; k< 256; k++) *newTempPtr++ = buff[k];
m_lines++; theLineLength = 0; m_file_length += 2; } } i++; } } ---- 三、實現OnPrint 函數
---- 在OnPrint 函數中實現真正的文字輸出,主要功能包括設置打印字體大小,計算當前頁號文字輸出位置,以及文字的輸出打印。
---- 程序中首先計算打印區域,文字在這個打印區域內輸出。然后設置新的打印字體。
---- 由于OnPrint 函數是每打印一頁被調用一次,所以需要根據當前打印頁號計算出當前頁的文字在整個文字緩沖區的起始偏移量和終止偏移量。這里程序中調用了函數GetOffset(),此函數在下面介紹。
---- 最后調用Windows 的DrawText() 函數實現文字的輸出。
OnPrint()函數實現如下: //==================================== // OnPrint //======================================== void CTestView::OnPrint(CDC* pDC, CPrintInfo* pInfo) { //計算打印區域////////////////// long yTopOfPage =(pInfo- >m_nCurPage -1) * m_LinesPerPage * m_LineHeight;
//左邊空出兩個字符寬度 pDC- >SetViewportOrg(m_CharWidth * 2, -yTopOfPage);
int nPageWidth = pDC- >GetDeviceCaps(HORZRES); CRect rectClip = CRect(0, yTopOfPage, nPageWidth, yTopOfPage + m_LinesPerPage * m_LineHeight);
/////設置縮小字體/////////////////// //取打印機的橫方向和縱方向的分辨率 //即每英寸點數 short cxInch=pDC- >GetDeviceCaps(LOGPIXELSX); short cyInch= DC- >GetDeviceCaps(LOGPIXELSY);
//取當前字體大小 CFont *curFont = pDC- >GetCurrentFont(); LOGFONT curLogFont; LOGFONT newLogFont;
curFont- >GetLogFont( &curLogFont ); long NewFontWidth = curLogFont.lfWidth; long NewFontHeight = curLogFont.lfHeight; newLogFont = curLogFont;
//計算新的字體大小--縮小一倍 newLogFont.lfWidth = (long)((float)NewFontWidth/2.0 * ((float)cxInch / 72.0)); newLogFont.lfHeight = (long)((float)NewFontHeight/2.0 * ((float)cyInch / 72.0));
//創建并設置新的字體,保留以前的字體 CFont newFont; CFont *oldFont; newFont.CreateFontIndirect(&newLogFont); oldFont = pDC- >SelectObject(&newFont );
/////文字打印輸出///////////////// unsigned short CurrentStartLine , CurrentEndLine; long StartPrintOffset, EndPrintOffset, PrintSize; LPSTR tempPtr; RECT rect1,rect2; //根據當前打印頁號計算文字起始行 CurrentStartLine=(pInfo- >m_nCurPage-1) * m_LinesPerPage; //文字終止行 CurrentEndLine = CurrentStartLine+m_LinesPerPage;
if(CurrentEndLine > m_lines) CurrentEndLine = m_lines; //計算打印文字的起始位置和終止位置 StartPrintOffset=GetOffset(m_newBuffer, m_file_length, CurrentStartLine); EndPrintOffset = GetOffset(m_newBuffer, m_file_length,CurrentEndLine);
PrintSize = EndPrintOffset - StartPrintOffset;
tempPtr = m_newBuffer + StartPrintOffset; //文字輸出 pDC- >DrawText(tempPtr, PrintSize, &rectClip, DT_NOCLIP |DT_NOPREFIX |DT_EXPANDTABS);
//還原舊的打印字體 pDC- >SelectObject(oldFont ); } ---- 程序中的GetOffset 函數是根據給定的行號計算文字的位置,其實現如下:
//======================================== // GetOffset () //======================================== long CTestView::GetOffset(LPSTR buffer, long buffer_length, unsigned short StartLine) { if(StartLine == 0) return 0;
unsigned short lines=0; long i,j;
i = 0; while(i < buffer_length) { j=i+1; if( (buffer[i++] == 0x0d && buffer[j] == 0x0a)) { lines++; if(lines == StartLine) return i; } } return buffer_length; } ---- 以上是本人在編程中的一點心得,歡迎和大家共同交流。
|