打印机的DPI和页面长和宽(100分)

  • 主题发起人 zhouming2
  • 开始时间
Z

zhouming2

Unregistered / Unconfirmed
GUEST, unregistred user!
我想通过GetDeviceCaps函数得到当前打印机的DPI和长宽.但没有成功,请
大虾们给我一个完整的Demo.
 
Printer有属性:pageHeight,pagewidth
getdevicecaps(printer.handle,logpixelsx);
getdevicecaps(printer.handle,logpixelsy);
 
printer.pageheight,pagewidth是当前页的长宽, 而且不准(如果没有打印过, 这
是个很大的数, 大约为2m*10m)
logpixelsx,logpixelsy是打印机每inch对应screen的点数.
如果要取得分辨率, 参数是physicalwidth, physicalheight吧?

如果要取得页长和页宽, 可通过两次调用DocumentProperties得到:
第一次调用fMode为0取得devmode的字节数, 然后分配内存, 第二次调用fMode的
参数DM_IN_BUFFER,取得所有devicemode参数. 页长页宽信息也包括在当中了.

同样如果要程序中动态设置打印页长度和宽度可以通过第三次调用DocumentProperties
实现, fMode的值为DM_OUT_BUFFER, 只要把DevMode中dmFields置为
DM_PAGEWIDTH or DM_PAGELENGTH or DM_PAGESIZE,
然后置相应的pagesize域为0, pagewidth,pagelength域设为你想修改的值即可.
具体可参考win32.hlp中关于DevMode的描述.
 
如果我没记错的话,printer的pagewidth和pageheight是以像数恒量的,指的是可绘的区域(扣除那些页边距等),physicalwidth(physicalheight)也是指像数点数,指的是物理纸张的总的大小(相比printer的pagewidth(pageheight)要加上页的边距)这就是说它会显示很大,如果你的打印机的DPI设得高的话,logpixelsx(y)就是指DPI(dots per inch),因此当你打一个位图时,要注意一下区域大小的变换,有时当位图较大而打印机的dpi设得很高,如720,1440,用delphi中TBitmap的stretchdraw往往打不出来(在win16下我用D1有这情况,95情况不知道),这时要直接用到API,举个打图的例子,希望对你有所帮助:
//以下的例子适用于高分辩率的打印机,我不知道有没有比一般屏幕分辨率小的打印机
//如针打
procedure DrawImage(Canvas: TCanvas;
DestRect: TRect;

ABitmap:TBitmap);//打印一个位图
var
Header, Bits: Pointer;
HeaderSize: Integer;
BitsSize: Longint;
begin

GetDIBSizes(ABitmap.Handle, HeaderSize, BitsSize);
GetMem(Header,HeaderSize);
GetMem(Bits,BitsSize);
try
GetDIB(ABitmap.Handle, ABitmap.Palette, Header^, Bits^);
StretchDIBits(Canvas.Handle,
DestRect.Left,
DestRect.Top,
DestRect.Right-DestRect.Left+1,
DestRect.Bottom-DestRect.Top+1,
0,
0,
ABitmap.Width,
ABitmap.Height,
Bits,
TBitmapInfo(Header^),
DIB_RGB_COLORS,
SRCCOPY);
finally
FreeMem(Header, HeaderSize);
FreeMem(Bits, BitsSize);
end;

end;

procedure PrintABmp(ABitmap:TBitmap)
//假设ABitmap是一个已有内容的位图,下面将该位图按屏幕大小(一倍的比例)打印
//在纸的正中间
var
DestRect:TRect;
//打在纸上的区域
XScale,YScale:Integer;
//并不精确的比例,有必要的话可以用浮点数
begin

