[Delphi] Exportando relatórios em QuickReport para PDF com o Synopse

[Delphi] Exportando relatórios em QuickReport para PDF com o SynopseOlá, amigos programadores!
Hoje, o objetivo é apresentar uma maneira rápida, simples e funcional de exportar relatórios elaborados em QuickReport para PDF, sem instalações de componentes de terceiros e sem a necessidade de instalar a versão Professional do componente. Quer saber como? Acompanhe!

 

Eu tenho um projeto de controle de pedidos desenvolvido em Delphi, no qual um dos recursos do software é gerar arquivos PDF dos pedidos para que os vendedores possam enviá-los por e-mail. Foi aí que a minha história com essa funcionalidade começou.

Primeiro, tentei utilizar a classe TQRPDFDocumentFilter da versão Professional do QuickReport, mas não fiquei satisfeito com a qualidade do documento gerado. Depois, tomei conhecimento do ExportPack (TExportQR), porém, não encontrei versões atualizadas deste componente para a família XE. Em seguida, tentei o componente Gnostice, mas, além de pago, o componente trouxe várias funcionalidades adicionais que eu não precisava. Ah, e sem contar que me deparei com algumas dificuldades na instalação.

Bom, continuei pesquisando, pesquisando… e encontrei o Synopse! É apenas uma biblioteca que deve ser vinculada ao projeto para gerar o PDF, sem complexidades.
Vamos fazer na prática?

O primeiro passo é baixar as bibliotecas neste link e descompactá-las em uma pasta específica de bibliotecas, como, por exemplo, “C:\BibliotecasDelphi”.
O próximo passo é apontar esse caminho no Library Path do Delphi para que as bibliotecas sejam encontradas na compilação. Para isso, no Delphi, clique no menu Tools > Options. Ao abrir a janela, navegue até Delphi Options > Library e clique no botão de reticências do Library Path. Agora, encontre o caminho da pasta de bibliotecas e clique em Add, conforme a imagem abaixo:

Incluir Synopse no Library Path do Delphi
 

O terceiro passo é adicionar a biblioteca SynPdf na seção uses do projeto. Adicione também a unit Printers, caso ainda não esteja adicionada:

uses
  SynPdf, Printers;

Pronto! Agora é só partir para a codificação:

var
  // variáveis necessárias para a geração do PDF
  oPDF: TPDFDocument;
  oMetaDados: TMetafile;
  oPosicionamento: TPDFCanvasRenderMetaFileTextPositioning;
  nPagina: integer;
begin
  // cria uma instância do documento PDF
  oPDF := TPDFDocument.Create(True, 0, False, nil);
 
  // inicializa a variável de metadados
  oMetaDados := nil;
  try
    // configura o tipo do papel
    oPDF.DefaultPaperSize := psA4;
 
    // configura o posicionamento dos caracteres
    oPosicionamento := tpKerningFromAveragePosition;
 
    // configura o layout de visualização do documento
    oPDF.Root.PageLayout := plOneColumn;
 
    // ajusta a orientação do documento, caso o relatório esteja em paisagem
    if QuickRep1.Page.Orientation = poLandscape then
    begin
      oPDF.DefaultPageLandscape := True;
    end;
 
    // prepara o relatório
    QuickRep1.Prepare;
 
    // carrega os metadados de cada página do relatório
    for nPagina := 1 to QuickRep1.QRPrinter.PageCount do
    begin
      // adiciona uma nova página no documento
      oPDF.AddPage;
 
      // carrega os metadados da página
      oMetaDados := QuickRep1.QRPrinter.PageList.GetPage(nPagina);
 
      // renderiza os metadados
      oPDF.Canvas.RenderMetaFile(oMetaDados, 1, 0, 0, oPosicionamento);
 
      // limpa a variável de metadados
      oMetaDados := nil;
    end;
 
    // salva o arquivo
    oPDF.SaveToFile('C:\Relatorios\Relatorio.pdf');
  finally
    // libera as variáveis
    oMetaDados.Free;
    oPDF.Free;
  end;
end;

Problema resolvido, pessoal! 🙂

