[WinAPI, Visual C++] DC 프린터에 출력하기, CPrintDialog, StartDoc, StartPage, EndPage, EndDoc

이 글을 보시기 전에 DC 그리기 모르시는 분들은 여기를 먼저 보시기 바랍니다.

이번 글에선 DC로 프린터 출력 하기를 해보겠습니다. 우선 MFC 프로젝트 생성후 버튼 하나 만들어주세요.
DC를 프린트 하기전에 우선 출력할 프린터를 선택해야 합니다. CPrintDialog를 사용하면 출력할 프린터를 쉽게 선택할 수 있습니다.
CPrintDialog를 사용하는 방법은 아래와 같습니다.

CPrintDialog dlgPrint(FALSE);

if ( IDOK == dlgPrint.DoModal() )
{
	// todo .. : 선택 프린터 정보 얻기
}

위 코드를 실행해 보면 아래와 같이 프린터를 선택할 수 있습니다.

프린터를 선택했으면, 출력할 문서 정보를 전달하고, 프린터 모드도 설정해줘야 합니다.

프린터 DC와 크기도 읽어와야 하구요. 아래와 같이 코딩하면 선택한 프린터에 대한 정보를 얻어 올 수 있습니다.

// 출력할 문서 정보
DOCINFO docinfo;
memset( &docinfo, 0, sizeof( DOCINFO ) );
docinfo.cbSize = sizeof( DOCINFO );
docinfo.lpszDocName = _T("Print Test"); // 문서 명칭
docinfo.lpszOutput = NULL;
docinfo.lpszDatatype = NULL;
docinfo.fwType = 0;
		
DEVMODE *pDevMode = dlgPrint.GetDevMode(); // 프린터 모드 얻기
pDevMode->dmOrientation = DMORIENT_PORTRAIT; // 가로&세로 모드 설정

HDC hdcPrint = dlgPrint.CreatePrinterDC(); // 프린터 DC 얻기
int iPrintWidth = ::GetDeviceCaps( hdcPrint, HORZRES ); // 가로
int iPrintHeight = ::GetDeviceCaps( hdcPrint, VERTRES ); // 세로

위 방식으로 설정과 얻을 정보를 다 얻었으면 이제 DC를 출력해야 합니다.

프린터 함수의 경우 경우 StartDoc, StartPage, EndPage, EndDoc순서로 출력해야하는데, StartPage와 EndPage을 반복하게 되면 여러 페이지를 출력합니다.
우선 위 함수들의 기본적인 사용 순서는 아래와 같습니다.

// 문서를 시작함
if ( ::StartDoc( hdcPrint, &docinfo ) )
{
	TRACE( _T( "StartDoc\n" ) );

	// 페이지 시작
	if ( TRUE == ::StartPage( hdcPrint ) )
	{
		TRACE( _T( "StartPage\n" ) );
        
		// todo .. : 프린터 dc에 그린다.
        
		// 페이지 종료
		::EndPage( hdcPrint );
	}
	else
	{
		TRACE( _T( "error StartPage\n" ) );
	}

	// 문서를 종료함
	::EndDoc( hdcPrint );
}
else
{
	TRACE( _T( "error StartDoc\n" ) );
}

주석에도 써있지만 StartPage와 EndPage 사이에서 DC를 그려야 출력이 됩니다.

현재는 아직 아무 작업을 하지 않았으므로 빈 페이지를 출력 됩니다.
이제 프린터 DC에 그림을 그려서 첫번째 페이지에 네모, 두번째에 원, 세번째엔 X표시를 그려 출력해보겠습니다.

// 문서를 시작함
if ( ::StartDoc( hdcPrint, &docinfo ) )
{
	TRACE( _T( "StartDoc\n" ) );

	// 첫번째 페이지 ( 네모 )
	if ( TRUE == ::StartPage( hdcPrint ) )
	{
		TRACE( _T( "StartPage\n" ) );

		::PatBlt( hdcPrint, 0, 0, iPrintWidth, iPrintHeight, WHITENESS ); // 하얗게 처리
		::Rectangle( hdcPrint, 500, 500, iPrintWidth - 500, iPrintHeight - 500 ); // 네모 그리기

		::EndPage( hdcPrint );
	}
	else
	{
		TRACE( _T( "error StartPage\n" ) );
	}

	// 두번째 페이지 ( 원 )
	if ( TRUE == ::StartPage( hdcPrint ) )
	{
		TRACE( _T( "StartPage\n" ) );

		::PatBlt( hdcPrint, 0, 0, iPrintWidth, iPrintHeight, WHITENESS ); // 하얗게 처리
		::Ellipse( hdcPrint, 50, 50, iPrintWidth - 50, iPrintHeight - 50 ); // 원 그리기

		::EndPage( hdcPrint );
	}
	else
	{
		TRACE( _T( "error StartPage\n" ) );
	}


	// 세번째 페이지 ( X표시 )
	if ( TRUE == ::StartPage( hdcPrint ) )
	{
		TRACE( _T( "StartPage\n" ) );

		::PatBlt( hdcPrint, 0, 0, iPrintWidth, iPrintHeight, WHITENESS ); // 하얗게 처리
		::MoveToEx( hdcPrint, 0, 0, NULL ); // 선 그리기 1
		::LineTo( hdcPrint, iPrintWidth, iPrintHeight );
		::MoveToEx( hdcPrint, iPrintWidth, 0, NULL ); // 선 그리기 2
		::LineTo( hdcPrint, 0, iPrintHeight );

		::EndPage( hdcPrint );
	}
	else
	{
		TRACE( _T( "error StartPage\n" ) );
	}

	// 문서를 종료함
	::EndDoc( hdcPrint );
}
else
{
	TRACE( _T( "error StartDoc\n" ) );
}

