From YYpBD's MediaWiki
[강좌] 인쇄 #1 코딩.. 인쇄할때 필요한것
┏━━━━━━━━━━ 코딩으로 구현하는 인쇄 ━━━━━━━━━━━┓
┃ ┃
┃ 제 1 장 델파이에서 코딩으로 인쇄할때 필요한 것들 ┃
┃ ┃
┃ 양 병 규 ┃
┃ 하이텔 : 슈베르트 ┃
┃ ohyesoo@hitel.net ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
델파이에는 인쇄를 지원하기 위하여 Printers.pas를 제공하고 있습니다.
제가 인프라이스라면 적어도 이것을 이용한 데모 한 개 정도는 만들었을
터인데 아마도 인프라이스는 리포터는 고사하고 이런 예제 하나 제대로
만들어 주지 않을걸로 봐서 아마도 꽤나 먹고 살기 바쁜가 봅니다.
인쇄에 관련된 API는 Windows.pas에 있지 않고 WinSpool.pas에 따로 정의
되어 있습니다. 하지만 이 강좌에서는 API를 직접 사용하지는 않을것이므로
WinSpool.pas를 직접 사용하지는 않습니다. 다만 Pritners.pas에는 WinSpool
을 사용하고 있습니다.
Printers.pas안에는 클래스가 딱 하나 TPritner가 정의되어 있습니다. 우리는
이것을 이용해서 인쇄의 모든 부분을 해결 할 것입니다. 고로 인쇄를 하기
위해서는 가장 먼저 해야 할일.. uses에 Printers를 추가 하는것입니다.
그리고 TPrinter에는 Canvas라는 프로퍼티가 있는데 이것은 윈도API중 그리기
함수들만을 모아서 만들어놓은 것으로 Graphics.pas에 정의 되어 있으므로
uses에 Graphics도 포함되어 있어야 합니다. 그 외의 내용은 강좌 중간 중간
에 필요할때 마다 설명을 하겠습니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 계속
[강좌] 인쇄 #2 TPrinter의 사용법
┏━━━━━━━━━━ 코딩으로 구현하는 인쇄 ━━━━━━━━━━━┓
┃ ┃
┃ 제 2 장 TPrinter의 사용법 ┃
┃ ┃
┃ 양 병 규 ┃
┃ 하이텔 : 슈베르트 ┃
┃ ohyesoo@hitel.net ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
그럼 이제부터 TPrinter를 이용하는 방법을 알아 보겠습니다.
Printers.pas에는 다음과 같은 function이 하나 전역으로 선언되어있습니다.
function Printer: TPrinter;
이것은 마치 Froms를 uses에 추가하면 자동으로 Application이나
Screen을 사용할 수 있듯이 uses에 Printers를 추가하면 Pritner라는
function을 사용할 수 있도록 되어있습니다.
TPrinter는 다음과 같은 구조를 가지고 있습니다.
// 생성, 소멸
constructor Create;
destructor Destroy; override;
// 인쇄 제어
procedure Abort;
procedure BeginDoc;
procedure EndDoc;
procedure NewPage;
property Canvas: TCanvas ; //ReadOnly
property Copies: Integer;
property Orientation: TPrinterOrientation;
property Title: string;
// 프린터 제어
procedure GetPrinter(ADevice, ADriver, APort: PChar;
var ADeviceMode: THandle);
procedure SetPrinter(ADevice, ADriver, APort: PChar;
ADeviceMode: THandle);
property Handle: HDC; //ReadOnly
property PrinterIndex: Integer ;
property Printers: TStrings; //ReadOnly
// 상태 정보
property Aborted: Boolean; //ReadOnly
property Capabilities: TPrinterCapabilities; //ReadOnly
property Fonts: TStrings; //ReadOnly
property PageHeight: Integer; //ReadOnly
property PageWidth: Integer; //ReadOnly
property PageNumber: Integer ; //ReadOnly
property Printing: Boolean; //ReadOnly
우리는 Printer를 사용하기 위하여 Create를 따로 해 줄 필요는 없습니다.
위의 전역 function에서 Create를 알아서 해주기 때문에 그저 처음부터 사용
하기만 하면 됩니다.
다음은 위의 내용 중 가장 중요한 것입니다.
BeginDoc : 인쇄를 시작합니다.
EndDoc : 인쇄를 종료합니다.
NewPage : 한 페이지의 인쇄를 마치고 용지를 새것으로 바꿈니다.
Canvas : 문서의 내용을 그리는데 사용합니다.
Handle : 프린터의 DC핸들값을 말합니다.
이 네가지만 알면 이 강좌를 다 소화 할 수 있습니다.
그 외의 것들...
Abort : 인쇄가 사용자에 의해 강제로 중단되었는지를 여부를 나타냅니다.
즉 이것이 True이면 이 후로는 더 해봐야 바로 삭제되므로 그 즉시
인쇄루틴을 중단하는것이 좋습니다.
Copies : 문서 한 페이지를 몇 장씩 인쇄할지를 결정합니다. 이것은
Pritner.Copies := 2; 식으로 직접 할 수도 있지만 프린터다이아로그
에 있는 "XX장씩 인쇄"의 값을 변경하면 이 값도 따라서 바뀝니다.
그렇다고 Copies가 2로 되어있다고해서 인쇄루틴을 두 번 반복해야
하는것은 아닙니다. 인쇄루틴은 한페이지에 한 번씩만 실행하면
사용자가 프린트다이아로그에서 2장씩 인쇄를 설정하면 윈도가 알
아서 두장씩 뽑아 주므로 사실 거의 프로그래머가 이 부분을 처리해
야 할 일은 없으며 인쇄옵션같은 폼을 따로 만들때의 경우에 주로
사용합니다.
Orientation : 문서의 출력 방향을 가로 또는 세로 중 하나로 결정을 합니다.
Title: 인쇄가 시작되면 윈도의 인쇄관리자가 실행되는데 인쇄관리자의
인쇄목록에 제목으로 사용합니다. 고로 인쇄 품질과는 아무런 관계가
없습니다.
GetPrinter : 프린터의 상태를 알아오는데 사용합니다.
SetPrinter : 프린터의 상태를 설정하는데 사용합니다.
PrinterIndex : 윈도에 여러개의 프린터가 설치되어있을 경우 현재 사용중인
프린터의 순서를 나타냅니다.
Printers : 윈도에 설치된 프린터들의 목록을 보여줍니다.
Fonts : 인쇄를 할때 사용할 수 있는 글꼴의 목록을 보여줍니다.
(화면에서 사용할 수 있는 글꼴 목록 = Screen.Fonts )
일부의 글꼴은 화면에서는 사용할 수 있으나 인쇄에서는 사용할 수
없는 글꼴도 있으므로 글꼴을 변경하기전 반드시 Fotns에 해당 글꼴
이 포함되어있는지를 알아보고 변경해야 합니다.
ex)
NewFont := '굴림체';
if Printers.Fonts.Indexof( NewFont ) >= 0 then
Printers.Canvas.Font.Name := NewFont;
PageHeight : 용지에서 사용가능한 면적의 높이를 픽셀단위로 나타냅니다.
PageWidth : 용지에서 사용가능한 면적의 넓이를 픽셀단위로 나타냅니다.
PageNumber : 현재 인쇄되고 있는 페이지를 나타냅니다.
Printing : 인쇄 중 인지를 나타냅니다.
가장 간단한 인쇄루틴은 다음과 같습니다.
procedrue TForm1.Buton1Click(Sender: TObject);
begin
Printer.BeginDoc;
Printer.EndDoc;
end;
이렇게 하면 내용이 없으므로 그냥 빈 용지만 한 장 출력됩니다.
이번에는 페이지를 한번 바꿔 볼까요?
procedrue TForm1.Buton1Click(Sender: TObject);
begin
Printer.BeginDoc;
Pritter.NewPage;
Printer.EndDoc;
end;
이렇게 하면 내용이 없기는 마찬가지지만 페이지를 한 장 넘겼으므로
빈 용지가 두 장이 나옵니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 계속
[강좌] 인쇄 #3 TCanvas의 사용법
┏━━━━━━━━━━ 코딩으로 구현하는 인쇄 ━━━━━━━━━━━┓
┃ ┃
┃ 제 3 장 TCanvas의 사용법 (Printer.Canvas) ┃
┃ ┃
┃ 양 병 규 ┃
┃ 하이텔 : 슈베르트 ┃
┃ ohyesoo@hitel.net ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
자~ 이제부터는 용지에다가 뭔가를 그려보겠습니다.
그 전에 용지에 뭔가를 그릴려면 Printer.Canvas부터 사용할 줄 알아야 합
니다. TCanvas가 지원하지 않는 일부 함수를 제외하고는 대부분 Printer.
Canvas를 이용해서 그릴것이기 때문입니다.
TCanvas는 다음과 같은 구조를 가지고 있습니다.
// 생성, 소멸
constructor Create;
destructor Destroy; override;
// 직선그리기
procedure LineTo(X, Y: Integer);
procedure MoveTo(X, Y: Integer);
procedure Polyline(const Points: array of TPoint);
procedure PolyBezier(const Points: array of TPoint);
procedure PolyBezierTo(const Points: array of TPoint);
// 사각형 그리기
procedure FillRect(const Rect: TRect);
procedure DrawFocusRect(const Rect: TRect);
procedure FrameRect(const Rect: TRect);
procedure Rectangle(X1, Y1, X2, Y2: Integer);
procedure RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer);
// 원 그리기
procedure Arc(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
procedure Ellipse(X1, Y1, X2, Y2: Integer);
procedure Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
procedure Chord(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
// 다각형 그리기
procedure Polygon(const Points: array of TPoint);
// 채우기
procedure FloodFill(X, Y: Integer; Color: TColor;
FillStyle: TFillStyle);
// 비트맵 처리
procedure BrushCopy(const Dest: TRect; Bitmap: TBitmap;
const Source: TRect; Color:TColor);
procedure CopyRect(const Dest: TRect; Canvas: TCanvas;
const Source: TRect);
procedure Draw(X, Y: Integer; Graphic: TGraphic);
procedure StretchDraw(const Rect: TRect; Graphic: TGraphic);
// 문자열 관련
function TextExtent(const Text: string): TSize;
function TextHeight(const Text: string): Integer;
procedure TextOut(X, Y: Integer; const Text: string);
procedure TextRect(Rect: TRect; X, Y: Integer;
const Text: string);
function TextWidth(const Text: string): Integer;
// 점찍기
property Pixels[X, Y: Integer]: TColor;
// 상태 제어
procedure Lock;
procedure Unlock;
procedure Refresh;
function TryLock: Boolean;
// 이벤트
property OnChange: TNotifyEvent;
property OnChanging: TNotifyEvent;
// 프로퍼티
property ClipRect: TRect; //ReadOnly
property Handle: HDC;
property LockCount: Integer ; //ReadOnly
property CanvasOrientation: TCanvasOrientation ; //ReadOnly
property PenPos: TPoint ;
property TextFlags: Longint;
property Brush: TBrush;
property CopyMode: TCopyMode;
property Font: TFont;
property Pen: TPen;
이 중에서 선그리기를 한번 해 볼까요?
빈폼의 OnPaint이벤트에다가 다음과 같이 써 보세요
procedure TForm1.FormPaint(Sender: TObject);
begin
Canvas.MoveTo( 100, 100 );
Canvas.LineTo( 200, 200 );
end;
음~ 이렇게 하니깐 어떤가요? 폼에 역슬래시 모양의 선이 그려졌나요?
그렇다면 제대로 한겁니다.
이번에는 프린터를 이용해서 용지에다가 그려 보겠습니다.
폼에 버튼을 하나 올려놓고 버튼의 OnClick에 이렇게 해 보세요
procedure TForm1.Button1Click(Sender: TObject);
begin
Printer.BeginDoc;
Printer.Canvas.MoveTo( 100, 100 );
Printer.Canvas.LineTo( 200, 200 );
Printer.EndDoc;
end;
아까것이랑 같은데 인쇄를 시작하는 문장과 끝내는 문장이 폼함되었고
Canvas가 Pritner.Canvas로 바뀌었다는것만 다르고 같습니다.
어떤가요? 용지에 잘 그려졌습니까? 역시 훌륭하시군요~
이번에는 선그리기와 더불어 네모와 동그라미 등 여러가지를 그려보겠습니다.
procedure DrawLine( Canvas: TCanvas );
begin
with Canvas do
begin
Pen.Width := 1;
MoveTo( 100, 100 );
LineTo( 2000, 100 );
Pen.Width := 3;
MoveTo( 100, 200 );
LineTo( 2000, 200 );
Pen.Width := 5;
MoveTo( 100, 400 );
LineTo( 2000, 400 );
Pen.Width := 10;
MoveTo( 100, 600 );
LineTo( 2000, 600 );
Pen.Width := 20;
MoveTo( 100, 800 );
LineTo( 2000, 800 );
Pen.Color := clRed;
Brush.Color := clYellow;
Ellipse( 100, 1000,
2000, 1200 );
Pen.Color := clGreen;
Brush.Color := clBlue;
Rectangle( 100, 1400,
2000, 1600 );
Pen.Color := clMaroon;
Brush.Color := clAqua;
Rectangle( 100, 1800,
2000, 2000 );
Brush.Color := clNavy;
Brush.Style:= bsDiagCross;
Rectangle( 100, 2200,
2000, 2500 );
end;
end;
procedure TMain_Form.Button1Click(Sender: TObject);
begin
Printer.BeginDoc;
DrawLine( Printer.Canvas );
Printer.EndDoc;
end;
자 선이 다섯개 동그라미 하나 네모가 세개가 그려졌습니다.
위의 소스만 보면 얼른 알 수 있을것이므로 자세한 설명은
생략하겠습니다. 한가지 짚고 넘어갈것은 아래에 있는
버튼OnClick이벤트에서는 인쇄를 시작하고 종료하는 문장만 있고
실제 그려지는 루틴은 위에 별도로 DrawLine이라는 procedure를 따로 만들
어 두었다는 점입니다. 만약 버튼의 OnClck이벤트에다가 이렇게 하면...
procedure TMain_Form.Button1Click(Sender: TObject);
begin
DrawLine( Image1.Canvas );
end;
어떻게 될까요? 물론 당연히 Image1에 똑같은 내용이 그려질겁니다.
이제 이해 하시겠죠? 인쇄를 할때 미리보기를 구현해야 합니다. 그럴경우
인쇄할때 그리는 루틴따로 미리보기할때 그리는 루틴따로 할것 없이
실제 그리는 루틴을 별도로 독립시켜놓으면 하나의 루틴으로 인쇄와 미리보기
를 다 할 수 있어서 좋습니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 계속
[강좌] 인쇄 #4 매핑모드의 사용법
┏━━━━━━━━━━ 코딩으로 구현하는 인쇄 ━━━━━━━━━━━┓
┃ ┃
┃ 제 4 장 매핑모드(Mapping Mode)의 사용법 ┃
┃ ┃
┃ 양 병 규 ┃
┃ 하이텔 : 슈베르트 ┃
┃ ohyesoo@hitel.net ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
실제로 인쇄에서의 가장 핵심 부분은 매핑모드에 있습니다. 그리고 쓰고
하는것은 이미 누구나 다 아는 사실이고 별로 어려울것도 없으며 궂이
설명을 할 것도 없습니다. 그러나 대부분 매핑모드를 몰라서 인쇄루틴을
엉터리로 하거나 매번 그릴때마다 인자값을 프린터의 해상도에 맞게 재 계산
을 해서 그리는것이 대부분입니다. 이제 이번 장의 몇 줄만 잘 배우시면
인쇄가 이렇게 쉽다는것을 알게 될겁니다.
매핑모드란?
그리기 함수들이 사용하는 인자값들의 단위를 픽셀, 밀리미터, 인치 혹은
사용자 정의 단위로 바꾸어 주며 그리기의 기준점을 변경하여 줍니다.
좀더 이해하기 쉽게 설명을 하면 3장에서 했던 한가지 예를 다시 들겠습니다.
procedure TForm1.Button1Click(Sender: TObject);
begin
Printer.BeginDoc;
Printer.Canvas.MoveTo( 100, 100 );
Printer.Canvas.LineTo( 200, 200 );
Printer.EndDoc;
end;
이 루틴을 실행하면 역슬래시모양의 선이 그려지는데 시작점이
가로 100 세로 100 지점이고 끝지점이가로 200 세로 200 지점입니다.
물론 단위는 픽셀이지요 그러면 문제가 있지요? 어떤 프린터는 해상도가
150DPI이고 어떤 프린터는 해상도가 300DPI고 또 어떤 프린터는 해상도가
600DPI잖아요 그러면 각 프린터마다 선의 길이와 위치, 굵기가 다르게
나옵니다. 그래서 이것을 해결하기 위해 프린터의 해상도를 얻어와서
시작점의 위치와 끝점의 위치를 재 계산하고 그럽니다. 그러나...
우리는 그렇게 하지 말고 이렇게 합시다.
procedure TForm1.Button1Click(Sender: TObject);
begin
Printer.BeginDoc;
SetMapMode( Printer.Handle, MM_LOMETRIC );
Printer.Canvas.MoveTo( 100, -100);
Printer.Canvas.LineTo( 200, -200 );
Printer.EndDoc;
end;
여기서 바뀐부분은 SetMapMode( Printer.Handle, MM_LOMETRIC );가 추가되
었다는것 하고 그리기의 두번째 인자(Y값)이 마이너스로 바뀌었다는 점입니다.
이렇게하면 그리기의 단위가 픽셀에서 0.1밀리미터 단위로 바뀝니다.
그러니깐 위의 문장은 시작점이 가로, 세로 각 10밀리미터 끝점이 가로, 세로
각 20밀리미터의 위치에 그려집니다. 그리고 선의 굵기를 지정하지 않았으므
로 디폴트값 1이 적용되니깐 선의 굵기는 0.1밀리미터입니다. 물론 프린터의
해상도하고는 아무런 관계가 없습니다. 매핑모드중 디폴트인 MM_TEXT(픽셀단위)
를 제외하고 나머지는 모두 Y값이 위로 갈수록 증가하고 내려 갈수록 감소합
니다. 물론 새로운 함수를 정의 해서 MM_TEXT처럼 밑으로 갈수록 증가하도록
바꿀수도 있지만 그냥 쓰는것이 가장 쉽습니다.
각 매핑모드의 단위는 다음과 같습니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
이름 단위 X증가 방향 Y증가 방향
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
MM_TEXT Pixel 오른쪽 아래
MM_LOMETRIC 0.1 mm 오른쪽 위
MM_HIMETRIC 0.01 mm 오른쪽 위
MM_LOENGLISH 0.01 inch 오른쪽 위
MM_HIENGLISH 0.001 inch 오른쪽 위
MM_TWIPS 1/20 Point, 1/1440 inch 오른쪽 위
MM_ISOTROPIC 사용자정의 X,Y동일 사용자정의 사용자정의
MM_ANISOTROPIC 사용자정의 사용자정의 사용자정의
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
※ MM_TEXT : 엠엠 텍스트
MM_LOMETRIC : 엠엠 로 메트릭
MM_HIMETRIC : 엠엠 하이 메트릭
MM_LOENGLISH : 엠엠 로 잉글리쉬
MM_HIENGLISH : 엠엠 하이 잉글리쉬
MM_TWIPS : 엠엠 트윕스
MM_ISOTROPIC : 엠엠 이소트로픽
MM_ANISOTROPIC : 엠엠 애니소트로픽
( 발음을 확실히 모르면 대중 앞에서 개망신 당할 수도 있어용~ )
자~ 3장에서 했던 예제에 이번에는 매핑모드를 여러가지로 바꾸어서
출력을 해 보겠습니다.
procedure DrawLine( Canvas: TCanvas );
begin
with Canvas do
begin
Pen.Width := 1;
MoveTo( 100, -100 );
LineTo( 2000, -100 );
Pen.Width := 3;
MoveTo( 100, -200 );
LineTo( 2000, -200 );
Pen.Width := 5;
MoveTo( 100, -400 );
LineTo( 2000, -400 );
Pen.Width := 10;
MoveTo( 100, -600 );
LineTo( 2000, -600 );
Pen.Width := 20;
MoveTo( 100, -800 );
LineTo( 2000, -800 );
Pen.Color := clRed;
Brush.Color := clYellow;
Ellipse( 100, -1000,
2000, -1200 );
Pen.Color := clGreen;
Brush.Color := clBlue;
Rectangle( 100, -1400,
2000,-1600 );
Pen.Color := clMaroon;
Brush.Color := clAqua;
Rectangle( 100, -1800,
2000, -2000 );
Brush.Color := clNavy;
Brush.Style:= bsDiagCross;
Rectangle( 100, -2200,
2000, -2500 );
end;
end;
procedure TMain_Form.Button1Click(Sender: TObject);
begin
Printer.BeginDoc;
SetMapMove( Printer.Handle, MM_LOMETRIC );
DrawLine( Printer.Canvas );
Printer.EndDoc;
end;
procedure TMain_Form.Button2Click(Sender: TObject);
begin
Printer.BeginDoc;
SetMapMove( Printer.Handle, MM_HIMETRIC );
DrawLine( Printer.Canvas );
Printer.EndDoc;
end;
procedure TMain_Form.Button3Click(Sender: TObject);
begin
Printer.BeginDoc;
SetMapMove( Printer.Handle, MM_LOENGLISH );
DrawLine( Printer.Canvas );
Printer.EndDoc;
end;
procedure TMain_Form.Button4Click(Sender: TObject);
begin
Printer.BeginDoc;
SetMapMove( Printer.Handle, MM_HIENGLISH );
DrawLine( Printer.Canvas );
Printer.EndDoc;
end;
가장 많이 쓰이는 네 가지로 해 보았습니다.
모두 다 제각각으로 출력이 됩니다. 이 중에서 어떤것을 사용할 지는
물론 본인이 문서의 특성에 맞게 선택을 하면 됩니다.
지금까지 해온 예제들은 모두 그리기의 기본위치가 왼쪽 위 지점이 었습니다.
물론 인쇄에서 뿐만 아니라 화면이나 모든 디바이스에서도 기준점(0,0)은
항상 왼쪽 위 지점입니다. 그래서 대부분의 사람들은 항상 기준점은 왼쪽
위이다. 라고 단정을 지어 버립니다. 하지만 그리기의 기준점은 얼마든지
변경할 수 있습니다. 다음 예를 보십시오.
procedure TForm1.Button1Click(Sender: TObject);
begin
Printer.BeginDoc;
SetMapMove( Printer.Handle, MM_LOMETRIC );
Printer.Canvas.MoveTo( 0, 0 );
Printer.Canvas.LineTo( 100, -100 );
Printer.EndDoc;
end;
이렇게 하면 물론 역슬래쉬모양의 선이 가로세로 10밀리미터 지점까지
그려지겠죠?그러나 시작점이 0, 0 임에도 불구하고 위치가 프린터마다
다르게 나옵니다. 왜냐하면 프린터마다 여백이 다르기 때문이죠~
그래서 왼쪽 여백을 20밀리미터로 하고 싶으면 프린터의 여백을 얻어와서
20밀리미터 빼기 여백을 한 지점을 기준점으로설정하면 모든 프린터에서
같은 여백이 적용됩니다. 쉽죠? 먼저 기준점을 바꾸는 것부터 해볼까요?
var
OldPoint: TPoint;
:
SetWindowOrgEx( Printer.Handle, 100, 100, @OldPoint );
이렇게 해주면 그리기의 기준점이 가로 100 세로 100의 위치로 바뀌면서
현재의 기준점을 OldPoint에 저장해 줍니다.
물론 단위는 현재 매핑모드따라 다릅니다. 즉 디폴트(MM_TEXT)에서는
100픽셀이고 MM_LOMETRIC에서는 10밀리미터의 위치를 말합니다.
MM_TEXT를 제외한 나머지에서는 세번째 인자값(Y축)을 마이너스로 해야겠죠?
기준점을 프린터마다 동일하게 적용하는 예제는 다음 장에서 하도록 하겠
습니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 계속
[강좌] 인쇄 #5 인쇄루틴의 기본 형태
┏━━━━━━━━━━ 코딩으로 구현하는 인쇄 ━━━━━━━━━━━┓
┃ ┃
┃ 제 5 장 인쇄루틴의 기본 형태 ┃
┃ ┃
┃ 양 병 규 ┃
┃ 하이텔 : 슈베르트 ┃
┃ ohyesoo@hitel.net ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
이번 장에서는 지금까지 배운 TPrinter와 Printer.Canvas, 매핑모드를
이용한 단위, 기준점 설정 등을 이용해서 전체 인쇄루틴이 갖추어야 할
기본적인 형태를 알아 보겠습니다. 대충 설명을 하면 이렇게 됩니다.
1. 인쇄를 시작한다.
2. 현재의 매핑모드를 변수에 저장하면서 새로운 매핑모드를 적용시킨다.
3. 현재의 기준점을 변수에 저장하면서 새로운 기준점을 적용시킨다.
4. 그린다.
5. 원래의 기준점을 복구한다.
6. 원래의 매핑모드를 복구한다.
7. 인쇄를 종료한다.
예제를 작성해 볼까요?
// 그리는 루틴
procedure DrawPage( Canvas: TCanvas );
begin
Canvas.Rectangle( 0, 0, 1000, -200 );
end;
// 기준 점을 APoint로 바꾸고 현재의 기준점을 리턴한다.
function SetOrgPoint( APoint: TPoint ): TSize;
var
Org: TPoint;
begin
Escape( Printer.Canvas.Handle, GETPRINTINGOFFSET, 0, nil, @Org );
SetWindowOrgEx( Printer.Handle, Org.X-APoint.X, APoint.Y-Org.Y, @Result );
end;
//메인 루틴
procedure TMain_Form.Button10Click(Sender: TObject);
var
OldMap: Integer;
OldOrg: TSize;
begin
{인쇄를 시작한다}
Printer.BeginDoc;
{현재 매핑모드를 oldMap에 저장하고 새로운 매핑모드(0.1mm)를 적용한다}
OldMap := SetMapMode( Printer.Handle, MM_LOMETRIC );
{현재의 기준점을 OldOrg에 저장하고 새로운 기준점(20mm)을 적용한다}
OldOrg := SetOrgPoint( Point( 200, 200 ) );
try
{그린다}
DrawPage( Printer.Canvas );
{여기서 finally 구문은 필히 사용해서 프린터가 먹통이 되는일이 없게..}
finally
{기준점을 원래대로 복구한다}
SetOrgPoint( Point( OldOrg.cx, OldOrg.cy ) );
{매핑모드를 원래대로 복구한다}
SetMapMode( Printer.Handle, OldMap );
{인쇄를 종료한다}
Printer.EndDoc;
end;
end;
지금 까지 강좌를 잘 이해했다면 별 어려움이 없을 겁니다. 새로운 사실은..
프린터의 여백을 구하는 일인데... 특별히 프린터의 여백을 한번에 구해주는
API는 없습니다. 그래서 Escape를 직접써서 프린터에서 직접 여백을 구했습
니다. Escape를 이용하면 프린터의 각종 정보를 얻어올 수 있습니다. 단!
Escape로 얻어 오는 정보는 그 자체가 표준화가 되어있는 부분이 있고 표준
화되지 않고 특정 프린터에만 적용이되는 부분이 있습니다. 프린터의 여백은
GETPRINTINGOFFSET를 이용하면 될수 있도록 표준화가 되어있습니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 계속
[강좌] 인쇄 #6 문자 출력하기
┏━━━━━━━━━━ 코딩으로 구현하는 인쇄 ━━━━━━━━━━━┓
┃ ┃
┃ 제 6 장 문자 출력하기 ┃
┃ ┃
┃ 양 병 규 ┃
┃ 하이텔 : 슈베르트 ┃
┃ ohyesoo@hitel.net ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
이번 장에서는 문자를 출력하도록 하겠습니다.
문자 출력하는 형태는 여러가지가 있겠으나 이번 강좌에서는 다음 세 가지
를 중점적으로 다루겠습니다.
1. 라인단위로 출력하기
2. 박스(표)안에 출력하기
3. 무질서하게 출력하기
첫번째, 라인단위로 출력하기는 한 라인에 서로 다른 글꼴크기를 적용할때
의 기법을 배우고
두번째, 박스안에 출력하기는 문서에서 표를 사용할때 각 셀에 문자를 중앙
또는 왼쪽, 오른쪽으로 정렬하는 방법에 대해서 배우고
세번째, 무질서하게 출력하기는 글꼴의 가로 세로 비율을 마음대로 조절하여
장평을 조절하는 방법과 문장을 여러 각도로 돌려서 출력하는 방법을 배우겠
습니다. 어때요? 이 정도만 하면 문자 출력은 다 되겠죠?
1. 라인 단위로 출력하기
한 라인에 여러 크기의 폰트가 적용이될때 그냥 TextOut( 0, 0 );
이라고 하면 모두 글꼴의 위부분을 기준으로 출력이 되기 때문에 문장이
위로 정렬된 상태로 됩니다.
( 예: TjT )
┏━━━━━━┓ ┏┓ ┏━━┓
┗━━┓┏━━┛ ┗┛ ┗┓┏┛
┃┃ ┏┓ ┃┃
┃┃ ┏┓┃┃ ┗┛
┃┃ ┃┗┛┃
┃┃ ┗━━┛
┃┃
┃┃
┗┛
이 그림에서 j와 마지막 T자의 크기를 작게 했는데 위로 정렬이 되어서
웃기게 되었죠? 그렇다고 TextOut( XX, Y - Font.Height ); 이렇게 하면
될까요? 그렇게 하면
┏━━━━━━┓
┗━━┓┏━━┛
┃┃
┃┃ ┏┓
┃┃ ┗┛
┃┃ ┏┓ ┏━━┓
┃┃ ┏┓┃┃ ┗┓┏┛
┃┃ ┃┗┛┃ ┃┃
┗┛ ┗━━┛ ┗┛
T자 끼리는 맞는데 j자가 위로 올라왔네요 약간 밑으로 내려 가야 하는디~
그럼 어떻게 해야 하느냐 그걸 알려면 먼저 몇 가지 용어를 알아야 겠군요
────┲━━┱ ──────Ascent Line
┗┓┏┛
┃┃ ┏┓
┃┃ ┗┛
┃┃ ┏┓
┏┓ ┃┃ ┃┃
┃┗━┛┃ ┃┃
──┺━━━┹ ─ ┲┓┨┨─ Base Line
┃┗┛┃
─────────┺━━┹ ─Descent Line
* Ascent 어센트 [asent] <-발음기호
Descent 디센트 [disent]
우리가 정렬을 해야할 기준은 Base Line입니다. 이해 하시죠?
그럼 어센트라인에서 베이스라인까지의 길이와 베이스라인에서 디센트라인
까지의 길이를 구해야 겠군요 그런것을 구하려면 TextMetric을 다룰줄 알아
야 합니다.
TTextMetric = record
tmHeight: Longint; ://전체 높이
tmAscent: Longint; //베이스라인의 윗부분
tmDescent: Longint; //베이스라인의 아랫부분
tmInternalLeading: Longint; //경계 내부의리딩
tmExternalLeading: Longint; //행간의 엑스트라 리딩
tmAveCharWidth: Longint; //문자의 평균 폭
tmMaxCharWidth: Longint; 가장 큰문자 가로 폭
tmWeight: Longint; //굵기
tmOverhang: Longint; //문자열간의 추가 폭
tmDigitizedAspectX: Longint; //수평 기준
tmDigitizedAspectY: Longint; //수직 기준
tmFirstChar: AnsiChar; //첫번째 문자
tmLastChar: AnsiChar; //마지막 문자
tmDefaultChar: AnsiChar; //글꼴에 없는문자를 대체할 문자(네모)
tmBreakChar: AnsiChar; //단어 잘림을 정의하는데 사용되는 문자
tmItalic: Byte; //기울임꼴 <> 0
tmUnderlined: Byte; //밑줄 <> 0
tmStruckOut: Byte; //취소선 <> 0
tmPitchAndFamily: Byte; //글꼴의 피치,패미리 정보
tmCharSet: Byte; //문자세트
end;
우왕~ 무지 많지용~ 하지만 다 알필요없고 제일 위에 세 개
tmHeight, tmAscent, tmDescent만 알면 됩니다. tmHeight는 디센트라인
에서 어센트라인까지의 폰트 높이를 말하구요 tmAscent는 베이스라인에서
어센트라인까지의 길이를 말하고 tmDescent는 베이스라인에서 디센트라인
까지의 길이를 말합니다. 우리는 GetTextMetrics API를 이용해서
프린터에서 사용되고 있는 글꼴의 정보를 위의 TTextMetric에다가 얻어올
것입니다.
아래 소스를 보세요
procedure TMain_Form.Button2Click(Sender: TObject);
var
Metrics : TTextMetric;
begin
Printer.BeginDoc;
with Printer.Canvas do
begin
SetMapMode( Handle, MM_LOMETRIC );
Font.Name := '굴림체';
Font.Height := 300;
GetTextMetrics( Handle, Metrics );
TextOut( 50, -500 + Metrics.tmAscent, 'Delphi델파이' );
Pen.Width := 2;
MoveTo( 0, -500 );
LineTo( 2000, -500 );
Pen.Width := 1;
MoveTo( 0, -500 + Metrics.tmAscent );
LineTo( 2000, -500 + Metrics.tmAscent );
MoveTo( 0, -500 - Metrics.tmDescent );
LineTo( 2000, -500 - Metrics.tmDescent );
end;
Printer.EndDoc;
end;
핵심은
GetTextMetrics( Handle, Metrics );
TextOut( 50, -500 + Metrics.tmAscent, 'Delphi델파이' );
이겁니다. GetTextMetrics로 프린터핸들의 폰트를 Metrics로 저장하고
TextOut( 50, -500 + Metrics.tmAscent, ... 로 출력을 하는데 왜
+ Metrics.tmAscent 냐구요? 매핑모드가 MM_LOMETRIC기 땜에 Y축이
증가할 수록 올라가니깐 그렇습니다. 즉 Y축 -500에서 폰트의 베이스라인
에서 어센트라인까지의 길이만큼 위로 올린다는 뜻입니다. 그러면 TextOut
의 기준 점이 폰트의 크기와 관계없이 베이스라인으로 정렬이 됩니다.
다음 예를 직접 해 보세요
procedure TextOutBase( X, Y: Integer; Text: String );
var
Metrics : TTextMetric;
begin
with Printer.Canvas do
begin
GetTextMetrics( Handle, Metrics );
TextOut( X, Y + Metrics.tmAscent, Text );
end;
end;
procedure TMain_Form.Button3Click(Sender: TObject);
const
Text1 = 'Delphi';
Text2 = 'print';
Text3 = 'Superpage';
Text4 = 'Yang Byoung Kyu';
Text5 = '델마당';
var
Len : Integer;
begin
Printer.BeginDoc;
with Printer.Canvas do
begin
SetMapMode( Handle, MM_LOMETRIC );
Font.Name := 'Times New Roman';
Brush.Style := bsClear;
Len := 100;
Font.Height := 200;
TextOutBase( Len, -300, Text1 );
Inc( Len, TextWidth( Text1 + ' ' ) );
Font.Height := 120;
TextOutBase( Len, -300, Text2 );
Inc( Len, TextWidth( Text2 + ' ' ) );
Font.Height := 60;
TextOutBase( Len, -300, Text3 );
Inc( Len, TextWidth( Text3 + ' ' ) );
Font.Height := 50;
TextOutBase( Len, -300, Text4 );
Inc( Len, TextWidth( Text4 + ' ' ) );
Font.Name := '굴림';
Font.Height := 100;
TextOutBase( Len, -300, Text5 );
MoveTo( 0, -300 );
LineTo( 2000, -300 );
end;
Printer.EndDoc;
end;
2. 박스안에 출력하기
박스안에 출력하는 가장 간편한방법으로는 DrawText이 있습니다.
// 박스안에 출력하기
procedure TMain_Form.Button4Click(Sender: TObject);
const
DrawFormat : array[0..8] of UINT =
( DT_LEFT or DT_TOP, DT_CENTER or DT_TOP,
DT_RIGHT or DT_TOP, DT_LEFT or DT_VCENTER,
DT_CENTER or DT_VCENTER, DT_RIGHT or DT_VCENTER,
DT_LEFT or DT_BOTTOM, DT_CENTER or DT_BOTTOM,
DT_RIGHT or DT_BOTTOM );
Texts: array[0..8] of String =('왼쪽 위', '중간 위', '오른쪽 위',
'왼쪽 중간','중간', '오른쪽 중간',
'왼쪽 아래','중간 아래','오른쪽 아래');
var
ARect: TRect;
i: Integer;
begin
ARect := Rect( 0, -600, 1000, -1600 );
Printer.BeginDoc;
with Printer.Canvas do
begin
SetMapMode( Handle, MM_LOMETRIC );
Font.Name := '굴림체';
Font.Height := 50;
Brush.Style := bsClear;
for i:= 0 to 8 do
DrawText( Handle, PChar( Texts[i] ), Length( Texts[i] ),
ARect, DT_SINGLELINE or DrawFormat[i] );
with ARect do Rectangle( Left, Top, Right, Bottom );
end;
Printer.EndDoc;
end;
DrawText를 이용하여 왼쪽, 오른쪽, 중앙 정렬도 간편하게 할 수 있습니다.
// 정렬하기
procedure TMain_Form.Button5Click(Sender: TObject);
const
DrawFormat: array[0..2] of UINT = ( DT_LEFT, DT_CENTER, DT_RIGHT );
Texts: array[0..4] of String = ( 'Delphi', 'print', 'Superpage',
'Yang Byoung Kyu', '델마당' );
var
ARect: TRect;
i, j: Integer;
begin
ARect := Rect( 200, 0, 1800, -2000 );
Printer.BeginDoc;
with Printer.Canvas do
begin
SetMapMode( Handle, MM_LOMETRIC );
Font.Name := '굴림체';
Font.Height := 50;
Brush.Style := bsClear;
for i:= 0 to 4 do
begin
for j:= 0 to 2 do
DrawText( Handle, PChar( Texts[i] ), Length( Texts[i] ),
ARect, DT_SINGLELINE or DT_BOTTOM or DrawFormat[j] );
Dec( ARect.Bottom, 100 );
end;
end;
Printer.EndDoc;
end;
3. 무질서하게 출력하기
때로는 양식이 아닌 카드나 연하장같은 것을 출력하고자 할때 글씨를 기울
이거나 장평을 조절하고플때가 있습니다. TLogFont를 이용하면 장평과 기울
기를 조절 할 수 있습니다. 이 역시 소스를 보시고 한번 해 보면 알 수 있
을 만한 내용입니다.
// 장평, 기울기
procedure TMain_Form.Button6Click(Sender: TObject);
var
LogFont: TLogFont;
OldFont, NewFont: HFONT;
begin
Printer.BeginDoc;
with Printer.Canvas do
begin
SetMapMode( Handle, MM_LOMETRIC );
Font.Name := '굴림체';
Brush.Style := bsClear;
{폰트정보를 LogFont에 저장한다}
if GetObject(Font.Handle, SizeOf(LogFont), @LogFont) <> 0 then
begin
{폰트정보 중 필요한 부분을 수정한다}
with LogFont do
begin
lfHeight := 100; // 폰트의 높이
lfWidth := 200; // 폰트의 장평(가로)
lfEscapement := -300; // 30도로 기울기, 0 = 3시방향, 단위 0.1도
end;
{폰트정보를 이용하여 새로운 폰트를 생성한다}
NewFont:= CreateFontIndirect( LogFont );
{현재의 폰트르 OldFont로 저장하면서 새로운 폰트를 적용한다}
OldFont := SelectObject( Handle, NewFont );
{글씨를 쓴다}
TextOut( 100, -1200, '글씨를 쓰자' );
{원래의 폰트로 복구한다}
NewFont := SelectObject(Canvas.Handle,OldFont);
DeleteObject(NewFont);
end;
end;
Printer.EndDoc;
end;
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 계속
[강좌] 인쇄 #7 음영 사용하기
┏━━━━━━━━━━ 코딩으로 구현하는 인쇄 ━━━━━━━━━━━┓
┃ ┃
┃ 제 7 장 음영 사용하기 ┃
┃ ┃
┃ 양 병 규 ┃
┃ 하이텔 : 슈베르트 ┃
┃ ohyesoo@hitel.net ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
이번에는 문서에 음영을 넣어보도록 하겠습니다. 설마 음영이 뭔지 모르시는
분은 없으시겠지요?
음영 처리 기법은 크게 두 가지가 있습니다.
1. RGB 색상을 이용한 음영
2. 브러시 패턴을 이용한 음영
표준 브러시 패턴을 이용한 방법
비트맵 브러시를 이용한 방법
첫번째, RGB 색상을 이용한 음영은 브러시 스타일을 bsSolid로 주고 브러시
색을 적절히 조절해서 그냥 네모를 그리는것입니다. 상당히 간편하고도 편
리하면서 깔끔하기때문에 가장 널리 사용합니다. 윈도용 아래아 한글에서 사
용하기도합니다.
두번째 브러시 패턴을 이용한 음영은 두가지가 있습니다. 그냥 표준 브러시
패턴을 이용하는 방법과 비트맵 브러시를 이용하는 방법이 있는데 표준 브러시
는 모양이 이쁘지 않기 때문에 거의 활용도가 떨어지고 대부분 비트맵브러시
를 이용해서 처리합니다. MS워드에서 이 방법을 사용합니다.
1. RGB 색상을 이용한 음영
이 방법은 그냥 네모를 그린다고 생각하면 됩니다. 다만 네모 안에 색을
적절히 조절만 하면 됩니다.
//RGB를 이용한 음영
procedure TMain_Form.Button7Click(Sender: TObject);
var
i: Integer;
begin
Printer.BeginDoc;
SetMapMode( Printer.Handle, MM_LOMETRIC );
with Printer.Canvas do
begin
for i:= 0 to 255 do
begin
Pen.Color:= RGB( i, i, i );
Brush.Color:= Pen.Color;
Brush.Style:= bsSolid;
Rectangle( 0, -( i * 10 ), 500, -( ( i+1 ) * 10 ) );
Brush.Style:= bsClear;
if i mod 10 = 0 then
TextOut( 550, -( i * 10 ),
Format( 'RGB( %d, %d, %d );', [i,i,i] ) );
end;
end;
Printer.EndDoc;
end;
2. 비트맵 브러시를 이용한 음영
비트맵 브러시는 음영 말고도 여러가지에 활용됩니다. 데스크탑의 무늬라던가
폼을 마우스로 이동할때 나타나는 선을 그린다거나 윈도 종료시 화면이 어두어
지게 한다거나 팝업 도움말이 나타날때 그림자 효과를 준다거나 델파이에서도
오브젝트인스펙터에서 그리드의 가로선(점선)을 비트맵 브러시패턴을 이용해서
그리고 있습니다.
비트맵 브러시에서 사용하는 비트맵은 가로,세로의 크기가 8 X 8 이어야 합니
다. 아마도 속도를 빠르게 처리하기 위해서 고정시킨것 같습니다.
아래의 예제에서 Image_Brush1, Image_Brush2, Image_Brush3, Image_Brush4
에 필요한 그림의 크기를 모두 8 X 8로 하고 바탕색을 흰색으로하고 검정색
으로 점을 몇개씩 (예를들어 흰색 3개에 검정색 1개) 찍되 네개를 모두
틀리게 그리십시오.
//비트맵 브러시를 이용한 음영
procedure TMain_Form.Button9Click(Sender: TObject);
var
i: Integer;
begin
Printer.BeginDoc;
SetMapMode( Printer.Handle, MM_LOMETRIC );
with Printer.Canvas do
begin
Brush.Bitmap := Image_Brush1.Picture.Bitmap;
Rectangle( 1700, 0, 2000, -400 );
Brush.Bitmap := Image_Brush2.Picture.Bitmap;
Rectangle( 1700, -400, 2000, -800 );
Brush.Bitmap := Image_Brush3.Picture.Bitmap;
Rectangle( 1700, -800, 2000, -1200 );
Brush.Bitmap := Image_Brush4.Picture.Bitmap;
Rectangle( 1700, -1200, 2000, -1600 );
end;
Printer.EndDoc;
end;
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 계속
[강좌] 인쇄 #8 그림 출력하기
┏━━━━━━━━━━ 코딩으로 구현하는 인쇄 ━━━━━━━━━━━┓
┃ ┃
┃ 제 8 장 그림 출력하기 ┃
┃ ┃
┃ 양 병 규 ┃
┃ 하이텔 : 슈베르트 ┃
┃ ohyesoo@hitel.net ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
사실 델파이에서 그림을 인쇄하는 일을 아주 쉽습니다. 그냥 그림출력함수
나 Canvas.Draw또는 Canvas.StretchDraw 만 가지고도 그릴 수 있습니다.
그러나 몇 가지 문제가 있습니다.
윈도를 16컬러로 세팅이 되어있는 상태에서 트루컬러로 되어있는 그림을 출
력하면 그림은 자동으로 16컬러로 변환되어 그려집니다. 아주 몰골이 되고
말지요~ 이유는 간단합니다. 출력을 할때 파레트를 따로 처리해 주지 않으면
자동으로 시스템의 파레트가 적용이 되기때문입니다. 그럼 어케 하느냐~
파레트를 원래 그림이 가지고 있는 색으로 적용시켜서 출력을 하면 됩니다.
그렇게 할 수 있는 API함수가 있는데 바로 StretchDIBits입니다.
다음 예제는 StretchDIBits를 이용해서 비트맵을 출력하는 예입니다.
StretchDIBits의 앞부분은 StretchDIBits를 사용하기위해서 인자값들을 준
비하는 부분이므로 StretchDIBits함수 한 개만 잘 사용할 줄 알면 되겠습니다.
procedure PrintBmp( ARect: TRect; ABitmap: TBitmap );
var
Info: PBitmapInfo;
InfoSize: DWORD;
Image: Pointer;
ImageSize: DWORD;
Bits: HBITMAP;
DIBWidth, DIBHeight: Longint;
begin
with Printer, Canvas do
begin
Bits := ABitmap.Handle;
GetDIBSizes( Bits, InfoSize, ImageSize );
Info := AllocMem( InfoSize );
try
Image := AllocMem( ImageSize );
try
GetDIB( Bits, ABitmap.Palette, Info^, Image^ );
with Info^.bmiHeader do
begin
DIBWidth := biWidth;
DIBHeight := biHeight;
end;
StretchDIBits( Printer.Canvas.Handle,
ARect.Left, ARect.Top, ARect.Right, ARect.Bottom,
0, 0, DIBWidth, DIBHeight,
Image, Info^, DIB_RGB_COLORS, SRCCOPY );
finally
FreeMem( Image, ImageSize );
end;
finally
FreeMem( Info, InfoSize );
end;
end;
end;
// 이 루틴은 TForm.Print 메소드를 수정한것입니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 계속