. 문서 인쇄

PrintDoc 함수는 인쇄 대상 차일드의 핸들, 프린터 DC, 인쇄 매수, 인쇄 범위를 인수로 받아들이며 이 정보대로 문서를 인쇄한다. 인쇄에 관련된 모든 처리를 담당하는 인쇄 메인 함수라고 할 수 있으며 이 함수에 의해 실제 문서가 인쇄된다. 먼저 인쇄 취소를 위해 다음 두 개의 전역변수를 선언한다.

 

BOOL bPrinting;

HWND hDlgCancel;

 

bPrinting은 인쇄중임을 나타내는 플래그이며 hDlgCancel은 인쇄 과정을 보여주는 대화상자의 핸들이다. bPrinting TRUE인 동안 인쇄가 계속 진행되며 hDlgCancel 대화상자에서 취소 버튼을 클릭하면 bPrinting FALSE가 된다. 인쇄는 프린터라는 또 다른 컴퓨터와 통신하는 동작이며 문서 전체를 인쇄하는 것은 굉장한 시간을 필요로 한다. 프린터라는 놈은 아무리 빨라 봐야 1초에 한 장도 제대로 못 찍는 형편없는 느림보다. 어떤 프린터는 1분에 고작 한 장밖에 출력하지 못하는 것도 있다.

인쇄하는 시간이 이렇게 많이 걸리기 때문에 인쇄중에 지금 어디쯤 인쇄하고 있는지 안내 대화상자를 보여주어야 하며 이 대화상자의 취소 버튼을 클릭하여 인쇄를 중간에 취소할 수 있도록 해야 한다. 사용자들은 프린터가 느리다는 것을 알기 때문에 프린터를 기다리는 일에는 관대하지만 작업 과정을 보여주지 않는 것은 용서하지 않는다. PrintDoc 함수와 관련 함수들의 코드는 다음과 같다. 워낙 많은 일들을 하기 때문에 코드가 길지만 아주 정석적인 절차를 따르고 있을 뿐이다.

 

BOOL PrintDoc(HWND hChild, HDC pdc, int nPages, int nCopies, int nFirst, int nFinal)