실행 시켜서 확인해 봅니다.

화면에 출력되는 것을 보여줘야 해서 PDF로 출력했습니다. 프린터 기기를 선택하셔서도 해보세요.

위 코드는 전체 코드는 아래와 같습니다.

void CMFCApplication1Dlg::OnBnClickedButton1()
{
	CPrintDialog dlgPrint(FALSE);

	if ( IDOK == dlgPrint.DoModal() )
	{
		// 출력할 문서 정보
		DOCINFO docinfo;
		memset( &docinfo, 0, sizeof( DOCINFO ) );
		docinfo.cbSize = sizeof( DOCINFO );
		docinfo.lpszDocName = _T("Print Test"); // 문서 명칭
		docinfo.lpszOutput = NULL;
		docinfo.lpszDatatype = NULL;
		docinfo.fwType = 0;
		
		DEVMODE *pDevMode = dlgPrint.GetDevMode(); // 프린터 모드 얻기
		pDevMode->dmOrientation = DMORIENT_PORTRAIT; // 가로&세로 모드 설정

		HDC hdcPrint = dlgPrint.CreatePrinterDC(); // 프린터 DC 얻기
		int iPrintWidth = ::GetDeviceCaps( hdcPrint, HORZRES ); // 가로
		int iPrintHeight = ::GetDeviceCaps( hdcPrint, VERTRES ); // 세로

		// 문서를 시작함
		if ( ::StartDoc( hdcPrint, &docinfo ) )
		{
			TRACE( _T( "StartDoc\n" ) );

			// 첫번째 페이지 ( 네모 )
			if ( TRUE == ::StartPage( hdcPrint ) )
			{
				TRACE( _T( "StartPage\n" ) );

				::PatBlt( hdcPrint, 0, 0, iPrintWidth, iPrintHeight, WHITENESS ); // 하얗게 처리
				::Rectangle( hdcPrint, 500, 500, iPrintWidth - 500, iPrintHeight - 500 ); // 네모 그리기

				::EndPage( hdcPrint );
			}
			else
			{
				TRACE( _T( "error StartPage\n" ) );
			}

			// 두번째 페이지 ( 원 )
			if ( TRUE == ::StartPage( hdcPrint ) )
			{
				TRACE( _T( "StartPage\n" ) );

				::PatBlt( hdcPrint, 0, 0, iPrintWidth, iPrintHeight, WHITENESS ); // 하얗게 처리
				::Ellipse( hdcPrint, 50, 50, iPrintWidth - 50, iPrintHeight - 50 ); // 원 그리기

				::EndPage( hdcPrint );
			}
			else
			{
				TRACE( _T( "error StartPage\n" ) );
			}


			// 세번째 페이지 ( X표시 )
			if ( TRUE == ::StartPage( hdcPrint ) )
			{
				TRACE( _T( "StartPage\n" ) );

				::PatBlt( hdcPrint, 0, 0, iPrintWidth, iPrintHeight, WHITENESS ); // 하얗게 처리
				::MoveToEx( hdcPrint, 0, 0, NULL ); // 선 그리기 1
				::LineTo( hdcPrint, iPrintWidth, iPrintHeight );
				::MoveToEx( hdcPrint, iPrintWidth, 0, NULL ); // 선 그리기 2
				::LineTo( hdcPrint, 0, iPrintHeight );

				::EndPage( hdcPrint );
			}
			else
			{
				TRACE( _T( "error StartPage\n" ) );
			}

			// 문서를 종료함
			::EndDoc( hdcPrint );
		}
		else
		{
			TRACE( _T( "error StartDoc\n" ) );
		}
	}
}

이상으로 DC를 프린트하는 방법을 알아봤습니다.