ActiveDelphi - Índice do Fórum ActiveDelphi
.: O site do programador Delphi! :.
 
 FAQFAQ   PesquisarPesquisar   MembrosMembros   GruposGrupos   RegistrarRegistrar 
 PerfilPerfil   Entrar e ver Mensagens ParticularesEntrar e ver Mensagens Particulares   EntrarEntrar 

Como imprimir um arquivo PDF diretamente, informando a impre

 
Novo Tópico   Responder Mensagem    ActiveDelphi - Índice do Fórum -> Delphi
Exibir mensagem anterior :: Exibir próxima mensagem  
Autor Mensagem
Nysp
Novato
Novato


Registrado: Terça-Feira, 4 de Mai de 2010
Mensagens: 22

MensagemEnviada: Qua Abr 24, 2019 8:26 am    Assunto: Como imprimir um arquivo PDF diretamente, informando a impre Responder com Citação

Bom dia a todos,

estou há alguns dias já pesquisando uma maneira de imprimir um arquivo em PDF no qual eu informo a impressora e o arquivo seja impresso sem ser aberto e sem ter nenhum leitor de PDF já instalado no computador (como Adobre Reader ou Foxit, por exemplo).

O grande problema é que, dentre vários exemplos testados, muitos abrem o arquivo antes. Por exemplo, aqui mesmo no forum achei essa thread ( www.activedelphi.com.br/forum/viewtopic.php?t=30397&sid=2ca143582633ad471599ca04ffdbc262 ) e ela funciona bem. Porém, em computadores com Windows 10 que tenham Adobe instalado, ele abre uma instância (vazia) do Adobe, fora que eu preciso utilizar a função de setar a impressora padrão antes e depois voltar a impressora correta do usuário, já que nem sempre a padrão é a que ele quer imprimir.

Pensei em usar o Ghostscript mas, assim como nas pesquisas que citei acima, está bem complicado encontrar uma maneira de apenas impressão. Vejo muitos artigos do Ghostscript ensinando a criar PDF, juntar vários PDF, convertê-lo para extensões de imagem, mas não acho um que apenas imprima o arquivo sem abrí-lo como quero. Pensei em usar a dll "gsdll32.dll" para meu objetivo mas não encontrei nada em exemplos.

Meu grande problema é que os clientes da minha empresa tem ambientes muito variados. Como falei, alguns sequer possui leitor de PDF instalado (usam pelo navegador) e, como já tenho o arquivo PDF no caminho que quero, gostaria apenas de imprimí-lo em impressora pré-determinada em um ini ou configuração de meu sistema sem que o usuário necessite nenhum clique adicional.

Alguém conhece uma maneira?

Me desculpa caso já haja outra thread a respeito, procurei e não encontrei especificamente o que quero.