Printer.begin
Doc;
XScale:=GetDeviceCaps(Printer.Handle,LOGPIXELSX) div
Screen.PixelsPerInch;
YScale:=GetDeviceCaps(Printer.Handle,LOGPIXELSY) div
Screen.PixelsPerInch;
DestRect.Left:=(Printer.PageWidth-ABitmap.Width*XScale) div 2;
DestRect.Top:=(Printer.PageHeight-ABitmap.Height*YScale) div 2;
DestRect.Right:=DestRect.Left+ABitmap.Width*XScale;
DestRect.Bottom:=DestRect.Top+Abitmap.Height*YScale;
DrawImage(Printer.Canvas,DestRect,ABitmap);
Printer.EndDoc;
end;

对于字体,也是一样要注意一下比例,否则当打印机的dpi设高的话,还按屏幕的字体
大小打,会很小的.
 
另外,如果你想知道纸张传统意义的大小(指以cm或inch表示),用二个属性除一下
即可,我不知道直接获得的方法,如果有请哪位大虾告知.
下面取得纸张的大小,以Inch为单位,若要转化为cm,则换算为1 inch=2.54 cm

纸的宽度:=GetDeviceCaps(Printer.Handle,PHYSICALWIDTH)/
GetDeviceCaps(Printer.Handle,LOGPIXELSX);

纸的高度:=GetDeviceCaps(Printer.Handle,PHYSICALHEIGHT)/
GetDeviceCaps(Printer.Handle,LOGPIXELSY);

对于如何取得纸的表达,如A4,B5,我不会,因为通常这些定义是有标准,试试匹配一下
不知可否,但纸的种类太多,我一直为这问题困闹.
 
谢谢大虾们的高见,我正在实验,稍后我会再次提问.
 
通常针式打印机没有不可打印区域,而激光或喷墨打印机都有不可打印区域,即
左上部分和右下部分有一定区域不可打印
下面该函数功能是将给出的具体长度的边界值转化为打印机左上角和右下交的相应的DPI数
并且考虑了不可对应的边界问题
请试用
//其中Rect给出的是左边界,上边距,右边距,下边距,单位为0.1毫米
procedure GenerateRect(var outRect : PRect);
var
changeTopY,changeleftX,ChangeRightX,ChangeBottomY : Real;
nTotalPixelsY,nTotalm,tmpPageHeight,tmpPageWidth,nTotalPixelsX : longint;
nPerPixels100mm : longint;//每100毫米的象素数
begin

Screen.Cursor := crHourGlass;

try
Printer.begin
Doc;

//设置打印字体
With Printer.Canvasdo

begin

Font.Name:='宋体';
Font.Size:=10;

Font.Style:=[];
end;

////////////////////////////////调节边界
//设置映射方式
SetMapmode(printer.canvas.Handle,mm_Text);
nTotalPixelsY := GetDevicecaps(Printer.Canvas.handle,VERTRES);//打印机垂直象素数
nTotalPixelsX := GetDevicecaps(Printer.Canvas.handle,HORZRES);//打印机水平象素数
nTotalm := GetDevicecaps(Printer.Canvas.handle,VERTSIZE);//打印机可用长度,毫米为单位
nPerPixels100mm := Round(nTotalPixelsY*100/nTotalm);//每100毫米对应的象素数,取100主要是为了增加分辨率
tmpPageWidth := GetDevicecaps(Printer.Canvas.handle,PHYSICALWIDTH);//完全水平长度
tmpPageHeight := GetDevicecaps(Printer.Canvas.handle,PHYSICALHEIGHT);//完全竖直长度
changeTopY := GetDevicecaps(Printer.Canvas.handle,PHYSICALOFFSETY);//不可打印上边界
ChangeleftX := GetDevicecaps(Printer.Canvas.handle,PHYSICALOFFSETX);
//不可打印左边界
ChangeRightX := tmpPageWidth - nTotalPixelsX - ChangeleftX;//不可打印右边界
ChangeBottomY := tmpPageHeight - nTotalPixelsY - changeTopY;//不可打印下边界
//原RECT结构为0.1毫米结构,
//现在将其转化为左上角和右下角的DPI数
outRect^.left := Round((OutRect^.left*nPerPixels100mm/1000-ChangeLeftX));
OutRect^.Top := Round((OutRect^.Top*nPerPixels100mm/1000-ChangeTopY));
OutRect^.Right := Round((tmpPageWidth - OutRect^.Right*nPerPixels100mm/1000 - ChangeRightX));
OutRect^.Bottom := Round((tmpPageHeight-OutRect^.Bottom*nPerPixels100mm/1000 - ChangeBottomY));
//打印TCHART
Chart1.Printpartial(outRect^);
Printer.EndDoc;//打印结束
except
on Exceptiondo
// 如果出错
begin

