BoletoNet / boletonet

Boleto.Net is a library developed for use in Brazil, given it has been programmed with Brazilian retail legislation and business rules for bank registered billing integration.
Apache License 2.0
479 stars 382 forks source link

Impressão / criação de PDF de vários boletos #165

Open lucascollina opened 8 years ago

lucascollina commented 8 years ago

Boa tarde a todos!

Vocês tem algum exemplo de como gerar o html de vários boletos de uma única vez?

Ou também poderia ser a criação de um único PDF com todos os boletos.

Agradeço o contato!

aasf86 commented 8 years ago

Olá Lucas, eu acabei de implementar isso em meu projeto. Vou colocar o código em meu github e te manda

carloscds commented 8 years ago

@aasf86 Mande o código para o repositorio aqui do projeto. Faça um PR.

aasf86 commented 8 years ago

Olá Carlos, bom o que fiz foi um verdadeiro POG pra resolver isso, não sei se vale a pena estar no projeto. O 'boletoBancario.MontaBytesPDF()' estava demorando muito pra retornar os bytes. Aí tive que apelar para a linha de comando usando o http://wkhtmltopdf.org/ e que foi muito, mas muito mais rápido. voce já deve estar imaginando o que fiz né, sim eu fiz isso rsrsrs. Vou preparar o codigo e repasso. Abraços

carloscds commented 8 years ago

@aasf86 O problema de linha de comando é com aplicação web.

EltonRst commented 8 years ago

Eu gostaria da ajuda de vocês com essa questão de vários boletos no mesmo PDF, eu até consegui um modo de fazer em web em que eu Monto o HTML de todos os Boletos e Mando para View por uma ViewBag uso a Rotativa PDF para fazer o PDF porém os Boletos Ficam todos Amontoados sem quebra de Página e não centralizam também .

lucascollina commented 8 years ago

@EltonRst eu solucionei da seguinte forma:

Dim htmlBoletos As New StringBuilder

htmlBoletos.Append("<div style=""page-break-after: always;"">") htmlBoletos.Append(htmlBoleto) htmlBoletos.Append("</div>")

Se quiser centralizar o boleto é só acrescentar no style da div "margin: 0 auto;".

lucascollina commented 8 years ago

O meu maior problema hoje está sendo a demora na montagem do PDF da mesma forma que o nosso amigo @aasf86 reportou.

Alguém conseguiu solucionar?

EltonRst commented 8 years ago

@lucascollina Eu Fiz da seguinte forma No Controller em C#:

public ActionResult Boletos()
{
List<BoletoBancario> Boletos = new List<BoletoBancario>(); // Crio a Lista e Adiciono 
//todos os boletos pulei essa parte do cógido por que é massante, dai após:
string html = string.Empty;
            foreach (BoletoBancario boleto in Boletos)
            {
                html += string.Concat("<center>", boleto.MontaHtml(), "</center>");
            }
ViewBag.Boletos = html;
var pdf = new ViewAsPdf  /// Essa é a Parte do Rotativa PDF
                 {
                    ViewName = "_Boletos",
                    FileName = "Boletos.pdf",
                    PageSize = Size.A4,
                    IsGrayScale = true,
                    PageMargins = new Margins { Bottom = 15, Left = 5, Right = 5, Top = 10 }
                };
                return File(pdf.BuildPdf(this.ControllerContext), "application/pdf");
}

/// Já na View fiz Assim

@{
    Layout = null;
}
<html lang="pt-br">
    <head>
        <meta charset="utf-8" />
        <title>Boletos</title>
        <style type="text/css">
            @@media print { //@@ Por causa do Razor do MVC mas no css é só @
                body > center {
                    page-break-after: always;
                }

                    body > center:last-child {
                        page-break-after: always;
                    }
            }
        </style>
    </head>
    <body>
        @Html.Raw(ViewBag.Boletos)
    </body>
</html>

Funciona Perfeitamente, testei com 10 boletos, cerca de 2 segundos, só o boleto fica menor, as margens da direita e da esquerda ficam com 2 dedos de largura em branco mas de boa.

EltonRst commented 8 years ago

@lucascollina estou implementando algumas Melhorias no Projeto Boleto.Net para podermos gerar os boletos em lote. assim que eu tiver algo eu te aviso, mas esse ai de cima funciona porém não ocupa toda a largura da folha e usa mais essa biblioteca chamada Rotativa PDF, mas quebra o galho.

lucascollina commented 8 years ago

Esse Rotativa só funciona em MVC?

aasf86 commented 8 years ago