{

     SInfo *pSi;

     DOCINFO doc;

     TCHAR DocName[MAX_PATH+20];

     int Result;

     int xpage, ypage;

     int dpiX, dpiY;

     int nRepeatCopy,nRepeatPage,nPage;

     BOOL bEnd;

     LOGFONT tLogFont;

     HFONT prtfont, oldfont;

     HFONT bkFont, bkOldfont;

     int BkHeight;

     UINT OldAlign;

     COLORREF OldColor;

     TCHAR *HFText;

     int HorF,HFLiney,HFTexty;

     SIZE sz;

     HPEN hPen, OldPen;

     int PenWidth;

 

     pSi=(SInfo *)GetWindowLong(hChild,0);

 

     bPrinting=TRUE;

     SetAbortProc(pdc, (ABORTPROC)AbortProc);

     hDlgCancel=CreateDialog(g_hInst, MAKEINTRESOURCE(IDD_PRINTING),

          g_hFrameWnd, (DLGPROC)DgPrintingProc);

     SetDlgItemText(hDlgCancel,IDC_PRINTINGDOC,pSi->NowFile);

     EnableWindow(g_hFrameWnd, FALSE);

 

     doc.cbSize=sizeof(DOCINFO);

     wsprintf(DocName,"당근 문서 : %s",pSi->NowFile);

     doc.lpszDocName=DocName;

     doc.lpszOutput=NULL;

     doc.lpszDatatype=NULL;

     doc.fwType=0;

     if (StartDoc(pdc, &doc) <= 0) {

          return FALSE;

     }

 

     xpage = GetDeviceCaps(pdc, HORZRES);

     ypage = GetDeviceCaps(pdc, VERTRES);

 

     dpiX = GetDeviceCaps(pdc, LOGPIXELSX);

     dpiY = GetDeviceCaps(pdc, LOGPIXELSY);

 

     prtfont=NULL;

     if (Option.prtFont.lfHeight != 0) {

          if (Option.prtFont.lfHeight==-1) {

              tLogFont=Option.logfont;

          } else {

              tLogFont=Option.prtFont;

          }

          if (tLogFont.lfHeight) {

              tLogFont.lfHeight=tLogFont.lfHeight*dpiY/72;

              tLogFont.lfWidth=0;

              prtfont=CreateFontIndirect(&tLogFont);

              oldfont=(HFONT)SelectObject(pdc,prtfont);

          }

     }

 

     if (lstrlen(Option.prtBkText)) {

          BkHeight=min(xpage,ypage)/lstrlen(Option.prtBkText);

          bkFont=CreateFont(BkHeight,0,600,0,FW_BOLD,0,0,0,HANGEUL_CHARSET,

              3,2,1,VARIABLE_PITCH | FF_ROMAN,"궁서");

     }

 

     Ae_PrintInfo pi;

     pi.pdc=pdc;

     pi.dpiY=dpiY;

     if (Option.prtbParse==TRUE) {

          pi.ParseID=-1;

     } else {

          pi.ParseID=0;

     }

     pi.nWrap=Option.prtnWrap;

     pi.bLineNum=Option.prtbLineNum;

     SetRect(&pi.prt,Mili(Option.prtMargin.left,dpiY),Mili(Option.prtMargin.top,dpiY),

          xpage-Mili(Option.prtMargin.right,dpiY),ypage-Mili(Option.prtMargin.bottom,dpiY));

     pSi->Ae.BeginPrint(&pi);

 

     for (nRepeatCopy=1;nRepeatCopy<=nCopies;nRepeatCopy++) {

     for (nPage=nFirst;nPage<=nFinal;nPage++) {

     for (nRepeatPage=1;nRepeatPage<=nPages;nRepeatPage++) {

          Result=StartPage(pdc);

          if (Result <= 0) goto failpage;

 

          if (Option.prtbSysHeader && prtfont!=NULL) {

              SelectObject(pdc,oldfont);

          }

          for (HorF=0;HorF<=1;HorF++) {

              if (HorF==0) {

                   PenWidth=Mili((double)Option.prtnHeader/10,dpiY);

                   HFLiney=pi.prt.top-PenWidth;

                   GetTextExtentPoint32(pdc,"",2,&sz);

                   HFTexty=HFLiney-Mili(1,dpiY)-sz.cy;

                   HFText=Option.prtHeader;

              } else {

                   PenWidth=Mili((double)Option.prtnFooter/10,dpiY);

                   HFLiney=pi.prt.bottom+PenWidth;

                   HFTexty=HFLiney+Mili(1,dpiY);

                   HFText=Option.prtFooter;

              }

 

              if (PenWidth) {

                   hPen=CreatePen(PS_SOLID,PenWidth,RGB(0,0,0));

                   OldPen=(HPEN)SelectObject(pdc,hPen);

                   MoveToEx(pdc,pi.prt.left,HFLiney,NULL);

                   LineTo(pdc,pi.prt.right,HFLiney);

                   SelectObject(pdc,OldPen);

                   DeleteObject(hPen);

              }

              PrintFormatText(HFText,&pi,pSi->NowFile,nPage,HFTexty);

          }

          if (Option.prtbSysHeader && prtfont!=NULL) {

              SelectObject(pdc,prtfont);

          }

 

          if (lstrlen(Option.prtBkText)) {

              bkOldfont=(HFONT)SelectObject(pdc,bkFont);

              OldColor=SetTextColor(pdc,RGB(200,200,200));

              OldAlign=SetTextAlign(pdc,TA_CENTER);

              TextOut(pdc,xpage/2,ypage/2-BkHeight/2,Option.prtBkText,lstrlen(Option.prtBkText));

              SetTextColor(pdc,OldColor);

               SetTextAlign(pdc,OldAlign);

              SelectObject(pdc,bkOldfont);

          }

 

          SendMessage(hDlgCancel,WM_USER+1,nPage,pi.TotalPage);

          bEnd=pSi->Ae.PrintPage(&pi,nPage);

 

          Result=EndPage(pdc);

          if (Result <= 0) goto failpage;

     }

     if (bEnd) break;

     }

     }

 

failpage:

     Result=EndDoc(pdc);

     pSi->Ae.EndPrint(&pi);

     if (prtfont) {

          SelectObject(pdc,oldfont);

          DeleteObject(prtfont);

     }

     if (lstrlen(Option.prtBkText)) {

          DeleteObject(bkFont);

     }

     if (bPrinting==TRUE) {

          EnableWindow(g_hFrameWnd, TRUE);

          DestroyWindow(hDlgCancel);

          hDlgCancel=NULL;

     }

     return TRUE;

}

 