PS: estou utilizando Delphi 10 Seattle com Fast Report (que, segundo seu suporte, não imprime PDF's não gerado pelo próprio).

Agradeço!
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular
strak2012
Colaborador
Colaborador


Registrado: Segunda-Feira, 13 de Janeiro de 2014
Mensagens: 1518
Localização: Maceió - AL

MensagemEnviada: Qui Abr 25, 2019 2:20 am    Assunto: Responder com Citação

com o uso do ghostscript fica mais ou menos assim:

Código:
const
  GS_ARG_ENCODING_LOCAL = 0;
  GS_ARG_ENCODING_UTF8 = 1;
  e_Quit = -990;

type

  Targvs = array of pansichar;

  TGSAPIrevision = packed record
    product: PChar;
    copyright: PChar;
    revision: longint;
    revisiondat: longint;
  end;

  PGSAPIrevision = ^TGSAPIrevision;
  Pgs_main_instance = Pointer;
  PPChar = array of PChar;

  { funções e procedure da dll gsdll32.dll }
function gsapi_new_instance(pinstance: Pgs_main_instance;
  caller_handle: Pointer): Integer; stdcall; external 'gsdll32.dll';
function gsapi_init_with_args(pinstance: Pgs_main_instance; argc: Integer;
  argv: PPChar): Integer; stdcall; external 'gsdll32.dll';
function gsapi_exit(pinstance: Pgs_main_instance): Integer; stdcall;
  external 'gsdll32.dll';
procedure gsapi_delete_instance(pinstance: Pgs_main_instance); stdcall;
  external 'gsdll32.dll';
function gsapi_set_arg_encoding(pinstance: Pgs_main_instance; ENCODING: Integer)
  : Integer; stdcall; external 'gsdll32.dll';

procedure addArg(var argv: Targvs; value: ansistring);
begin
  setlength(argv, length(argv) + 1);
  argv[high(argv)] := pansichar(value);
end;

function ImprimirPdf(impressora: string; filename: string): Integer;
var
  code, code1, gsargc: Integer;
  gsargv: Targvs;
  minst: PGSAPIrevision;
begin
  addArg(gsargv, 'gs');
  addArg(gsargv, '-dPrinted');
  addArg(gsargv, '-dNoCancel=true');
  addArg(gsargv, '-dBATCH');
  addArg(gsargv, '-dNOPAUSE');
  addArg(gsargv, '-dNOSAFER');
  addArg(gsargv, '-dNumCopies="' + inttostr(1) + '"');
  addArg(gsargv, '-sDEVICE=mswinpr2');
  addArg(gsargv, '-sOutputFile=%printer%"' + impressora + '"');
  addArg(gsargv, '"' + filename + '"');
  gsargc := length(gsargv);
  code := gsapi_new_instance(@minst, nil);
  if (code < 0) then
  begin
    result := 1;
    exit;
  end;
  code := gsapi_set_arg_encoding(minst, GS_ARG_ENCODING_UTF8);
  if (code = 0) then
    code := gsapi_init_with_args(minst, gsargc, @gsargv[0]);
  code1 := gsapi_exit(minst);
  if ((code = 0) or (code = e_Quit)) then
    code := code1;
  gsapi_delete_instance(minst);
  if ((code = 0) or (code = e_Quit)) then
  begin
    result := 0;
    exit;
  end;
  result := 1;
end;

procedure TForm2.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then
    ImprimirPdf('nome da impressora aqui', OpenDialog1.filename);
end;



caso chame a função ImprimirPdf sem informar o nome da impressora o ghostscript vai abrir uma janela de escolha da impressora similar a esta aqui:


caso contrario vai imprimir diratamente na impressora informada.

lembrete não esqueça te de a gsdll32.dll no mesmo diretorio da aplicação ou na pasta sistem32.

não testei com impressora em rede apenas impressora local.
_________________
Tudo podemos quando tudo sabemos!
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular Enviar E-mail MSN Messenger
Nysp
Novato
Novato


Registrado: Terça-Feira, 4 de Mai de 2010
Mensagens: 22

MensagemEnviada: Sex Abr 26, 2019 8:30 am    Assunto: Responder com Citação

Primeiro, quero agradecer o strak2012. Ele me ajudou demais, não só aqui no Forum, mas por Skype também. Realmente, obrigado!

Para quem se interessar, segue o que resultou pra mim. Há poucas mudanças em relação ao que o strak2012, mas há algumas considerações a se fazer. O código final foi esse:

Código:
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Printers;

const
  GS_ARG_ENCODING_LOCAL = 0;
  GS_ARG_ENCODING_UTF8 = 1;
  e_Quit = -990;

type
  Targvs = array of pansichar;

  TGSAPIrevision = packed record
    product: PChar;
    copyright: PChar;
    revision: longint;
    revisiondat: longint;
  end;

  PGSAPIrevision = ^TGSAPIrevision;
  Pgs_main_instance = Pointer;
  PPChar = array of PChar;

  TForm1 = class(TForm)
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    ComboBox1: TComboBox;
    procedure Button1Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
    procedure addArg(var argv: Targvs; value: ansistring);
  public
    { Public declarations }
    function ImprimirPdf(impressora: string; filename: string): Integer;
  end;

var
  Form1: TForm1;

implementation

{ funções e procedure da dll gsdll32.dll }
function gsapi_new_instance(pinstance: Pgs_main_instance;
  caller_handle: Pointer): Integer; stdcall; external 'gsdll32.dll';
function gsapi_init_with_args(pinstance: Pgs_main_instance; argc: Integer;
  argv: PPChar): Integer; stdcall; external 'gsdll32.dll';
function gsapi_exit(pinstance: Pgs_main_instance): Integer; stdcall;
  external 'gsdll32.dll';
procedure gsapi_delete_instance(pinstance: Pgs_main_instance); stdcall;
  external 'gsdll32.dll';
function gsapi_set_arg_encoding(pinstance: Pgs_main_instance; ENCODING: Integer)
  : Integer; stdcall; external 'gsdll32.dll';

{$R *.dfm}

{ TForm1 }

procedure TForm1.addArg(var argv: Targvs; value: ansistring);
begin
  setlength(argv, length(argv) + 1);
  argv[high(argv)] := pansichar(value);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then
    ImprimirPdf(ComboBox1.Text, OpenDialog1.filename);
end;

procedure TForm1.FormShow(Sender: TObject);
var
  i: Integer;
  vPrinter: TPrinter;
begin
  ComboBox1.Items.Clear;

  vPrinter := TPrinter.Create;
  for i := 0 to vPrinter.Printers.Count - 1 do
    ComboBox1.Items.Add(vPrinter.Printers.Strings[i]);

  FreeAndNil(vPrinter);
end;

function TForm1.ImprimirPdf(impressora, filename: string): Integer;
var
  code, code1, gsargc: Integer;
  gsargv: Targvs;
  minst: PGSAPIrevision;
begin
  addArg(gsargv, 'gs');
  addArg(gsargv, '-dPrinted');
  addArg(gsargv, '-dNoCancel=true');
  addArg(gsargv, '-dBATCH');
  addArg(gsargv, '-dNOPAUSE');
  addArg(gsargv, '-dNOSAFER');
  addArg(gsargv, '-dNumCopies="' + inttostr(1) + '"');
  addArg(gsargv, '-sDEVICE=mswinpr2');
  //addArg(gsargv, '-sOutputFile=%printer%"' + impressora + '"');
  addArg(gsargv, '-sOutputFile="\\spool\' + impressora + '"');
  addArg(gsargv, '"' + filename + '"');

  gsargc := length(gsargv);
  code := gsapi_new_instance(@minst, nil);
  if (code < 0) then
  begin
    result := 1;
    exit;
  end;

  code := gsapi_set_arg_encoding(minst, GS_ARG_ENCODING_UTF8);

  if (code = 0) then
    code := gsapi_init_with_args(minst, gsargc, @gsargv[0]);

  code1 := gsapi_exit(minst);

  if ((code = 0) or (code = e_Quit)) then
    code := code1;

  gsapi_delete_instance(minst);

  if ((code = 0) or (code = e_Quit)) then
  begin
    result := 0;
    exit;
  end;

  result := 1;
end;

end.


Importante dizer que, para testar esse exemplo enviando o nome de uma impressora específica, deve-se gerar versão Release e testar diretamente do executável. Usando versões com debug, ao passar pela seguinte linha, aponta um access violation:

Código:
code := gsapi_new_instance(@minst, nil);


Usando diretamente pelo executável e com versão Release, não acontece.

A versão da gsdll32.dll que usei foi a 0.0.9.27. Segundo o strak2012, com ele também funcionou na 0.0.9.23.

Outra alternativa que testamos também foi sem usar o Ghostscript, porém ela requer sempre algum PDf reader na máquina (como Adobe Reader, Foxbit etc). Para quem abre o PDF pelo navegador, ela já não funciona. Estou postando o código caso seja interesse de alguém usar assim, mesmo com a ressalva. A vantagem dela é usar componentes nativos e não necessitar de DLL extra para o funcionamento:

Código:
procedure TForm1.printFile(AFile: TFileName; APrinter: string);
var
  Device: array[0..255] of Char;
  Driver: array[0..255] of Char;
  Port: array[0..255] of Char;
  S: string;
  hDeviceMode: THandle;
  i: Integer;
  vPrinter : TPrinter;
begin
  Printer.PrinterIndex := -1;

  for i := 0 to Printer.Printers.Count - 1 do
  begin
    if Printer.printers[i] = APrinter then
    begin
      vPrinter := TPrinter.Create;
      vPrinter.PrinterIndex := i;
      vPrinter.GetPrinter(Device,Driver,Port,hDeviceMode);
      S := Format('"%s" "%s" "%s"',[Device,Driver,Port]);
      ShellExecute(Application.Handle,'printto',PChar(AFile),PChar(S),nil,SW_HIDE);
      vPrinter.Free;
    end;
  end;
end;


Sobre essa função, é bom observar que o parâmetro enviado para o ShellExecute é 'printto'. Usando somente 'print', pelo menos no meu caso, sempre saía na impressora padrão.

Estou usando Delphi 10 Seattle em um Windows 10 64 bits. Não sei o comportamento em outras plataformas.

Agradeço ao strak2012 novamente!

Abraços
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular
Mostrar os tópicos anteriores:   
Novo Tópico   Responder Mensagem    ActiveDelphi - Índice do Fórum -> Delphi Todos os horários são GMT - 3 Horas
Página 1 de 1

 
Ir para:  
Enviar Mensagens Novas: Proibido.
Responder Tópicos Proibido
Editar Mensagens: Proibido.
Excluir Mensagens: Proibido.
Votar em Enquetes: Proibido.


Powered by phpBB © 2001, 2005 phpBB Group
Traduzido por: Suporte phpBB