【问题标题】:Delphi - How to add multiple JPG images to a TCanvas and print themDelphi - 如何将多个 JPG 图像添加到 TCanvas 并打印它们
【发布时间】:2020-07-29 07:44:14
【问题描述】:

作为一名 Delphi 程序员,我有点像 hack - 在此过程中学习点点滴滴,主要是自学。

我正在开发一个有趣的程序,它是一个绝版纸牌游戏的数据库。包含卡片信息和卡片图片 JPG 的链接。

正在与某人谈论它,他们说“如果您可以在 2.5 x 3.5 英寸的页面上渲染多个卡片图像并打印它们,那不是很酷”。

除了法律问题(未经前出版商明确许可,我不会分发此内容),我想看看是否可以将其作为练习来教我如何使用 TCanvas。不幸的是,我真的不知道该怎么做。

我正在处理一个 8.5 x 11 英寸的页面,所以我想要一个 2.3 x 3.5 的卡片图像,它可以让我玩大约一英寸,而不是被卡片图像占用,所以就像 0.25 英寸的页边距一样每个图像之间大约 0.25 英寸。这将在页面上放置 9 张卡片。

对于我目前在程序中编写的内容,我可以将 TImage 拖放到表单上,然后从磁盘读取任何给定卡的关联 jpg 文件并将其显示在表单上。

假设我已经用这些卡片构建了一副卡片,并且想要打印我的“卡片”的图像。然后我如何为每张卡片获取关联的图像,调整它的大小以使其适合画布上的 2.5 x 3.5 空间,然后继续加载图像,直到页面上有 9 个。然后我想移动到一个新的页面,然后再做一次,直到我把卡片打印在我的牌组中(通常大约 50 张 - 所以大约 6 页)。

我一直在做一些搜索,但我不确定如何处理这些方面

  • 读取后调整 JPG 大小
  • 将其放置在画布上 - 总共可以获得 9 张图片

当前图像(大部分)为 390 x 546 像素。 (这是 2.5 x 3.5 英寸的正确比例)。

另外,为了尽量节省墨水和纸张,有没有办法在将画布发送到打印机之前“预览”画布(如果您愿意,可以进行打印预览),还是只是一个巨大的试验和错误直到你做对为止。

虽然首选特定代码示例,但如果我什至可以获得指向显示如何使用这些对象的网站的指针,我愿意做一些跑腿工作。

大约 15 年前,我把 Text 放到 TCanvas 上并打印它,但我不确定如何在从磁盘读取然后将其调整大小以打印到我想要​​的比例后使用 JPG 进行打印.

谁能在这里为我提供一些指点?

哦,如果重要的话,我正在使用 Delphi XE3 Enterprise / Windows 10。

谢谢。

【问题讨论】:

  • 研究StretchBlt WinAPI 调用以将一个 DC (canvas.handle) 上的内容绘制到另一个 DC 到确定的位置和大小。
  • 你可能会在这里找到一些灵感:stackoverflow.com/a/60366803/282848
  • 只需在画布上绘制 TPicture,使用适当的位置和缩放比例,使用常规的 VCL 命令。并不比在画布上写文字更难。请至少显示您尝试的一些代码。比将您的问题分为两个:一个关于打印 Jpeg,另一个关于预览。预览很简单:只需在 TPaintBox 画布上绘制即可。请注意,两者都已经在 SO 或网络上得到了回答,当然。你至少谷歌了吗?
  • 虽然可以在打印机画布上渲染多个图像,但我认为对于您需要的内容,仅使用 Delphi 附带的报告组件之一(如 Fast Report)可能会更容易例如创建所需的打印报告。这种方法的主要优点是几乎所有报表组件都支持生成打印预览,无需额外代码,更不用说某些报表组件允许您以与设计表单类似的方式直观地设计打印报表。
  • “为了尽量节省墨水和纸张”。当我想在不支持打印预览的应用程序中预览文档时,我总是打印到 PDF 或 XPS 打印机。主要是我用它来尝试我自己的打印程序。大多数现代 Windows PC 都有这样的虚拟打印机驱动程序。

标签: delphi delphi-xe3


【解决方案1】:

有一些粗鲁的方法可以让它发挥作用。

首先,我看到至少有两种方法可以做到:

  • 将所有图像加载到数组或 ObjectList 中(我想这更好),然后进行转换并在画布上绘制它们;
  • 一张一张地加载、转换和绘制图像;

哪一个更好?我不知道。我更喜欢第二个,因为我想它会占用更少的内存。

uses ...,jpeg, SysUtils, Graphics;

...

const CARD_WIDTH:integer = /*your card width*/;
const CARD_HEIGHT:integer = /*your card height*/;
const MARGIN_X:integer = 0; //you can change it if you want to
const MARGIN_Y:integer = 0; //you can change it if you want to
...

//Load images. You can call it from button's OnClick event;
function LoadImages(aWidth, aHeight:integer):TBitmap;
var i:integer;
    lImage:TJpegImage;
    lResizedBmp:TBitmap;
    lPosX,lPosY:integer; //it can be TPoint as well       
