Смена режима двусторонней печати в середине отчёта
Сергей Гаврилов
Ростов-на-Дону
Здравствуйте,
Мне понадобилось печатать разнородный пакет документов, часть из которых должна быть напечатана с одной стороны листа, а остальные - с двух. Достаточно быстро я обнаружил, что некоторые (все?) драйверы принтеров игнорируют смену режима двусторонней печати в середине задания на печать. Поиск в интернете тоже не дал обнадёживающих результатов.
Можно было конечно формировать и печатать документы в два захода (отдельно односторонние и отдельно двусторонние). Но пользователю удобнее увидеть весь пакет документов в окне предварительного просмотра за один раз и потом сразу распечатать его.
После некоторых размышлений я написал процедуру, которая разбивает уже сформированные страницы из окна предварительного просмотра на группы в соответствии с режимом двусторонней печати и печатает их в отдельных заданиях. Возможно, кто-то тоже наехал на пень двусторонней печати, поэтому я решил поделиться своим решением.
Процедура использует свойство PrintOptions.PageNumbers поэтому, если пользователь указал печатаемые страницы, то использовать её не следует. В этом случае, ответственность за правильность печати ложится на пользователя. Также, не стоит её вызывать, если пользователь принудительно установил режим двусторонней/односторонней печати в диалоге принтера.
Можно организовать вызов данной процедуры из события OnPrintReport для печати из стандартного окна предварительного просмотра (я так и сделал). Но для этого требуется небольшая корректировка одной процедуры FastReport для правильного освобождения памяти после исключения (кстати, её неплохо бы исправить в любом случае). Если кому-то это интересно, я приведу код и инструкции в отдельном сообщении.
Всем удачи,
Сергей
Мне понадобилось печатать разнородный пакет документов, часть из которых должна быть напечатана с одной стороны листа, а остальные - с двух. Достаточно быстро я обнаружил, что некоторые (все?) драйверы принтеров игнорируют смену режима двусторонней печати в середине задания на печать. Поиск в интернете тоже не дал обнадёживающих результатов.
Можно было конечно формировать и печатать документы в два захода (отдельно односторонние и отдельно двусторонние). Но пользователю удобнее увидеть весь пакет документов в окне предварительного просмотра за один раз и потом сразу распечатать его.
После некоторых размышлений я написал процедуру, которая разбивает уже сформированные страницы из окна предварительного просмотра на группы в соответствии с режимом двусторонней печати и печатает их в отдельных заданиях. Возможно, кто-то тоже наехал на пень двусторонней печати, поэтому я решил поделиться своим решением.
Процедура использует свойство 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.