Clique neste link e baixe um exemplo dessa funcionalidade desenvolvido em Delphi XE7, mas deixo uma observação: para que o exemplo funcionasse em qualquer computador, coloquei todos arquivos do Synopse na mesma pasta do projeto, ok?

Para tirar o máximo de proveito da biblioteca, recomendo que as outras propriedades das classes sejam exploradas, como os parâmetros de criação do “TPDFDocument”, tratamento de fontes e os tipos de posicionamento de caracteres. Mesmo assim, em caso de dúvidas, deixe um comentário!

 

Abraços e até a próxima!


 

Compartilhe!
Share on FacebookTweet about this on TwitterShare on LinkedInShare on Google+Pin on PinterestEmail this to someone

25 comentários

    1. Olá, William, tudo bem?
      Sim, as versões mais recentes do Delphi (salvo engano, a partir da versão XE3), já disponibiliza uma plataforma para desenvolvimento de projetos mobile. Infelizmente tive pouco contato com esse recurso, mas aparentemente é funcional.

      Abraço!

  1. Boa noite, tudo beleza?

    Interessante a sua explicação, fiz uns teste e consegui gerar o relatório usando o Delphi 6 e QuickReport 3.0.9, só que nos relatórios aqui da empresa, o total de páginas também aparece no cabeçalho, isto é, página 1/5.

    No seu exemplo, como você faria para incluir o total de páginas no cabeçalho do relatório? Seria usando a instrução QuickRep1.QRPrinter.PageCount ?

    1. Olá, Airton, como vai?
      Isso mesmo, utilize as seguintes propriedades:
      QuickReport.PageNumber para acessar o número da página atual;
      QuickReport.QRPrinter.PageCount para acessar a quantidade total de páginas.

      Ou então, utilize o componente TQRSysData e configure a propriedade Data para qrsPageNumber. Ao abrir o relatório, este componente exibirá automaticamente o número da página atual.

      Abraço!

    2. Boa tarde,

      Então no momento da impressão do relatório, os comandos usados são:

      frmAlmox.QuickRep1.Prepare;
      frmAlmox.v_total_pagina:=frmAlmox.QuickRep1.QRPrinter.PageCount;
      frmAlmox.QuickRep1.Preview;

      Onde v_total_pagina é uma variável criada no form do relatório, e no evento beforeprint da band de detalhe tem a linha abaixo:

      QRL_TotPaginas.Caption:=IntToStr(QuickRep1.PageNumber) + ' / ' + IntToStr(v_total_pagina);

      É dessa forma que é criada a informação com a página corrente / total de páginas.

      Como você faria para colocar o total de páginas conforme exemplo acima?

    3. Olá, Airton.
      É justamente dessa forma mesmo. Tenho esse mesmo código no meu projeto.
      Primeiro, obtemos a quantidade de página após o Prepare e atribuímos à uma label no relatório. Depois, no evento BeforePrint, exibimos a página atual.

    4. Entendi, o detalhe é que impressão normal sem gerar o PDF, o total de páginas aparece corretamente, mas na impressão do PDF aparece zerado.
      Pelo que percebi, para o PDF a variável não esta assumindo o valor.

  2. Olá André, meus parabéns pelo código. Ficou muito bom!! Principalmente porque este procedimento faz com seja impresso diretamente em PDF sem reimprimir o relatório, como acontece com os relatórios impressos em impressoras virtuais.
    Minha contribuição para o seu código é a seguinte: Caso queiram utilizar o procedimento para exportar em PDF após o relatório já estar impresso na tela, segue código abaixo. Para este exemplo abaixo, considerem o seguinte cenário: Uma unit com um TQRPREVIEW onde todos os relatórios do sistema são visualizados nesta unit, ou seja, todos os relatórios são gerados no mesmo lugar, acoplados ao QRPREVIEW. Um botão para exportar em PDF e o procedimento segue abaixo para exportar qualquer relatório já impresso na tela.
    //Código abaixo é quase 100% o código do André, apenas sem o prepare já que já está impresso e considerando as properties direto QRPREVIEW.

    procedure ExportToPDF;
    var
      // variáveis necessárias para a geração do PDF
      oPDF: TPDFDocument;
      oMetaDados: TMetafile;
      oPosicionamento: TPDFCanvasRenderMetaFileTextPositioning;
      nPagina: integer;
    begin
      // cria uma instância do documento PDF
      oPDF := TPDFDocument.Create(True, 0, False, nil);
      // inicializa a variável de metadados
      oMetaDados := nil;
      try
        // configura o tipo do papel
        oPDF.DefaultPaperSize := psA4;
        // configura o posicionamento dos caracteres
        oPosicionamento := tpKerningFromAveragePosition;
        // configura o layout de visualização do documento
        oPDF.Root.PageLayout := plOneColumn;
        // ajusta a orientação do documento, caso o relatório esteja em paisagem
        if qrprvwPadrao.QRPrinter.Orientation = poLandscape then
        begin
          oPDF.DefaultPageLandscape := True;
        end;
        // carrega os metadados de cada página do relatório
        for nPagina := 1 to qrprvwPadrao.QRPrinter.PageCount do
        begin
          // adiciona uma nova página no documento
          oPDF.AddPage;
          // carrega os metadados da página
          oMetaDados := qrprvwPadrao.QRPrinter.PageList.GetPage(nPagina);
          // renderiza os metadados
          oPDF.Canvas.RenderMetaFile(oMetaDados, 1, 0, 0, oPosicionamento);
          // limpa a variável de metadados
          oMetaDados := nil;
        end;
        // salva o arquivo
        oPDF.SaveToFile(dlgSaveDialog.FileName);
      finally
        // libera as variáveis
        oMetaDados.Free;
        oPDF.Free;
      end;
    end;

    // onde qrprvwPadrao é o TQRPREVIEW e dlgsavedialog é um TSaveDialog para pedir ao usuário o local onde salvar o PDF.

    1. Perfeito, Leonardo!
      Agradeço fortemente pela visita e pelo tempo dedicado para deixar essa contribuição!
      Também uso o TQRPreview para elaborar visualizadores personalizados e disponibilizar recursos adicionais, como opções de exportação e envio do relatório por e-mail através de uma integração com o Microsoft Outlook. Dá pra fazer bastante coisa! 🙂

      Grande abraço!

  3. Olá, boa tarde!
    Estou usando o delphi para gerar relatório, uso o TQuickRep, adicionei o QRPDFFilter para salvar em PDF, Porém estou notando que esta sumindo todos os acentos e caracteres especiais.

  4. Bom dia ,

    Gostei muito do post , porem estou com dificuldade de agregar uma imagem ao pdf ,poderia me ajudar?

    1. Bom dia, Pedro, tudo bem?
      Não fiz o teste, mas, se existir uma imagem no relatório (QuickReport), você testou se o Synopse consegue exportá-la?

      Abraço!

  5. Opa obrigado pela resposta. Estava utilizando o QRComposite, depois que coloquei cada relatório separado funcionou certinho. Só ficou meio ruim de trocar a fonte do relatório. Mudava pelo comando SetFontAndSize, porém nada acontecia… no final tive que tirar a justificação do parágrafo para imprimir de forma correta. De qualquer forma obrigado, vou dar uma olhada em seu site para ver algumas outras postagens.

  6. André, gostei muito de sua solução, mas não consigo fazer funcionar no Delphi 2010.
    Acho que tudo que se refere ao QRPrinter não funciona = QuickRep1.QRPrinter.PageCount retorna zero e

    oMetaDados := QuickRep1.QRPrinter.PageList.GetPage(nPagina); retorna nil.

    Pode me ajudar?

  7. Oi André,

    Obrigado por responder.

    Utilizo no Delphi 2010 o QuickReport 5.04.2.

    Temos 2 tipos de relatórios no Quick:

    Um para Boletos e Danfe que é uma caixa tradicional

    de banda, foi aí que eu testei o Synopse (Boleto bancário).

    Se aí eu conseguir já me dou por satisfeito, mas deu erro…

    oMetaDados := QuickRep1.QRPrinter.PageList.GetPage(nPagina); retorna nil.

    O Outro tipo é quase em toda a aplicação, de modo

    personalizado e sempre crio o relatório desta forma.

    Inclusive mudei a forma pois no Delphi 5 não usava

    o TQRPrinter.Create(RelItem);

    Assim:

    RelItem := TQuickRep.Create(nil);
    Rel := TQRPrinter.Create(RelItem);

    BeginDoc;

    ….

    EndDoc;
    Preview;

  8. Segue código para ter um botão que salva PDF em todos os relatórios, alterando o QRprev.
    Criando uma procedure genérica, mais ou menos abaixo.
    Mudança importante no typecast:

    (qrprinter.ParentReport as TQuickRep).Page.Orientation = poLandscape then

    A seguir:

    uses ...., SynPdf;
    
    procedure TQRStandardPreview.CriarPDF;
    var
      // variáveis necessárias para a geração do PDF
      oPDF: TPDFDocument;
      oMetaDados: TMetafile;
      sdialog: TSaveDialog;
      oPosicionamento: TPDFCanvasRenderMetaFileTextPositioning;
      nPagina, findx: integer;
      sext, savefile : string;
    begin
    
      try
    
        // cria uma instância do documento PDF
        oPDF := TPDFDocument.Create(True, 0, False, nil);
    
        // inicializa a variável de metadados
        oMetaDados := nil;
    
        // configura o tipo do papel
        oPDF.DefaultPaperSize := psA4;
    
        // configura o posicionamento dos caracteres
        oPosicionamento := tpKerningFromAveragePosition;
    
        // configura o layout de visualização do documento
        oPDF.Root.PageLayout := plOneColumn;
    
        // ajusta a orientação do documento, caso o relatório esteja em paisagem
        if (qrprinter.ParentReport as TQuickRep).Page.Orientation = poLandscape then
        begin
          oPDF.DefaultPageLandscape := True;
        end;
    
        // prepara o relatório
    
        // carrega os metadados de cada página do relatório
        for nPagina := 1 to QRPreview.QRPrinter.PageCount do
        begin
          // adiciona uma nova página no documento
          oPDF.AddPage;
    
          // carrega os metadados da página
          oMetaDados := QRPreview.QRPrinter.PageList.GetPage(nPagina);
    
          // renderiza os metadados
          oPDF.Canvas.RenderMetaFile(oMetaDados, 1, 0, 0, oPosicionamento);
    
          // limpa a variável de metadados
          oMetaDados := nil;
        end;
    
        sdialog := TSaveDialog.Create(Application);
        try
          sdialog.Title := SqrSaveReport;
          sdialog.Filter := '.PDF|*.PDF';
    
          sdialog.Filename := '*.PDF';
          if not sdialog.Execute then exit;
    
          sext := ExtractFileExt(sdialog.FileName);
          savefile := sdialog.FileName;
          sext := upperCase(sext);
          // enforce an extension
          if sext = '' then
          begin
            findx := sdialog.FilterIndex-1;
            if findx = 0 then
               sext := '.PDF'
            else if findx = 1 then
               sext := 'PDF';
    
            if sext[1] = '.' then sext := copy( sext, 2, 3 );
            savefile := savefile + '.' + sext;
          end;
    
          if sext[1] = '.' then sext := copy( sext, 2, 3 );
    
          if sext='PDF' then
          begin
            // salva o arquivo
            oPDF.SaveToFile(savefile);
            exit;
          end;
    
        finally
          sdialog.Free;
        end;
    
      finally
        // libera as variáveis
        oMetaDados.Free;
        oPDF.Free;
      end;
    end;
    1. Adriano, muito obrigado por ter dedicado um tempo para retornar aqui no blog e deixar a sua contribuição!
      O seu código certamente será de grande ajuda para os desenvolvedores que acessarem este artigo.

      Abração!

  9. André, bom dia. Tenho alguns sistemas e utilizo 2 sistemas em Delphi XE8 e tenho 2 sistemas em Delphi 5. Um deles esta migrando para Delphi XE8. Estou precisando de uma soçução para gerar o relatório em PDF no Delphi 5 (enquanto não migra, essa solução vai funcionar no Delphi 5. Se não, você conhece alguma solução para Delphi 5?

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Preencha o campo abaixo * Time limit is exhausted. Please reload CAPTCHA.