begin
result := TBitmap.Create;
lPosX := MARGIN_X;
lPosY := MARGIN_Y;
try
result.Width := aWidth;
result.Height := aHeight;
//I don't know how you will get filenames, so I'll make some dummy code
for i := 0 to 10 do
   begin
      lImage := TJpegImage.Create;
      lResizedBmp := nil;
      try
         lImage.LoadFromFile('C:\' + inttostr(i) + '.jpg'); //it's bad code just to demonstrate the way we load file. I don't remember if "i.toString()" is acceptable in XE3.
         lResizedBmp := ResizeJpeg(lImage, CARD_WIDTH, CARD_HEIGHT);
         result.Canvas.Draw(lPosX, lPosY, lResizedBmp);
         //let's calculate next card position
         lPosX := lPosX + CARD_WIDTH + MARGIN_X;
         if (lPosX + CARD_WIDTH + MARGIN_X > aWidth) then
            begin
            lPosX := MARGIN_X;
            lPosY := lPosY + MARGIN_Y + CARD_HEIGHT;
            end; 
      finally
         FreeAndNil(lImage); 
         if assigned(lResizedBmp) then
            FreeAndNil(lResizedBmp)
      end;
   end;
except 
  on (e:Exception) do
   begin
      FreeAndNil(result);
      raise e;
   end; 
end;
end;

//Resize image and convert it into Bitmap
function ResizeJpeg(aJpg:TJpegImage; aWidth, aHeight:integer):TBitmap;
var lProxyBmp:TBitmap;
begin
result := TBitmap.Create;
try
   result.Width := aWidth;
   result.Height := aHeight;

   lProxyBmp := TBitmap.Create;
   try
      lProxyBmp.Assign(aJpg);    
      result.Canvas.StretchDraw(result.Canvas.ClipRect, lProxyBmp);
   finally
      FreeAndNil(lProxyBmp);
   end;

except
   on e:Exception do
   begin
      FreeAndNil(result);
      raise e;
   end;
end;
end;

因此,您拥有制作页面的所有程序。只需制作 Form,将 TImage 和两个按钮放在其中。将 TImage 的 StretchedProportional 属性设置为 true。将按钮的captions 设置为LoadPrint。不要忘记将带有过程的单元添加到uses 或将它们作为Form 的方法。将私有字段_bmp:TBitmap 添加到您的Form

对于Load 按钮:

//Page preview. 
procedure TForm1.Button1Click(Sender:TObject)
begin
if assigned(_bmp) then
   FreeAndnIl(_bmp);

_bmp := LoadImages(2000,3000);
Image1.Picture.Assign(_bmp);
end;

对于Print 按钮:

procedure TForm1.Button2Click(Sender:TObject)
begin
if not assigned(_bmp) then
begin
  ShowMessage('Click "Load" first');
  Exit;
end;

with TPrintDialog.Create(nil) do
try
if not Execute then
  Exit;
finally
  Free;
end;

Printer.BeginDoc;
try
   Printer.Canvas.Draw(0,0,_bmp);
finally
   Printer.EndDoc;
end;
end;

在我使用 PDF 打印机在 Delphi 10.1 上检查此代码后,我得到了我的 pdf 文件。

我试图让它尽可能简单,但是代码很多。我可能会错过一些东西,但我愿意提供帮助。所有常量都可以作为变量,作为函数的参数传递,这取决于你。

【讨论】:

  • 感谢您的回复,我会在接下来的一两天内玩这个,看看我能用它做什么。
  • 我拿了这段代码,玩了一下,让它运行并生成了一个 PDF……太棒了。我对我不太了解的事情有一些疑问。它似乎使用单个图像的压缩版本加载 TImage,然后将图像移动到打印机画布并打印它。如果我试图打印 50 张卡片,每页 9 张(3x3 格式)将是正确的,可以执行 printer.begindoc / 加载前 9 张图像 /printer.NewPage / 加载接下来的 9 张直到完成,然后是 Printer.EndDoc ?
  • 我不确定 LoadImages(2000,3000) 在做什么。我的图像文件在磁盘上是 390 像素宽和 546 像素高。我想每页打印 3 个和 3 个。标准的 8.5 x 11 英寸页面为 215 x 279 毫米。 1 毫米是 3.7795275591 像素(如果我的 google-fu 是正确的)。大约是 815 x 1054 像素。为了保持比例,假设我将图像的大小调整为 240 x 336 - 如果放置在具有该大小的页面上,我会得到 3 个横向和 3 个向下。在发布了这个额外的问题后,我正在玩它,看看我是否可以解决它,但您的指导将不胜感激
  • 我曾尝试将 TImage 的大小设置为 815 x 1054 像素,但是在将输出打印到 PDF 时,即使表单上的 Timage 大部分已被填充,它也不会填满整个页面。已经很晚了,该睡觉了,但是如果您有指点来克服我在这里缺少的东西,我将不胜感激
  • 是的,您可以使用Printer.NewPage
猜你喜欢
  • 2015-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-10
  • 1970-01-01
  • 2018-10-19
  • 1970-01-01
相关资源
最近更新 更多