Как правильно печатать любую информацию (растровые и векторные изображения), а также как сделать режим предварительного просмотра?

Материал из DRKB

Как правильно печатать любую информацию (растровые и векторные изображения), а также как сделать режим предварительного просмотра?


Маленькое предисловие.[править | править код]

Т.к. основная моя работа связана с написанием софта для института, обрабатывающего геоданные, то и в отделе, где работаю, так же мучаются проблемами печати (в одном случае - надо печатать каpты, с изолиниями, заливкой, подписями и пр.; в другом случае - свои таблицы и сложные отрисовки по внешнему виду).

В итоге, моим коллегой был написан кусок, в котором ему удалось добиться качественной печати в двух режимах: MetaFile, Bitmap.

Работа с MetaFile у нас сложилась уже исторически - достаточно удобно описать функцию, которая что-то отрисовыват (хоть на экране, хоть где), которая принимает TCanvas, и подсовывать ей то канвас дисплея, то канвас метафайла, а потом этот Metafile выбрасывать на печать.

Достаточно решить лишь проблемы масштабирования, после чего - вперед.

Главная головная боль при таком методе - при отрисовке больших кусков, которые занимают весь лист или его большую часть, надо этот метафайл по размерам делать сpазу же в пикселах на этот самый лист. Тогда при изменении размеров (просмотр пеpед печатью) - искажения при уменьшении не критичны, а вот при увеличении линии и шрифты не "поползут".

Итак :

Набор идей, которые были написаны (с) Андреем Аpистовым, программистом отдела матобеспечения СибНИИНП, г. Тюмень. Моего здесь только - приделывание сверху надстроек для личного использования.


Вся работа сводится к следующим шагам :[править | править код]

  1. Получить необходимые коэффициенты.
  2. Построить метафайл или bmp для последующего вывода на печать.
  3. Напечатать.

Hиже приведенный кусок (прошу меня не пинать, но писал я и писал для достаточно кpивой реализации с передачей параметров через глобальные переменные) я использую для того, чтобы получить коэффициенты пересчета.

kScale - для пересчета размеров шрифта, а потом уже закладываюсь на его размеры и получаю два новых коэффициента для kW, kH - которые и позволяют мне с учетом высоты шрифта выводить графику и пр. У меня при работе kW <> kH, что приходится учитывать.


Решили пункт 1.[править | править код]

procedure SetKoeffMeta; // установить коэф-ты
var
  PrevMetafile: TMetafile;
  MetaCanvas: TMetafileCanvas;
begin
  PrevMetafile := nil;
  MetaCanvas := nil;
  try
    PrevMetaFile := TMetaFile.Create;
    try
      MetaCanvas := TMetafileCanvas.Create(PrevMetafile, 0);
      kScale := GetDeviceCaps(Printer.Handle, LOGPIXELSX) / Screen.PixelsPerInch;
      MetaCanvas.Font.Assign(oGrid.Font);
      MetaCanvas.Font.Size := Round(oGrid.Font.Size * kScale);

      kW := MetaCanvas.TextWidth('W') / oGrid.Canvas.TextWidth('W');
      kH := MetaCanvas.TextHeight('W') / oGrid.Canvas.TextHeight('W');
    finally
      MetaCanvas.Free;
    end;
  finally
    PrevMetafile.Free;
  end;
end;


Решаем 2.[править | править код]

var
  PrevMetafile: TMetafile;
  MetaCanvas: TMetafileCanvas;
begin
  PrevMetafile := nil;
  MetaCanvas := nil;

  try
    PrevMetaFile := TMetaFile.Create;
    PrevMetafile.Width := oWidth;
    PrevMetafile.Height := oHeight;

    try
      MetaCanvas := TMetafileCanvas.Create(PrevMetafile, 0);

      // здесь должен быть ваш код - с учетом масштабирования.
      // я эту вещь вынес в ассигнуемую процедуру, и данный блок
      // вызываю лишь для отрисовки целой страницы.

      // см. PS1 (ниже)

    finally
      MetaCanvas.Free;
    end;
//...


PS1
Код, который используется для отрисовки. oCanvas - TCanvas метафайла.
var
  iHPage: Integer; // высота страницы