Printer.Abort;
end;

end;

Screen.Cursor:=crDefault;
end;


 
谢谢各位大虾:

我在激光打印机上打印条码,图象和汉字(混排),因打印机的DPI较高,图象打印
效果不好,我应该在程序中怎样处理,才能适应不同DPI以及图象的低DPI和打印机的
高DPI.


 
这和如何避免图像的放大后失真类似,因为在这种情况下打印,在打印纸上是用
几个点方阵来代表一个点的,这样就会出现马塞克现象,如果dpi相差较大而打印
机又没有好的处理方法.
你可以临时生成一个和将要打印在打印纸上一样大小的位图,再用一定的算法从
原始位图生成该位图的数据,再将该位图以一倍的比例打印到纸上去,可以避免
这种情况.

 
既然是打印条码, 有必要作为图象来打印吗? 先画到某个bitmap再把bitmap画到打印
机的canvas上还不如直接生成到打印机的canvas上.
这样文字部分用textout, 条码部分用lineto不就解决了(用屏幕像素坐标即可)?
也不存在什么分辨率的问题了.
 
这是因为Printer的DC一般也是用像数来衡量的(缺省的MM_TEXT方式),当它的DPI
高的话,那在一英寸内容纳的点的个数就比屏幕上的多,而我们显示的图它的大小一般
也是以像数来衡量的,假设一个512*512大小的图在屏幕上占5inch*5inch大小,在
一个高DPI打印机上的占的区域就会小很多,看上去就好像图像缩小了,所以要将原
图像放大,才能达到和屏幕上显示的效果一样,线条,字都是一样的,当然可以用
StretchDraw的方法来打到printer上的一个区域,但由于StretchDraw的方法只是
简单的用几个点的方阵取同样的值来表示原图上一个点的值(放大),这样当放大的比
例大的情况下,就会出现马塞克现象,因此要采用一些算法来避免,常用到的是线性插值.
我不清楚这个条行码是如何来表示的,如果获得的是图像,就要用到上述的方法,如果是仅仅
用线条来表示,确实不需要那么繁琐.
 
此问题可分两部分回答:
1、Delphi的TPrinter检测出的打印机宽度高度,与设置的纸张类型有关。用API
GetDeviceCaps可检测出实际打印机的能力。记得Delphi深度历险中有一个很
好的控件,可在95及NT下动态设置纸张类型,可能对你有用。
2、在TPrinter的Canvas上直接绘图,默认情况下使用的是像素,可使用API
SetMapMode设置成强制影射模式,可按照实际尺寸绘图,Windows可自动
根据打印机的分辨率实现所见既所得的输出效果,在屏幕上也可采用这种
方式,并且一个程序即可实现。
 
两个办法: 1, Escape( printer.handle, GETPHYSPAGESIZE, 0, nil, @physsize );可取得纸张的实际尺寸(点数,与打印机分辨率有关,把它转为
以Twips为单位就是实际尺寸)
2. pixelsperinchx := GetDeviceCaps( printer.handle, LOGPIXELSX );
Longint(printer.pagewidth) * 1440) div pixelsperinchx
可得到打印机可打印的范围尺寸(已转为Twips)。
以后打印都使用Twips为单位就与任何打印机甚至屏幕分辨率无关了。
 
多人接受答案了。
 
顶部