Смена режима двусторонней печати в середине отчёта

Сергей ГавриловСергей Гаврилов Ростов-на-Дону
отредактировано 22:05 Раздел: FastReport VCL
Здравствуйте,

Мне понадобилось печатать разнородный пакет документов, часть из которых должна быть напечатана с одной стороны листа, а остальные - с двух. Достаточно быстро я обнаружил, что некоторые (все?) драйверы принтеров игнорируют смену режима двусторонней печати в середине задания на печать. Поиск в интернете тоже не дал обнадёживающих результатов.

Можно было конечно формировать и печатать документы в два захода (отдельно односторонние и отдельно двусторонние). Но пользователю удобнее увидеть весь пакет документов в окне предварительного просмотра за один раз и потом сразу распечатать его.

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

Процедура использует свойство PrintOptions.PageNumbers поэтому, если пользователь указал печатаемые страницы, то использовать её не следует. В этом случае, ответственность за правильность печати ложится на пользователя. Также, не стоит её вызывать, если пользователь принудительно установил режим двусторонней/односторонней печати в диалоге принтера.

Можно организовать вызов данной процедуры из события OnPrintReport для печати из стандартного окна предварительного просмотра (я так и сделал). Но для этого требуется небольшая корректировка одной процедуры FastReport для правильного освобождения памяти после исключения (кстати, её неплохо бы исправить в любом случае). Если кому-то это интересно, я приведу код и инструкции в отдельном сообщении.

Всем удачи,
Сергей
unit Unit2;

interface

uses
  Classes,
  frxClass;

function PrintMixedDuplex(AFRXReport: TfrxReport; const APrompt: Boolean = False): Boolean;

implementation

uses
  Dialogs, SysUtils;

const

  SComma = ',';

type
  TRGDDuplexModes = set of TfrxDuplexMode;

resourcestring

  SRGDDuplexHorizontal    = 'короткой';
  SRGDDuplexVertical      = 'длинной';
  SRGDPrintDuplex         = 'Последующие страницы документа будут печаться с двух сторон листа; ' +
                            'переворот будет выполняться вдоль %s стороны листа.';
  SRGDPrintSimplex        = 'Оставшиеся страницы документа будут печаться с одной стороны листа.';

function PrintMixedDuplex(AFRXReport: TfrxReport; const APrompt: Boolean): Boolean;
var
  ppp, saveDlg: Boolean;
  saveEvt: TNotifyEvent;
  pnLists: array [TfrxDuplexMode] of String;

  function get_pn_lists: Integer;
  var
    i, n1, n2: Integer;
    pg: TfrxReportPage;
    dm: TfrxDuplexMode;

    procedure add_range(const dm: TfrxDuplexMode; const n1, n2: Integer);
    begin
      if n2 > n1 then
        pnLists[dm] := pnLists[dm] + SComma + Format('%d-%d', [n1, n2])
      else if n2 > 0 then
        pnLists[dm] := pnLists[dm] + SComma + IntToStr(n2);
    end;

  begin
    Result := 0;
    for dm:=Low(pnLists) to High(pnLists) do
      pnLists[dm] := '';
    n1 := 1;
    n2 := 0;
    dm := dmNone;
    {---
      Сформировать диапазоны печатаемых страниц для каждого режима двусторонней печати.
    ---}
    with AFRXReport.PreviewPages do
      for i:=0 to Count-1 do
        begin
          pg := Page[i];
          if pg.Visible then
            begin
              if pg.Duplex = dmNone then
                pg.Duplex := dmSimplex;

              if pg.Duplex = dm then  // Расширить текущий диапазон на одну страницу.
                n2 := i + 1
              else                    // Добавить диапазон в список и начать формирование нового.
                begin
                  add_range(dm, n1, n2);
                  dm := pg.Duplex;
                  n2 := i + 1;
                  n1 := n2;
                end;
            end;
        end;
    {---
      Добавить последний диапазон страниц в список.
    ---}
    add_range(dm, n1, n2);
    {---
      Проверить и подсчитать непустые списки диапазонов страниц.
    ---}
    for dm:=Low(pnLists) to High(pnLists) do
      if pnLists[dm] <> '' then
        begin
          Delete(pnLists[dm], 1, 1);
          Inc(Result);
        end;
  end;

  function print_pages(const dm: TfrxDuplexMode; const APrompt: Boolean): Boolean;
  var
    msg: String;
  begin
    Result := (pnLists[dm] <> '');
    if not Result then
      Exit;

    if APrompt then
      begin
        case dm of
          dmHorizontal:
            msg := Format(SRGDPrintDuplex, [SRGDDuplexHorizontal]);
          dmVertical:
            msg := Format(SRGDPrintDuplex, [SRGDDuplexVertical]);
          else
            msg := SRGDPrintSimplex;
        end;
        MessageDlg(msg, mtInformation, [mbOK], 0);
      end;

    with AFRXReport do
      begin
        PrintOptions.PageNumbers := pnLists[dm];
        Print;
      end;
  end;

begin
  Result := False;
  with AFRXReport do
    begin
      {---
        Получить списки номеров страниц, подготовленных для печати: отдельно для односторонней
        печати и отдельно - для двух видов двусторонней.
        Если присутствует более одной разновидности страниц, то печатать их по отдельности.
        В противном случае, отчёт следует распечатать обычным образом (вне данной функции).
      ---}
      if get_pn_lists > 1 then
        begin
          {---
            Не отображать диалог драйвера принтера.
          ---}
          saveDlg := PrintOptions.ShowDialog;
          PrintOptions.ShowDialog := False;
          {---
            Отключить обработчик события во избежание рекурсии.
          ---}
          saveEvt := OnPrintReport;
          OnPrintReport := nil;
          try
            {---
              Двусторонняя печать
            ---}
            ppp := print_pages(dmHorizontal, False);
            if print_pages(dmVertical, APrompt and ppp) then
              ppp := True;
            {---
              Односторонняя печать
            ---}
            print_pages(dmSimplex, APrompt and ppp);
          finally
            {---
              Востановить исходные значения свойств.
            ---}
            OnPrintReport := saveEvt;
            PrintOptions.ShowDialog := saveDlg;
            PrintOptions.PageNumbers := '';
          end;
          Result := True;
        end;
    end;
end;

end.

Оставить комментарий

Многофункциональный текстовый редактор. Чтобы отредактировать стиль параграфа, нажмите TAB, чтобы перейти к меню абзаца. Там вы можете выбрать стиль. По умолчанию не выбран ни один стиль. Когда вы выберете текст, появится встроенное меню форматирования. Нажмите TAB, чтобы войти в него. Некоторые элементы, такие как многофункциональные вставки ссылок, картинок, индикаторов загрузки и сообщений об ошибок могут быть вставлены в редактор. Вы можете перемещаться по ним, используя стрелки внутри редактора и удалять с помощью клавиш delete или backspace.