begin
  with oCanvas do
  begin
    iHPage := 3000;

    // залили область метайфайла белым - для дальнейшей работы
    Pen.Color := clBlack;
    Brush.Color := clWhite;
    FillRect(Rect(0, 0, 2000, iHPage));

    // установили шрифты - с учетом их дальнейшего масштабирования
    oCanvas.Font.Assign( oGrid.Font);
    oCanvas.Font.Size := Round( oGrid.Font.Size * kScale );

    //...
    xEnd := xBegin;
    iH := Round(RowHeights[iRow] * kH);
    for iCol := 0 to ColCount - 1 do
    begin
      x := xEnd;
      xEnd := x + Round(ColWidths[iCol] * kW);
      Rectangle(x, yBegin, xEnd, yBegin + iH);
      r := Rect(x + 1, yBegin + 1, xEnd - 1, yBegin + iH - 1);
      s := Cells[iCol, iRow];

      // выписали в полученный квадрат текст
      DrawText(oCanvas.Handle, PChar(s), Length(s), r, DT_WORDBREAK or DT_CENTER);


Главное, что важно помнить на этом этапе - это не забывать, что все выводимые объекты должны пользоваться описанными коэффициентами (как вы их получите - это уже ваше дело). В данном случае - я работаю с переделанным TStringGrid, который сделал для многостраничной печати.

Последний пункт[править | править код]

Надо сформированный метафайл или bmp напечатать.

var
  Info: PBitmapInfo;
  InfoSize: Integer;
  Image: Pointer;
  ImageSize: DWORD;
  Bits: HBITMAP;
  DIBWidth, DIBHeight: Longint;
  PrintWidth, PrintHeight: Longint;
begin
  case ImageType of
    itMetafile:
    begin
      if Picture.Metafile <> nil then
        Printer.Canvas.StretchDraw(Rect(aLeft, aTop, aLeft + fWidth,
                 aTop + fHeight), Picture.Metafile);
    end;

    itBitmap:
    begin
      if Picture.Bitmap <> nil then
      begin
        with Printer, Canvas do
        begin
          Bits := Picture.Bitmap.Handle;
          GetDIBSizes(Bits, InfoSize, ImageSize);
          Info := AllocMem(InfoSize);
          try
            Image := AllocMem(ImageSize);
            try
              GetDIB(Bits, 0, Info^, Image^);

              with Info^.bmiHeader do
              begin
                DIBWidth := biWidth;
                DIBHeight := biHeight;
              end;
              PrintWidth := DIBWidth;
              PrintHeight := DIBHeight;
              StretchDIBits(Canvas.Handle, aLeft, aTop, PrintWidth,
                        PrintHeight, 0, 0, DIBWidth, DIBHeight, Image, Info^,
                        DIB_RGB_COLORS, SRCCOPY);
            finally
              FreeMem(Image, ImageSize);
            end;
          finally
            FreeMem(Info, InfoSize);
          end;
        end;
      end;
    end;
  end;


В чем заключается идея PreView? Остается имея на руках Metafila, Bmp - отрисовать с пересчетом внешний вид изображения (надо высчитать левый верхний угол и размеры "предварительно просматриваемого" изображения.

Для показа изображения достаточно использовать StretchDraw.

После того, как удалось вывести объекты на печать, проблему создания PreView решили как "домашнее задание".

Кстати, когда мы работаем с Bmp, то для просмотра используем следующий хинт - записываем битовый обpаз через такую процедуру:

   w := MulDiv(Bmp.Width, GetDeviceCaps(Printer.Handle, LOGPIXELSX), Screen.PixelsPerInch);
   h := MulDiv(Bmp.Height, GetDeviceCaps(Printer.Handle, LOGPIXELSY), Screen.PixelsPerInch);
   PrevBmp.Width := w;
   PrevBmp.Height := h;
   PrevBmp.Canvas.StretchDraw(Rect(0, 0, w, h), Bmp);
   aPicture.Assign(PrevBmp);


При этом масштабируется битовый обpаз с минимальными искажениями, а вот при печати - приходится bmp печатать именно так, как описано выше.

Итог - наша bmp при печати чуть меньше, чем печатать ее через WinWord, но при этом - внешне - без каких-либо искажений.

Imho, я для себя проблему печати решил. Hа основе вышесказанного, сделал PreView для myStringGrid, где вывожу сложные многострочные заголовки и прочее на несколько листов, осталось кое-что допилить, но с принтером у меня проблем не будет уже точно :)

PS. Кстати, Андpей Аpистов на основе своей наработки сделал сложные геокарты, которые по качестве не хуже, а может и лучше, чем выдает Surfer (специалисты поймут). Hа ватмат.

PPS. Прошу прощения за возможные стилистические неточности - время вышло, охрана уже ругается. Hо код - выдран из работающих исходников.

Борисов Олег Николаевич (ZB)
panterra@sbtx.tmn.ru
(2:5077/5)


Author: Борисов Олег Николаевич (ZB)
Source: Взято с сайта http://blackman.wp-club.net/
ID: 03225