LRESULT CALLBACK DgPrintingProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

{

     TCHAR Mes[128];

 

     switch (message) {

     case WM_INITDIALOG:

          MoveToParentCenter(hDlg);

          return TRUE;

     case WM_USER+1:

          wsprintf(Mes,"전체 %d쪽 중 %d쪽을 인쇄하고 있습니다.(%d%%)",

              lParam,wParam,wParam*100/lParam);

          SetDlgItemText(hDlg,IDC_PRINTINGMES,Mes);

          return TRUE;

     case WM_COMMAND:

          bPrinting=FALSE;

          EnableWindow(g_hFrameWnd, TRUE);

          DestroyWindow(hDlgCancel);

          hDlgCancel=NULL;

          return TRUE;

     default:

          return FALSE;

     }

}

 

BOOL CALLBACK AbortProc(HDC hPrtdc, int iError)

{

     MSG msg;

     while (bPrinting && PeekMessage(&msg, NULL,0,0,PM_REMOVE)) {

          if (!IsDialogMessage(hDlgCancel, &msg)) {

              TranslateMessage(&msg);

              DispatchMessage(&msg);

          }

     }

     return bPrinting;

}

 

함수 선두에서 인쇄 과정 안내와 취소를 위한 인쇄중 대화상자를 만들고 취소 프로시저를 설정하였다. 이 대화상자는 모델리스로 생성되지만 실제로는 모달로 동작해야 하므로 메인 윈도우를 사용 금지 시켰다. 인쇄중 대화상자가 떠 있는 동안에는 메인 윈도우를 조작하지 못한다. PrintDoc 함수는 인쇄중에 WM_USER+1 메시지를 인쇄중 대화상자로 보내주는데 이때 wParam으로 인쇄중인 페이지 번호를 보내 인쇄 과정을 보여주도록 한다.

StartDoc 함수를 호출하여 문서 인쇄를 시작하고 프린터의 해상도 조사, 인쇄 글꼴 생성, 용지 크기 조사 등 인쇄에 필요한 정보를 조사하여 pi 구조체에 채운다. 그리고 ApiEdit BeginPrint를 호출하여 ApiEdit가 인쇄에 필요한 정렬과 구문 분석을 하도록 시킨다. 여기까지가 인쇄 준비 과정이다.

준비가 끝나면 루프를 돌며 각 페이지를 인쇄한다. 인쇄 매수, 인쇄 범위, 반복횟수에 따라 3중 루프로 구성되어 있으며 한 번 루프를 돌 때마다 한 페이지가 인쇄된다. 3중 루프를 구성하는 이유는 여러 매 인쇄시 한 부씩 인쇄 옵션에 따라 페이지 반복 방법이 달라지기 때문이다. 만약 한 부만 인쇄한다면 nCopies, nPages는 모두 1이 되며 중간에 nPage루프만 실제로 루프를 구성한다.

각 페이지는 본문 인쇄, 머리/꼬리말 인쇄 등 두 개의 과정을 거치는데 본문 인쇄는 앞에서 이미 작성해놓은 CApiEdit PrintPage 함수를 호출하면 된다. 이 함수로 페이지 번호와 pi 구조체만 전달하면 본문이 출력될 것이다. 머리/꼬리말 그리고 본문과의 구분선은 호스트가 직접 인쇄해야 한다.

모든 페이지의 인쇄가 끝나면 EndDoc 함수로 문서 인쇄를 끝내고 ApiEdit에게도 EndPrint 함수를 호출하여 인쇄가 끝났음을 알린다. 인쇄를 위해 생성했던 폰트 객체와 인쇄중 대화상자 등을 제거하면 문서 인쇄가 마무리된다. PrintDoc 함수가 끝나면 Print 함수나 PrintDirect 함수로 리턴하며 이 함수들은 인쇄에 사용한 DC를 제거함으로써 모든 인쇄 과정을 최종 마무리한다.