Lucas, foi mal cara, ainda não te enviei por que estou muito corrido cara, esta semana eu dou um jeito de te enviar cara. @lucascollina

EltonRst commented 8 years ago

@lucascollina Apenas em MVC, ela não converte somente o HTML, quem faz isso é o NReco PDF, que inclusive está incluso no projeto do Boleto.Net, Estou trabalhando em uma possível solução para a quebra de página a cada boleto, já conseguí alterar o Boleto.Net para Receber uma lista de Boletos, o resto é rápido.

EltonRst commented 8 years ago

@lucascollina Consegui Finalmente fazer as alterações no Projeto Boleto.Net segue o Método Utilizado na Classe BoletoBancario.cs

/// <summary>
        /// Lista de Boletos, objetos do tipo
        /// BoletoBancario
        /// </summary>
        /// <param name="boletos">Lista de Boletos, objetos do tipo BoletoBancario</param>
        /// <param name="tituloNaView">Título Que aparecerá na Aba do Navegador</param>
        /// <param name="convertLinhaDigitavelToImage">bool Converter a Linha Digitavel Em Imagem</param>
        /// <returns>byte[], Vetor de bytes do PDF</returns>
public byte[] MontaBytesListaBoletosPDF(List<BoletoBancario> boletos, string tituloNaView = "", bool convertLinhaDigitavelToImage = false)
{
            string htmlHead = string.Concat(
                "<html>",
                "<head><title>", tituloNaView, "</title>",
                "<style type='text/css' media='screen,print'>",
                    ".break{ display: block; clear: both; page-break-after: always;}",
                "</style>",
                "</head>",
                "<body>"
            );
            string htmlBoletos = string.Empty;
            foreach (BoletoBancario boleto in boletos)
            {
                htmlBoletos += string.Concat("<div class='break'>", boleto.MontaHtmlEmbedded(convertLinhaDigitavelToImage, true), "</div>");
            }
            string htmlFooter = "</body></html>";
            return (new NReco.PdfGenerator.HtmlToPdfConverter()).GeneratePdf(string.Concat(htmlHead, htmlBoletos, htmlFooter));
}

Só implementar esse Método no seu Boleto.Net, Compliar e Utilizar a DLL Eu Implementei ele Logo Baixo do Método original

public byte[] MontaBytesPDF(bool convertLinhaDigitavelToImage = false)
{
            return (new NReco.PdfGenerator.HtmlToPdfConverter()).GeneratePdf(this.MontaHtmlEmbedded(convertLinhaDigitavelToImage, true));
}

Modo de Uso:

public ActionResult Boletos()
{
            // Lista que Receberá os Boletos
            List<BoletoBancario> Boletos = new List<BoletoBancario>();

            /// Monte seus Boletos Bancários e Adicione Na lista depois Divirta-se

        return File(new BoletoBancario().MontaBytesListaBoletosPDF(Boletos, "Boletos Bancários"), "application/pdf", "Boletos.pdf");
}

Caso queira também pode baixar Minha DLL Compilada http://eltonrst.serveblog.net/Upload/downloader.php?Arquivo=Boleto.Net.zip Aqui o Arquivo BoletoBancario.cs http://eltonrst.serveblog.net/Upload/downloader.php?Arquivo=BoletoBancario.zip

Eu só começei a usar o Boleto.Net por que eu preciso de um pouco de agilidade no trabalho, só estou usando para gerar os Boletos em PDF mesmo, pra Remessa / Retorno eu Mesmo Desenvolví meus Layouts de Banco, Estou no Processo de Validação, por eunquanto eu tenho o Itau e o BB Validados.

lucascollina commented 8 years ago

@EltonRst Obrigado pela postagem, será de grande ajuda.

Entretanto, estou com problemas referente ao tamanho do boleto.

Ao gerar o arquivo PDF o boleto não ocupa a página toda.

Teve esse problema também? Ou da forma como você implementou o boleto fica certo?

Westfallx commented 8 years ago

Posso dar uma sugestão, na parte do código abaixo prq não alteram para stringbuilder? Por exemplo minha aplicação por volta de uns 1000 boletos por vez, fazendo essa alteração tive uma melhora na performance.

