搜了N久,找到这么一篇文章:使用RichEdit打印http://www.codeproject.com/KB/printing/richeditprint.aspx?fid=13948&fr=1#xx0xx 打印总算完成了. 但是也诚如文章开头所说:
The preview is sucks!
这是取自另外一篇文章的代码,我做了一些修改:
- BOOL PrintRich(CRichEditCtrl *pCtrl ,const CString& Title)
- {
- CDC ThePrintDC; //create a device context to use
- CPrintDialog PrintDialog(FALSE); //make a print dialog too
- if(PrintDialog.DoModal( ) == IDOK) //pressed the ok button on the print dialog?
- {
- ThePrintDC.Attach(PrintDialog.GetPrinterDC());//if so get settings
- }
- else
- {
- return FALSE; //leave this procedure, before anything bad happens
- }
- DOCINFO di; //make a docinfo structure
- ::ZeroMemory(&di, sizeof(DOCINFO));
- di.cbSize = sizeof(DOCINFO); //set size member
- di.lpszDocName = Title;//set doc name, was passed via another funtion
- FORMATRANGE fr; //format range structure
- ::ZeroMemory(&fr, sizeof(FORMATRANGE));
- HDC hdc = ThePrintDC.GetSafeHdc(); //get handle to DC we are using
- fr.hdc = hdc; //Set meber in FormatRange struct
- fr.hdcTarget = hdc; //Set member in FormatRange struct
- //This bit here will get all the dimentions of the printer setup
- int nHorizRes = ThePrintDC.GetDeviceCaps(HORZRES), //width P in MM
- nVertRes = ThePrintDC.GetDeviceCaps(VERTRES), //hight in raster lines
- nLogPixelsX = ThePrintDC.GetDeviceCaps(LOGPIXELSX), //pixels per inch along x
- nLogPixelsY = ThePrintDC.GetDeviceCaps(LOGPIXELSY); //pixels per inch along y
- //set the printable area of printer in the FormatRange struct
- fr.rcPage.left = 0; //these 2 mean top left
- fr.rcPage.top = 0;
- fr.rcPage.right = (nHorizRes * 1440 / nLogPixelsX);//these 2 mean bottom right
- fr.rcPage.bottom = (nVertRes * 1440 / nLogPixelsY); //equation changes pixel to TWIPS
- // Set up some margins all around. Make them one inch
- //results vary on printers depending on setup
- fr.rc.left = fr.rcPage.left + 1100; // 1440 TWIPS = 1 inch.
- fr.rc.top = fr.rcPage.top + 1440;
- fr.rc.right = fr.rcPage.right - 1100;
- fr.rc.bottom = fr.rcPage.bottom - 1440;
- //select all text for printing
- CHARRANGE &cr = fr.chrg;
- cr.cpMin=0;
- cr.cpMax=-1;//-1 selects all
- //get length of document, used for more than one page
- long CharRange = pCtrl->GetTextLength();;
- int ErrorStatus=0;
- //Start Printing
- //ThePrintDC.SetAbortProc(&AbortProc(hdc, )); //dont know how callbacks works yet
- ThePrintDC.StartDoc(&di); //start printing document
- long LastChar = -1;//will store document length
- do
- {
- ThePrintDC.StartPage(); //start the page
- cr.cpMin = LastChar + 1;//Change charrange struct for next page
- cr.cpMax = -1;
- LastChar = pCtrl->FormatRange( &fr, TRUE );//send text to DC, and record index
- //of last fitting char
- ErrorStatus=ThePrintDC.EndPage(); //end this page, and record status
- if (cr.cpMin >= LastChar)
- break;
- cr.cpMin = LastChar;
- cr.cpMax = CharRange; //using last char printed
- }while(LastChar < CharRange && ErrorStatus > 0); //while there is stuff to print out there
- //is not an error(error is -No)
- //if there is an error or printing has finished
- //have to make sure AbortDoc is called instead of EndDoc if there has been a problem
- // delete pDlg;
- CRect rcClient;
- pCtrl->GetClientRect(&rcClient);
- pCtrl->DisplayBand(&rcClient);
- switch(ErrorStatus)
- {
- case SP_ERROR:
- {
- ThePrintDC.AbortDoc();
- AfxMessageBox(_T("There was a general printing error, please check printer is working properly, connected, on line etc."), MB_OK | MB_ICONEXCLAMATION);
- return FALSE;
- }
- case SP_APPABORT:
- {
- ThePrintDC.AbortDoc();
- return FALSE;
- }
- case SP_USERABORT:
- {
- ThePrintDC.AbortDoc();
- return FALSE;
- }
- case SP_OUTOFDISK:
- {
- ThePrintDC.AbortDoc();
- AfxMessageBox(_T("out of disk"));
- return FALSE;
- }
- case SP_OUTOFMEMORY:
- {
- ThePrintDC.AbortDoc();
- AfxMessageBox(_T("out of memeory"));
- return FALSE;
- }
- default:
- {
- ThePrintDC.EndDoc();
- return TRUE;
- }
- }
- }
只此一个函数,即可完成多页打印功能,而你只需要提供一个RichEdit控件的指针即可.
但是在实际测试的过程中却总有问题:
- do
- {
- ThePrintDC.StartPage();
- ...
- LastChar = pCtrl->FormatRange( &fr, TRUE );
- // LastChar 达不到 CharRange的值
- ...
- ErrorStatus=ThePrintDC.EndPage();
- }while(LastChar < CharRange && ErrorStatus > 0);
LastChar 达不到 CharRange的值,但是当我加上这么一行代码,文档也能完全打印出来(参见上面函数完整代码):
- if (cr.cpMin >= LastChar)
- break;
这是什么道理咩??UNICODE字符集问题咩?
从RichEdit20W它就已经支持UNICODE了. 但是在某些方面它又表现的不支持UNICODE,比如StreamIn
- void CAutoRichEditCtrl::SetRTF(CString sRTF)
- {
- // Put the RTF string sRTF into the rich edit control.
- // Read the text in
- EDITSTREAM es;
- es.dwError = 0;
- es.pfnCallback = CBStreamIn;
- es.dwCookie = (DWORD) &sRTF;
- StreamIn(SF_RTF, es); // Do it.
- }
- /*
- Callback function to stream an RTF string into the rich edit control.
- */
- DWORD CALLBACK CAutoRichEditCtrl::CBStreamIn(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
- {
- // We insert the rich text here.
- /*
- This function taken from CodeGuru.com
- http://www.codeguru.com/richedit/rtf_string_streamin.shtml
- Zafir Anjum
- */
- CString *pstr = (CString *) dwCookie;
- if (pstr->GetLength() < cb)
- {
- *pcb = pstr->GetLength();
- memcpy(pbBuff, (LPCSTR) *pstr, *pcb);
- pstr->Empty();
- }
- else
- {
- *pcb = cb;
- memcpy(pbBuff, (LPCSTR) *pstr, *pcb);
- *pstr = pstr->Right(pstr->GetLength() - cb);
- }
- return 0;
- }
这两个函数在使用多字节字符集编译的工程中表现良好,但是在UNICODE字符集下就会出现乱码
向下面一样,将字符串转换一下后则运行无误
- void CAutoRichEditCtrl::SetRTF(CString sRTF)
- {
- // Put the RTF string sRTF into the rich edit control.
- // Read the text in
- EDITSTREAM es;
- es.dwError = 0;
- es.pfnCallback = CBStreamIn;
- // fixed by qiuchengw -- unicode is not right
- CAnsi ansi(sRTF);
- std::string str(ansi);
- es.dwCookie = (DWORD)&str;
- // es.dwCookie = (DWORD) &sRTF;
- StreamIn(SF_RTF, es); // Do it.
- }
- /*
- Callback function to stream an RTF string into the rich edit control.
- */
- DWORD CALLBACK CAutoRichEditCtrl::CBStreamIn(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
- {
- // We insert the rich text here.
- /*
- This function taken from CodeGuru.com
- http://www.codeguru.com/richedit/rtf_string_streamin.shtml
- Zafir Anjum
- */
- std::string *pstr = (std::string *)dwCookie;
- // CString *pstr = (CString *) dwCookie;
- int len = pstr->length();
- if (len < cb)
- {
- *pcb = len;
- memcpy(pbBuff,pstr->c_str(),len);
- pstr->clear();
- }
- else
- {
- *pcb = cb;
- memcpy(pbBuff,pstr->c_str(),cb);
- *pstr = pstr->substr(cb);
- }
- /* if (pstr->GetLength() < cb)
- {
- *pcb = pstr->GetLength();
- memcpy(pbBuff, (LPCSTR) *pstr, *pcb);
- pstr->Empty();
- }
- else
- {
- *pcb = cb;
- memcpy(pbBuff, (LPCSTR) *pstr, *pcb);
- *pstr = pstr->Right(pstr->GetLength() - cb);
- }
- */
- return 0;
- }
最近比较忙, 没有做什么更多测试,也不深入研究了,下面的两个问题,烦请高人解决:
1. 是否StreamIn真的不支持UNICODE,还是我某些设置没有做好(比如在StreamIn之前应该做什么初始化之类的)?
2. 那个打印是否也真的是UNICODE的问题? 有更优雅的解决方法不? 我那个"解决方法"太暴力, 简直成了我一块心病啊!