htmlBoletos += string.Concat("

", boleto.MontaHtmlEmbedded(convertLinhaDigitavelToImage, true), "
"); }

EltonRst commented 8 years ago

@lucascollina da forma que implementei os boletos ficam corretos, utilizei a mesma lógica do modo original que gerava 1 boleto só, para gerar todos os boletos, vou deixar um link abaixo para você com um

PDF

gerado por mim. @Westfallx , essa questão depende muito do seu hardware, e do tipo de de desempenho que você quer extrair dele, usando o StringBuilder que é um objeto que no final das contas monta sua string do mesmo modo, faz com que você economize memória RAM, mas perca mais desempenho em processamento, já o

string.concat()

acaba que usa muita memória, mas o processamento cai pela metade, elas por elas, dependendo do hardware que sustenta faz a diferença sim.

mas qualquer ajuda é bem-vinda, você pode alterar o quanto quiser. a final estamos aqui para Nos Ajudar, qualquer sugestão, duvida ou argumentação eu agradeço, pois estou aprendendo com vocês. grato !

EltonRst commented 8 years ago

@lucascollina & @Westfallx segue a correção do Projeto Boleto.Net:

Classe Boleto.Net.BoletoBancario.cs

Acabei por testar o StringBuilder e ganhei cerca de 4 Segundos de desempenho em relação ao string.Concat() Além de ficar mais organizado e mais legível, só o Processamento foi de 15% para 37% mas a memória RAM caiu de 70 MB para 20 MB

public byte[] MontaBytesPDF(bool convertLinhaDigitavelToImage = false)
        {
            return (new NReco.PdfGenerator.HtmlToPdfConverter()).GeneratePdf(this.MontaHtmlEmbedded(convertLinhaDigitavelToImage, true));
        }

        /// <summary>
        /// Lista de Boletos, objetos do tipo
        /// BoletoBancario
        /// </summary>
        /// <param name="boletos">Lista de Boletos, objetos do tipo BoletoBancario</param>
        /// <param name="tituloNaView">Título Que aparecerá na Aba do Navegador</param>
        /// <param name="CustomSwitches">Custom WkHtmlToPdf global options</param>
        /// <param name="tituloPDF">Título No Início do PDF</param>
        /// <param name="PretoBranco">Preto e Branco = true</param>
        /// <param name="convertLinhaDigitavelToImage">bool Converter a Linha Digitavel Em Imagem</param>
        /// <returns>byte[], Vetor de bytes do PDF</returns>
        public byte[] MontaBytesListaBoletosPDF(List<BoletoBancario> boletos, string tituloNaView = "", string CustomSwitches = "", string tituloPDF = "", bool PretoBranco = false, bool convertLinhaDigitavelToImage = false)
        {
            StringBuilder htmlBoletos = new StringBuilder();
            htmlBoletos.Append("<html><head><title>");
            htmlBoletos.Append(tituloNaView);
            htmlBoletos.Append("</title><style type='text/css' media='screen,print'>");
            htmlBoletos.Append(".break{ display: block; clear: both; page-break-after: always;}");
            htmlBoletos.Append("</style></head><body>");
            if (!string.IsNullOrEmpty(tituloPDF))
            {
                htmlBoletos.Append("<br/><center><h1>");
                htmlBoletos.Append(tituloPDF);
                htmlBoletos.Append("</h1></center><br/>");
            }
            foreach (BoletoBancario boleto in boletos)
            {
                htmlBoletos.Append("<div class='break'>");
                htmlBoletos.Append(boleto.MontaHtmlEmbedded(convertLinhaDigitavelToImage, true));
                htmlBoletos.Append("</div>");
            }
            htmlBoletos.Append("</body></html>");
            return (new NReco.PdfGenerator.HtmlToPdfConverter() { CustomWkHtmlArgs = CustomSwitches, Grayscale = PretoBranco }).GeneratePdf(htmlBoletos.ToString());
        }

Se alguém quiser fazer um Pull Request para incrementar no projeto ficarei feliz, pois não tenho como fazer o commit ou request

Segue um exemplo bem tosco de uso: Esses CustomSwitches são opções globais customizáveis do wkhtmltopdf

string CustomSwitches = string.Format(
                        "--print-media-type --header-line --header-font-size \"11\" --header-spacing 2 --header-font-name \"calibri light\" --header-center \"{0}\" --header-right \"Data: [date] [time]\" " +
                        "--footer-left \"{1}\" --footer-right \"Página: [page] de [toPage]\" --footer-line --footer-font-size \"9\" --footer-spacing 2 --footer-font-name \"calibri light\" ",
                        "Empresa do Seu Cliente", "Sua Empresa");
            return File(new BoletoBancario().MontaBytesListaBoletosPDF(Boletos, "Boletos Bancários", CustomSwitches, "Boletos Bancários"), "application/pdf", "Boletos.pdf"); 
valentimmx commented 7 years ago

@EltonRst Ficou show essa solução. Parabéns para o pessoal que resolveu essa questão ! 😄

fmfabiano commented 7 years ago

pessoal bom dia! em que momento chamo a rotina para gerar o pdf

EltonRst commented 7 years ago

@fmfabiano

public ActionResult Boletos() { // Lista que Receberá os Boletos List Boletos = new List();

        /// Monte seus Boletos Bancários e Adicione Na lista depois Divirta-se

    return File(new BoletoBancario().MontaBytesListaBoletosPDF(Boletos, "Boletos Bancários"), "application/pdf", "Boletos.pdf");

}

o que realmente interessa: new BoletoBancario().MontaBytesListaBoletosPDF(Boletos, "Boletos Bancários")

diegohunter commented 7 years ago

Bom dia, Gostaria de saber se é possível alterar o output path da geração do boleto. Aparentemente quando o pdf é gerado, ele é gerado na pasta bin. Quando o IIS detecta qualquer mudança nessa pasta ele recarrega tudo, derruba sessão, chama applicaton_end e etc. Em resumo, toda vez que eu gero um pdf do boleto o IIS derruba a sessão de todos e recarrega tudo.

douglasacioli commented 5 years ago

Olá Pessoal, sou novo nisso , estou com muita dificuldade nessa parte de gerar o boleto. a parte do html no meu código esta funcionando normalmente, mas não to sabendo exportar para pdf. :( vi que temos dois metodos na classe boletobancario:

MontaBytesListaBoletosPDF MontaBytesPDF

mas não estou sabendo usar nenhum dos dois :(

será que alguem poderia me enviar um exemplo de usabilidade?

desde ja agradeço.

segue um trecho do nosso código logo :

`public string HTMLParaImpressao() { BoletoNet.BoletoBancario pre = preImpressao();

   return pre.MontaHtmlEmbedded();
  }

public bite HtmlParaPdf()
{
    BoletoNet.BoletoBancario pre = preImpressao();
    return pre.MontaBytesPDF();
}

private BoletoNet.BoletoBancario preImpressao()
{

    BoletoBancario boletoRetorno = new BoletoBancario();

    DateTime vencimento = DateTime.Now.AddDays(5);

    Cedente c = new Cedente(CNPJ_CEDENTE, RAZAO_SOCIAL_CEDENTE, AGENCIA_CEDENTE, CONTA_CEDENTE);
    c.Codigo = CodigoConvenio;

    BoletoNet.Boleto b = new BoletoNet.Boleto(DataVencimento, ValorBoleto, CARTEIRA, NossoNumero.ToString(), c);

    #region Adiciona Instruções somente no Cedente
    //IInstrucao instrucaoCedente = new Instrucao(33);

    //instrucaoCedente.Descricao = InstrucoesCaixa;

    //c.Instrucoes.Add(instrucaoCedente);
    #endregion

    b.NumeroDocumento = SeuNumero;

    b.Sacado = new Sacado(CnpjSacado, RazaoSocialSacado);
    b.Sacado.Endereco.End = EnderecoSacado + " " + NumeroSacado + " " + ComplementoSacado;
    b.Sacado.Endereco.Bairro = BairroSacado;
    b.Sacado.Endereco.Cidade = CidadeSacado;
    b.Sacado.Endereco.CEP = CepSacado;
    b.Sacado.Endereco.UF = UfSacado;

    #region Adiciona Instruções somente no Sacado
    //IInstrucao instrucaoSacado = new Instrucao(33);

    //instrucaoSacado.Descricao = InstrucoesCaixa;

    //b.Sacado.Instrucoes.Add(instrucaoSacado);
    #endregion

    #region Adiciona Instruções comuns - Cedente e Sacado
    IInstrucao instrucaoComum = new Instrucao(33);

    instrucaoComum.Descricao = InstrucoesCaixa;

    b.Instrucoes.Add(instrucaoComum);
    #endregion

    //Espécie Documento - [R] Recibo
    b.EspecieDocumento = new EspecieDocumento_Santander("17");

    b.LinhaDigitavelVindaDoBanco = LinhaDigitavel;
    b.CodigoDeBarrasVindaDoBanco = CodigoDeBarras;

    boletoRetorno.CodigoBanco = short.Parse(CodigoBanco);
    boletoRetorno.Boleto = b;
    boletoRetorno.MostrarCodigoCarteira = true;
    boletoRetorno.MostrarComprovanteEntrega = false;
    boletoRetorno.Boleto.Valida();

    return boletoRetorno;
}

private static T ToEnum<T>(string value, bool ignoreCase, T defaultValue) where T : struct
{
    if (string.IsNullOrEmpty(value))
        return defaultValue;

    T result;
    if (Enum.TryParse(value, ignoreCase, out result))
        return result;

    return defaultValue;
}`
EltonRst commented 5 years ago

@douglasacioli

Olá Pessoal, sou novo nisso , estou com muita dificuldade nessa parte de gerar o boleto. a parte do html no meu código esta funcionando normalmente, mas não to sabendo exportar para pdf. :( vi que temos dois metodos na classe boletobancario:

MontaBytesListaBoletosPDF MontaBytesPDF

mas não estou sabendo usar nenhum dos dois :(

será que alguem poderia me enviar um exemplo de usabilidade?

desde ja agradeço.

segue um trecho do nosso código logo :

`public string HTMLParaImpressao() { BoletoNet.BoletoBancario pre = preImpressao();

   return pre.MontaHtmlEmbedded();
  }

public bite HtmlParaPdf()
{
    BoletoNet.BoletoBancario pre = preImpressao();
    return pre.MontaBytesPDF();
}

private BoletoNet.BoletoBancario preImpressao()
{

    BoletoBancario boletoRetorno = new BoletoBancario();

    DateTime vencimento = DateTime.Now.AddDays(5);

    Cedente c = new Cedente(CNPJ_CEDENTE, RAZAO_SOCIAL_CEDENTE, AGENCIA_CEDENTE, CONTA_CEDENTE);
    c.Codigo = CodigoConvenio;

    BoletoNet.Boleto b = new BoletoNet.Boleto(DataVencimento, ValorBoleto, CARTEIRA, NossoNumero.ToString(), c);

    #region Adiciona Instruções somente no Cedente
    //IInstrucao instrucaoCedente = new Instrucao(33);

    //instrucaoCedente.Descricao = InstrucoesCaixa;

    //c.Instrucoes.Add(instrucaoCedente);
    #endregion

    b.NumeroDocumento = SeuNumero;

    b.Sacado = new Sacado(CnpjSacado, RazaoSocialSacado);
    b.Sacado.Endereco.End = EnderecoSacado + " " + NumeroSacado + " " + ComplementoSacado;
    b.Sacado.Endereco.Bairro = BairroSacado;
    b.Sacado.Endereco.Cidade = CidadeSacado;
    b.Sacado.Endereco.CEP = CepSacado;
    b.Sacado.Endereco.UF = UfSacado;

    #region Adiciona Instruções somente no Sacado
    //IInstrucao instrucaoSacado = new Instrucao(33);

    //instrucaoSacado.Descricao = InstrucoesCaixa;

    //b.Sacado.Instrucoes.Add(instrucaoSacado);
    #endregion

    #region Adiciona Instruções comuns - Cedente e Sacado
    IInstrucao instrucaoComum = new Instrucao(33);

    instrucaoComum.Descricao = InstrucoesCaixa;

    b.Instrucoes.Add(instrucaoComum);
    #endregion

    //Espécie Documento - [R] Recibo
    b.EspecieDocumento = new EspecieDocumento_Santander("17");

    b.LinhaDigitavelVindaDoBanco = LinhaDigitavel;
    b.CodigoDeBarrasVindaDoBanco = CodigoDeBarras;

    boletoRetorno.CodigoBanco = short.Parse(CodigoBanco);
    boletoRetorno.Boleto = b;
    boletoRetorno.MostrarCodigoCarteira = true;
    boletoRetorno.MostrarComprovanteEntrega = false;
    boletoRetorno.Boleto.Valida();

    return boletoRetorno;
}

private static T ToEnum<T>(string value, bool ignoreCase, T defaultValue) where T : struct
{
    if (string.IsNullOrEmpty(value))
        return defaultValue;

    T result;
    if (Enum.TryParse(value, ignoreCase, out result))
        return result;

    return defaultValue;
}`

Esse objeto que retorna do método BoletoNet.BoletoBancario preImpressao() você adiciona em uma lista: List<BoletoNet.BoletoBancario> Boletos = new List<BoletoNet.BoletoBancario>();

Depois você pode utilizar:

public ActionResult Boletos() { // Lista que Receberá os Boletos List Boletos = new List();

        /// Monte seus Boletos Bancários e Adicione Na lista depois Divirta-se

    return File(new BoletoBancario().MontaBytesListaBoletosPDF(Boletos, "Boletos Bancários"), "application/pdf", "Boletos.pdf");

}

o que realmente interessa: new BoletoBancario().MontaBytesListaBoletosPDF(Boletos, "Boletos Bancários")