From b9471eb033375a358183617b8a7332d82f92c54e Mon Sep 17 00:00:00 2001 From: Vinicius Cunha Date: Thu, 4 Jul 2024 15:10:11 -0300 Subject: [PATCH] Revert "Removidos projetos PdfClown e QuestPdf" This reverts commit 14e0cf32bceb4a5a2c654f76aa21bf32d32ae7b9. --- .../Atributos/AlturaFixaAttribute.cs | 6 + NFe.Danfe.PdfClown/Blocos/BlocoBase.cs | 65 ++ .../Blocos/BlocoCalculoImposto.cs | 59 ++ .../Blocos/BlocoCalculoIssqn.cs | 24 + NFe.Danfe.PdfClown/Blocos/BlocoCanhoto.cs | 38 + .../Blocos/BlocoDadosAdicionais.cs | 54 ++ .../Blocos/BlocoDestinatarioRemetente.cs | 39 + .../Blocos/BlocoDuplicataFatura.cs | 42 + .../Blocos/BlocoIdentificacaoEmitente.cs | 70 ++ .../Blocos/BlocoLocalEntrega.cs | 15 + .../Blocos/BlocoLocalEntregaRetirada.cs | 39 + .../Blocos/BlocoLocalRetirada.cs | 15 + .../Blocos/BlocoTransportador.cs | 52 ++ .../Blocos/TabelaProdutoServicos.cs | 108 +++ NFe.Danfe.PdfClown/Constantes.cs | 13 + NFe.Danfe.PdfClown/DanfeDoc.cs | 266 ++++++ NFe.Danfe.PdfClown/DanfePagina.cs | 141 +++ NFe.Danfe.PdfClown/Elementos/Barcode128C.cs | 220 +++++ .../Elementos/CabecalhoBloco.cs | 29 + NFe.Danfe.PdfClown/Elementos/Campo.cs | 97 ++ .../Elementos/CampoMultilinha.cs | 45 + NFe.Danfe.PdfClown/Elementos/CampoNumerico.cs | 27 + NFe.Danfe.PdfClown/Elementos/DrawableBase.cs | 51 ++ NFe.Danfe.PdfClown/Elementos/Duplicata.cs | 48 + NFe.Danfe.PdfClown/Elementos/ElementoBase.cs | 25 + NFe.Danfe.PdfClown/Elementos/ElementoVazio.cs | 11 + NFe.Danfe.PdfClown/Elementos/Estilo.cs | 49 + NFe.Danfe.PdfClown/Elementos/FlexibleLine.cs | 108 +++ .../Elementos/IdentificacaoEmitente.cs | 85 ++ NFe.Danfe.PdfClown/Elementos/LinhaCampos.cs | 37 + .../Elementos/LinhaTracejada.cs | 31 + NFe.Danfe.PdfClown/Elementos/NumeroNfSerie.cs | 44 + .../Elementos/NumeroNfSerie2.cs | 100 +++ NFe.Danfe.PdfClown/Elementos/Tabela.cs | 194 ++++ NFe.Danfe.PdfClown/Elementos/TabelaColuna.cs | 23 + NFe.Danfe.PdfClown/Elementos/TextBlock.cs | 166 ++++ NFe.Danfe.PdfClown/Elementos/TextStack.cs | 84 ++ NFe.Danfe.PdfClown/Elementos/TextoSimples.cs | 48 + NFe.Danfe.PdfClown/Elementos/VerticalStack.cs | 62 ++ .../Enumeracoes/AlinhamentoHorizontal.cs | 9 + .../Enumeracoes/AlinhamentoVertical.cs | 9 + NFe.Danfe.PdfClown/Enumeracoes/Orientacao.cs | 8 + .../Enumeracoes/PosicaoBloco.cs | 8 + NFe.Danfe.PdfClown/Esquemas/NFReferenciada.cs | 93 ++ NFe.Danfe.PdfClown/Esquemas/Namespaces.cs | 7 + NFe.Danfe.PdfClown/Esquemas/ProcNFe.cs | 850 ++++++++++++++++++ NFe.Danfe.PdfClown/Esquemas/Versao.cs | 40 + NFe.Danfe.PdfClown/Graphics/Fonte.cs | 63 ++ NFe.Danfe.PdfClown/Graphics/Gfx.cs | 132 +++ .../Modelo/CalculoImpostoViewModel.cs | 108 +++ .../Modelo/CalculoIssqnViewModel.cs | 35 + NFe.Danfe.PdfClown/Modelo/DanfeViewModel.cs | 370 ++++++++ .../Modelo/DanfeViewModelCreator.cs | 389 ++++++++ .../Modelo/DuplicataViewModel.cs | 23 + NFe.Danfe.PdfClown/Modelo/EmpresaViewModel.cs | 142 +++ .../Modelo/LocalEntregaRetiradaViewModel.cs | 15 + NFe.Danfe.PdfClown/Modelo/ProdutoViewModel.cs | 123 +++ .../Modelo/TransportadoraViewModel.cs | 97 ++ NFe.Danfe.PdfClown/NFe.Danfe.PdfClown.csproj | 15 + NFe.Danfe.PdfClown/Strings.cs | 19 + .../Structs/DateTimeOffsetIso8601.cs | 95 ++ NFe.Danfe.PdfClown/Tools/Extensions.cs | 126 +++ NFe.Danfe.PdfClown/Tools/Formatador.cs | 223 +++++ NFe.Danfe.PdfClown/Tools/Utils.cs | 34 + NFe.Danfe.QuestPdf/Fontes/Arial.ttf | Bin 0 -> 275572 bytes .../ImpressaoEventoNfe/EventoNfeDocument.cs | 654 ++++++++++++++ .../ImpressaoNfce/DanfeNfceDocument.cs | 564 ++++++++++++ .../ImpressaoNfce/TamanhoImpressao.cs | 15 + NFe.Danfe.QuestPdf/Models/EmpresaModel.cs | 7 + .../Models/NfceInutilizacaoModel.cs | 10 + .../Models/NfceResumidaModel.cs | 11 + .../Models/NfeInutilizadaModel.cs | 10 + NFe.Danfe.QuestPdf/Models/NfeResumidaModel.cs | 11 + NFe.Danfe.QuestPdf/Models/PeriodoModel.cs | 7 + .../RelatorioFiscalEmissoesNfceModel.cs | 20 + .../Models/RelatorioFiscalEmissoesNfeModel.cs | 20 + NFe.Danfe.QuestPdf/NFe.Danfe.QuestPdf.csproj | 28 + .../RelatorioFiscalEmissoesNfceDocument.cs | 285 ++++++ .../RelatorioFiscalEmissoesNfeDocument.cs | 285 ++++++ 79 files changed, 7590 insertions(+) create mode 100644 NFe.Danfe.PdfClown/Atributos/AlturaFixaAttribute.cs create mode 100644 NFe.Danfe.PdfClown/Blocos/BlocoBase.cs create mode 100644 NFe.Danfe.PdfClown/Blocos/BlocoCalculoImposto.cs create mode 100644 NFe.Danfe.PdfClown/Blocos/BlocoCalculoIssqn.cs create mode 100644 NFe.Danfe.PdfClown/Blocos/BlocoCanhoto.cs create mode 100644 NFe.Danfe.PdfClown/Blocos/BlocoDadosAdicionais.cs create mode 100644 NFe.Danfe.PdfClown/Blocos/BlocoDestinatarioRemetente.cs create mode 100644 NFe.Danfe.PdfClown/Blocos/BlocoDuplicataFatura.cs create mode 100644 NFe.Danfe.PdfClown/Blocos/BlocoIdentificacaoEmitente.cs create mode 100644 NFe.Danfe.PdfClown/Blocos/BlocoLocalEntrega.cs create mode 100644 NFe.Danfe.PdfClown/Blocos/BlocoLocalEntregaRetirada.cs create mode 100644 NFe.Danfe.PdfClown/Blocos/BlocoLocalRetirada.cs create mode 100644 NFe.Danfe.PdfClown/Blocos/BlocoTransportador.cs create mode 100644 NFe.Danfe.PdfClown/Blocos/TabelaProdutoServicos.cs create mode 100644 NFe.Danfe.PdfClown/Constantes.cs create mode 100644 NFe.Danfe.PdfClown/DanfeDoc.cs create mode 100644 NFe.Danfe.PdfClown/DanfePagina.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/Barcode128C.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/CabecalhoBloco.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/Campo.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/CampoMultilinha.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/CampoNumerico.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/DrawableBase.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/Duplicata.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/ElementoBase.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/ElementoVazio.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/Estilo.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/FlexibleLine.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/IdentificacaoEmitente.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/LinhaCampos.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/LinhaTracejada.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/NumeroNfSerie.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/NumeroNfSerie2.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/Tabela.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/TabelaColuna.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/TextBlock.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/TextStack.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/TextoSimples.cs create mode 100644 NFe.Danfe.PdfClown/Elementos/VerticalStack.cs create mode 100644 NFe.Danfe.PdfClown/Enumeracoes/AlinhamentoHorizontal.cs create mode 100644 NFe.Danfe.PdfClown/Enumeracoes/AlinhamentoVertical.cs create mode 100644 NFe.Danfe.PdfClown/Enumeracoes/Orientacao.cs create mode 100644 NFe.Danfe.PdfClown/Enumeracoes/PosicaoBloco.cs create mode 100644 NFe.Danfe.PdfClown/Esquemas/NFReferenciada.cs create mode 100644 NFe.Danfe.PdfClown/Esquemas/Namespaces.cs create mode 100644 NFe.Danfe.PdfClown/Esquemas/ProcNFe.cs create mode 100644 NFe.Danfe.PdfClown/Esquemas/Versao.cs create mode 100644 NFe.Danfe.PdfClown/Graphics/Fonte.cs create mode 100644 NFe.Danfe.PdfClown/Graphics/Gfx.cs create mode 100644 NFe.Danfe.PdfClown/Modelo/CalculoImpostoViewModel.cs create mode 100644 NFe.Danfe.PdfClown/Modelo/CalculoIssqnViewModel.cs create mode 100644 NFe.Danfe.PdfClown/Modelo/DanfeViewModel.cs create mode 100644 NFe.Danfe.PdfClown/Modelo/DanfeViewModelCreator.cs create mode 100644 NFe.Danfe.PdfClown/Modelo/DuplicataViewModel.cs create mode 100644 NFe.Danfe.PdfClown/Modelo/EmpresaViewModel.cs create mode 100644 NFe.Danfe.PdfClown/Modelo/LocalEntregaRetiradaViewModel.cs create mode 100644 NFe.Danfe.PdfClown/Modelo/ProdutoViewModel.cs create mode 100644 NFe.Danfe.PdfClown/Modelo/TransportadoraViewModel.cs create mode 100644 NFe.Danfe.PdfClown/NFe.Danfe.PdfClown.csproj create mode 100644 NFe.Danfe.PdfClown/Strings.cs create mode 100644 NFe.Danfe.PdfClown/Structs/DateTimeOffsetIso8601.cs create mode 100644 NFe.Danfe.PdfClown/Tools/Extensions.cs create mode 100644 NFe.Danfe.PdfClown/Tools/Formatador.cs create mode 100644 NFe.Danfe.PdfClown/Tools/Utils.cs create mode 100644 NFe.Danfe.QuestPdf/Fontes/Arial.ttf create mode 100644 NFe.Danfe.QuestPdf/ImpressaoEventoNfe/EventoNfeDocument.cs create mode 100644 NFe.Danfe.QuestPdf/ImpressaoNfce/DanfeNfceDocument.cs create mode 100644 NFe.Danfe.QuestPdf/ImpressaoNfce/TamanhoImpressao.cs create mode 100644 NFe.Danfe.QuestPdf/Models/EmpresaModel.cs create mode 100644 NFe.Danfe.QuestPdf/Models/NfceInutilizacaoModel.cs create mode 100644 NFe.Danfe.QuestPdf/Models/NfceResumidaModel.cs create mode 100644 NFe.Danfe.QuestPdf/Models/NfeInutilizadaModel.cs create mode 100644 NFe.Danfe.QuestPdf/Models/NfeResumidaModel.cs create mode 100644 NFe.Danfe.QuestPdf/Models/PeriodoModel.cs create mode 100644 NFe.Danfe.QuestPdf/Models/RelatorioFiscalEmissoesNfceModel.cs create mode 100644 NFe.Danfe.QuestPdf/Models/RelatorioFiscalEmissoesNfeModel.cs create mode 100644 NFe.Danfe.QuestPdf/NFe.Danfe.QuestPdf.csproj create mode 100644 NFe.Danfe.QuestPdf/ParaContadores/RelatorioFiscalEmissoesNfceDocument.cs create mode 100644 NFe.Danfe.QuestPdf/ParaContadores/RelatorioFiscalEmissoesNfeDocument.cs diff --git a/NFe.Danfe.PdfClown/Atributos/AlturaFixaAttribute.cs b/NFe.Danfe.PdfClown/Atributos/AlturaFixaAttribute.cs new file mode 100644 index 000000000..2a2696a4d --- /dev/null +++ b/NFe.Danfe.PdfClown/Atributos/AlturaFixaAttribute.cs @@ -0,0 +1,6 @@ +namespace NFe.Danfe.PdfClown.Atributos +{ + internal class AlturaFixaAttribute : Attribute + { + } +} diff --git a/NFe.Danfe.PdfClown/Blocos/BlocoBase.cs b/NFe.Danfe.PdfClown/Blocos/BlocoBase.cs new file mode 100644 index 000000000..d8d3a6103 --- /dev/null +++ b/NFe.Danfe.PdfClown/Blocos/BlocoBase.cs @@ -0,0 +1,65 @@ +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; +using NFe.Danfe.PdfClown.Modelo; + +namespace NFe.Danfe.PdfClown.Blocos +{ + /// + /// Define um bloco básico do DANFE. + /// + internal abstract class BlocoBase : ElementoBase + { + /// + /// Constante de proporção dos campos para o formato retrato A4, porcentagem dividida pela largura desenhável. + /// + public const float Proporcao = 100F / 200F; + + public DanfeViewModel ViewModel { get; private set; } + + public abstract PosicaoBloco Posicao { get; } + + /// + /// Pilha principal. + /// + public VerticalStack MainVerticalStack { get; private set; } + + /// + /// Quando verdadeiro, o bloco é mostrado apenas na primeira página, caso contário é mostrado em todas elas. + /// + public virtual Boolean VisivelSomentePrimeiraPagina => true; + + public virtual String Cabecalho => null; + + public BlocoBase(DanfeViewModel viewModel, Estilo estilo) : base(estilo) + { + MainVerticalStack = new VerticalStack(); + ViewModel = viewModel ?? throw new ArgumentNullException(nameof(viewModel)); + + if (!String.IsNullOrWhiteSpace(Cabecalho)) + { + MainVerticalStack.Add(new CabecalhoBloco(estilo, Cabecalho)); + } + } + + public LinhaCampos AdicionarLinhaCampos() + { + var l = new LinhaCampos(Estilo, Width); + l.Width = Width; + l.Height = Constantes.CampoAltura; + MainVerticalStack.Add(l); + return l; + } + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + MainVerticalStack.SetPosition(X, Y); + MainVerticalStack.Width = Width; + MainVerticalStack.Draw(gfx); + } + + public override float Height { get => MainVerticalStack.Height; set => throw new NotSupportedException(); } + public override bool PossuiContono => false; + } +} diff --git a/NFe.Danfe.PdfClown/Blocos/BlocoCalculoImposto.cs b/NFe.Danfe.PdfClown/Blocos/BlocoCalculoImposto.cs new file mode 100644 index 000000000..9875512fc --- /dev/null +++ b/NFe.Danfe.PdfClown/Blocos/BlocoCalculoImposto.cs @@ -0,0 +1,59 @@ +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Modelo; + +namespace NFe.Danfe.PdfClown.Blocos +{ + class BlocoCalculoImposto : BlocoBase + { + public BlocoCalculoImposto(DanfeViewModel viewModel, Estilo estilo) : base(viewModel, estilo) + { + var m = ViewModel.CalculoImposto; + + var l = AdicionarLinhaCampos() + .ComCampoNumerico("BASE DE CÁLC. DO ICMS", m.BaseCalculoIcms) + .ComCampoNumerico("VALOR DO ICMS", m.ValorIcms) + .ComCampoNumerico("BASE DE CÁLC. ICMS S.T.", m.BaseCalculoIcmsSt) + .ComCampoNumerico("VALOR DO ICMS SUBST.", m.ValorIcmsSt) + .ComCampoNumerico("V. IMP. IMPORTAÇÃO", m.ValorII); + + if (ViewModel.ExibirIcmsInterestadual) + { + l.ComCampoNumerico("V. ICMS UF REMET.", m.vICMSUFRemet) + .ComCampoNumerico("VALOR DO FCP", m.vFCPUFDest); + } + + if (ViewModel.ExibirPisConfins) + { + l.ComCampoNumerico("VALOR DO PIS", m.ValorPis); + } + + l.ComCampoNumerico("V. TOTAL PRODUTOS", m.ValorTotalProdutos) + .ComLargurasIguais(); + + l = AdicionarLinhaCampos() + .ComCampoNumerico("Valor do Frete", m.ValorFrete) + .ComCampoNumerico("Valor do Seguro", m.ValorSeguro) + .ComCampoNumerico("Desconto", m.Desconto) + .ComCampoNumerico("Outras Despesas", m.OutrasDespesas) + .ComCampoNumerico("Valor Ipi", m.ValorIpi); + + if (ViewModel.ExibirIcmsInterestadual) + { + l.ComCampoNumerico("V. ICMS UF DEST.", m.vICMSUFDest) + .ComCampoNumerico("V. TOT. TRIB.", m.ValorAproximadoTributos); + } + + if (ViewModel.ExibirPisConfins) + { + l.ComCampoNumerico("VALOR DO COFINS", m.ValorCofins); + } + + l.ComCampoNumerico("Valor Total da Nota", m.ValorTotalNota) + .ComLargurasIguais(); + } + + public override PosicaoBloco Posicao => PosicaoBloco.Topo; + public override string Cabecalho => "Cálculo do Imposto"; + } +} diff --git a/NFe.Danfe.PdfClown/Blocos/BlocoCalculoIssqn.cs b/NFe.Danfe.PdfClown/Blocos/BlocoCalculoIssqn.cs new file mode 100644 index 000000000..04d05df55 --- /dev/null +++ b/NFe.Danfe.PdfClown/Blocos/BlocoCalculoIssqn.cs @@ -0,0 +1,24 @@ +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Modelo; + +namespace NFe.Danfe.PdfClown.Blocos +{ + internal class BlocoCalculoIssqn : BlocoBase + { + public BlocoCalculoIssqn(DanfeViewModel viewModel, Estilo estilo) : base(viewModel, estilo) + { + var m = viewModel.CalculoIssqn; + + AdicionarLinhaCampos() + .ComCampo("INSCRIÇÃO MUNICIPAL", m.InscricaoMunicipal, AlinhamentoHorizontal.Centro) + .ComCampoNumerico("VALOR TOTAL DOS SERVIÇOS", m.ValorTotalServicos) + .ComCampoNumerico("BASE DE CÁLCULO DO ISSQN", m.BaseIssqn) + .ComCampoNumerico("VALOR TOTAL DO ISSQN", m.ValorIssqn) + .ComLargurasIguais(); + } + + public override PosicaoBloco Posicao => PosicaoBloco.Base; + public override string Cabecalho => "CÁLCULO DO ISSQN"; + } +} diff --git a/NFe.Danfe.PdfClown/Blocos/BlocoCanhoto.cs b/NFe.Danfe.PdfClown/Blocos/BlocoCanhoto.cs new file mode 100644 index 000000000..9814073dc --- /dev/null +++ b/NFe.Danfe.PdfClown/Blocos/BlocoCanhoto.cs @@ -0,0 +1,38 @@ +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Modelo; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Blocos +{ + internal class BlocoCanhoto : BlocoBase + { + public const float TextoRecebimentoAltura = 10; + public const float AlturaLinha2 = 9; + + public BlocoCanhoto(DanfeViewModel viewModel, Estilo estilo) : base(viewModel, estilo) + { + var textoRecebimento = new TextoSimples(estilo, viewModel.TextoRecebimento) { Height = TextoRecebimentoAltura, TamanhoFonte = 8 }; + var nfe = new NumeroNfSerie(estilo, viewModel.NfNumero.ToString(Formatador.FormatoNumeroNF), viewModel.NfSerie.ToString()) { Height = AlturaLinha2 + TextoRecebimentoAltura, Width = 30 }; + + var campos = new LinhaCampos(Estilo) { Height = AlturaLinha2 } + .ComCampo("Data de Recebimento", null) + .ComCampo("Identificação e assinatura do recebedor", null) + .ComLarguras(50, 0); + + var coluna1 = new VerticalStack(); + coluna1.Add(textoRecebimento, campos); + + var linha = new FlexibleLine() { Height = coluna1.Height } + .ComElemento(coluna1) + .ComElemento(nfe) + .ComLarguras(0, 16); + + MainVerticalStack.Add(linha, new LinhaTracejada(2)); + + } + + public override PosicaoBloco Posicao => PosicaoBloco.Topo; + + } +} diff --git a/NFe.Danfe.PdfClown/Blocos/BlocoDadosAdicionais.cs b/NFe.Danfe.PdfClown/Blocos/BlocoDadosAdicionais.cs new file mode 100644 index 000000000..cfe3b6ac7 --- /dev/null +++ b/NFe.Danfe.PdfClown/Blocos/BlocoDadosAdicionais.cs @@ -0,0 +1,54 @@ +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; +using NFe.Danfe.PdfClown.Modelo; + +namespace NFe.Danfe.PdfClown.Blocos +{ + internal class BlocoDadosAdicionais : BlocoBase + { + public const float AlturaMinima = 25; + private CampoMultilinha _cInfComplementares; + private FlexibleLine _Linha; + private Campo _cReservadoFisco; + public const float InfComplementaresLarguraPorcentagem = 75; + + public BlocoDadosAdicionais(DanfeViewModel viewModel, Estilo estilo) : base(viewModel, estilo) + { + _cInfComplementares = new CampoMultilinha("Informações Complementares", ViewModel.TextoAdicional(), estilo); + _cReservadoFisco = new CampoMultilinha("Reservado ao fisco", ViewModel.TextoAdicionalFisco(), estilo); + + _Linha = new FlexibleLine() { Height = _cInfComplementares.Height } + .ComElemento(_cInfComplementares) + .ComElemento(_cReservadoFisco) + .ComLarguras(InfComplementaresLarguraPorcentagem, 0); + + MainVerticalStack.Add(_Linha); + } + + public override float Width + { + get => base.Width; + set + { + base.Width = value; + // Força o ajuste da altura do InfComplementares + if (_cInfComplementares != null && _Linha != null) + { + _Linha.Width = value; + _Linha.Posicionar(); + _cInfComplementares.Height = AlturaMinima; + _Linha.Height = _cInfComplementares.Height; + } + } + } + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + } + + public override PosicaoBloco Posicao => PosicaoBloco.Base; + public override string Cabecalho => "Dados adicionais"; + } +} diff --git a/NFe.Danfe.PdfClown/Blocos/BlocoDestinatarioRemetente.cs b/NFe.Danfe.PdfClown/Blocos/BlocoDestinatarioRemetente.cs new file mode 100644 index 000000000..c2179243d --- /dev/null +++ b/NFe.Danfe.PdfClown/Blocos/BlocoDestinatarioRemetente.cs @@ -0,0 +1,39 @@ +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Modelo; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Blocos +{ + internal class BlocoDestinatarioRemetente : BlocoBase + { + public BlocoDestinatarioRemetente(DanfeViewModel viewModel, Estilo estilo) : base(viewModel, estilo) + { + var destinatario = viewModel.Destinatario; + + AdicionarLinhaCampos() + .ComCampo(Strings.RazaoSocial, destinatario.RazaoSocial) + .ComCampo(Strings.CnpjCpf, Formatador.FormatarCpfCnpj(destinatario.CnpjCpf), AlinhamentoHorizontal.Centro) + .ComCampo("Data de Emissão", viewModel.DataHoraEmissao.Formatar(), AlinhamentoHorizontal.Centro) + .ComLarguras(0, 45F * Proporcao, 30F * Proporcao); + + AdicionarLinhaCampos() + .ComCampo(Strings.Endereco, destinatario.EnderecoLinha1) + .ComCampo(Strings.BairroDistrito, destinatario.EnderecoBairro) + .ComCampo(Strings.Cep, Formatador.FormatarCEP(destinatario.EnderecoCep), AlinhamentoHorizontal.Centro) + .ComCampo("Data Entrada / Saída", ViewModel.DataSaidaEntrada.Formatar(), AlinhamentoHorizontal.Centro) + .ComLarguras(0, 45F * Proporcao, 25F * Proporcao, 30F * Proporcao); + + AdicionarLinhaCampos() + .ComCampo(Strings.Municipio, destinatario.Municipio) + .ComCampo(Strings.FoneFax, Formatador.FormatarTelefone(destinatario.Telefone), AlinhamentoHorizontal.Centro) + .ComCampo(Strings.UF, destinatario.EnderecoUf, AlinhamentoHorizontal.Centro) + .ComCampo(Strings.InscricaoEstadual, destinatario.Ie, AlinhamentoHorizontal.Centro) + .ComCampo("Hora Entrada / Saída", ViewModel.HoraSaidaEntrada.Formatar(), AlinhamentoHorizontal.Centro) + .ComLarguras(0, 35F * Proporcao, 7F * Proporcao, 40F * Proporcao, 30F * Proporcao); + } + + public override string Cabecalho => "Destinatário / Remetente"; + public override PosicaoBloco Posicao => PosicaoBloco.Topo; + } +} diff --git a/NFe.Danfe.PdfClown/Blocos/BlocoDuplicataFatura.cs b/NFe.Danfe.PdfClown/Blocos/BlocoDuplicataFatura.cs new file mode 100644 index 000000000..c473260a6 --- /dev/null +++ b/NFe.Danfe.PdfClown/Blocos/BlocoDuplicataFatura.cs @@ -0,0 +1,42 @@ +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Modelo; + +namespace NFe.Danfe.PdfClown.Blocos +{ + internal class BlocoDuplicataFatura : BlocoBase + { + + public BlocoDuplicataFatura(DanfeViewModel viewModel, Estilo estilo) : base(viewModel, estilo) + { + var de = viewModel.Duplicatas.Select(x => new Duplicata(estilo, x)).ToList(); + var eh = de.First().Height; + + int numeroElementosLinha = ViewModel.Orientacao == Orientacao.Paisagem ? 7 : 6; + + int i = 0; + + while (i < de.Count) + { + FlexibleLine fl = new FlexibleLine(Width, eh); + + int i2; + for (i2 = 0; i2 < numeroElementosLinha && i < de.Count; i2++, i++) + { + fl.ComElemento(de[i]); + } + + for (; i2 < numeroElementosLinha; i2++) + fl.ComElemento(new ElementoVazio()); + + + fl.ComLargurasIguais(); + MainVerticalStack.Add(fl); + } + + } + + public override string Cabecalho => "Fatura / Duplicata"; + public override PosicaoBloco Posicao => PosicaoBloco.Topo; + } +} diff --git a/NFe.Danfe.PdfClown/Blocos/BlocoIdentificacaoEmitente.cs b/NFe.Danfe.PdfClown/Blocos/BlocoIdentificacaoEmitente.cs new file mode 100644 index 000000000..5e55e9546 --- /dev/null +++ b/NFe.Danfe.PdfClown/Blocos/BlocoIdentificacaoEmitente.cs @@ -0,0 +1,70 @@ +using System.Drawing; +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Modelo; +using NFe.Danfe.PdfClown.Tools; +using org.pdfclown.documents.contents.xObjects; + +namespace NFe.Danfe.PdfClown.Blocos +{ + internal class BlocoIdentificacaoEmitente : BlocoBase + { + public const float LarguraCampoChaveNFe = 93F; + public const float AlturaLinha1 = 30; + + NumeroNfSerie2 ifdNfe; + IdentificacaoEmitente idEmitente; + + public BlocoIdentificacaoEmitente(DanfeViewModel viewModel, Estilo estilo) : base(viewModel, estilo) + { + + var textoConsulta = new TextoSimples(Estilo, Strings.TextoConsulta) + { + Height = 8, + AlinhamentoHorizontal = AlinhamentoHorizontal.Centro, + AlinhamentoVertical = AlinhamentoVertical.Centro, + TamanhoFonte = 9 + }; + + var campoChaveAcesso = new Campo("Chave de Acesso", Formatador.FormatarChaveAcesso(ViewModel.ChaveAcesso), estilo, AlinhamentoHorizontal.Centro) { Height = Constantes.CampoAltura }; + var codigoBarras = new Barcode128C(viewModel.ChaveAcesso, Estilo) { Height = AlturaLinha1 - textoConsulta.Height - campoChaveAcesso.Height }; + + var coluna3 = new VerticalStack(); + coluna3.Add(codigoBarras, campoChaveAcesso, textoConsulta); + + ifdNfe = new NumeroNfSerie2(estilo, ViewModel); + idEmitente = new IdentificacaoEmitente(Estilo, ViewModel); + + FlexibleLine fl = new FlexibleLine() { Height = coluna3.Height } + .ComElemento(idEmitente) + .ComElemento(ifdNfe) + .ComElemento(coluna3) + .ComLarguras(0, 15, 46.5F); + + MainVerticalStack.Add(fl); + + AdicionarLinhaCampos() + .ComCampo("Natureza da operação", ViewModel.NaturezaOperacao) + .ComCampo("Protocolo de autorização", ViewModel.ProtocoloAutorizacao, AlinhamentoHorizontal.Centro) + .ComLarguras(0, 46.5F); + + AdicionarLinhaCampos() + .ComCampo("Inscrição Estadual", ViewModel.Emitente.Ie, AlinhamentoHorizontal.Centro) + .ComCampo("Inscrição Estadual do Subst. Tributário", ViewModel.Emitente.IeSt, AlinhamentoHorizontal.Centro) + .ComCampo("Cnpj", Formatador.FormatarCnpj(ViewModel.Emitente.CnpjCpf), AlinhamentoHorizontal.Centro) + .ComLargurasIguais(); + + } + + public XObject Logo + { + get => idEmitente.Logo; + set => idEmitente.Logo = value; + } + + public override PosicaoBloco Posicao => PosicaoBloco.Topo; + public override bool VisivelSomentePrimeiraPagina => false; + + public RectangleF RetanguloNumeroFolhas => ifdNfe.RetanguloNumeroFolhas; + } +} diff --git a/NFe.Danfe.PdfClown/Blocos/BlocoLocalEntrega.cs b/NFe.Danfe.PdfClown/Blocos/BlocoLocalEntrega.cs new file mode 100644 index 000000000..0df1e2e9b --- /dev/null +++ b/NFe.Danfe.PdfClown/Blocos/BlocoLocalEntrega.cs @@ -0,0 +1,15 @@ +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Modelo; + +namespace NFe.Danfe.PdfClown.Blocos +{ + class BlocoLocalEntrega : BlocoLocalEntregaRetirada + { + public BlocoLocalEntrega(DanfeViewModel viewModel, Estilo estilo) + : base(viewModel, estilo, viewModel.LocalEntrega) + { + } + + public override string Cabecalho => "Informações do local de entrega"; + } +} diff --git a/NFe.Danfe.PdfClown/Blocos/BlocoLocalEntregaRetirada.cs b/NFe.Danfe.PdfClown/Blocos/BlocoLocalEntregaRetirada.cs new file mode 100644 index 000000000..fe2a2287d --- /dev/null +++ b/NFe.Danfe.PdfClown/Blocos/BlocoLocalEntregaRetirada.cs @@ -0,0 +1,39 @@ +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Modelo; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Blocos +{ + abstract class BlocoLocalEntregaRetirada : BlocoBase + { + public LocalEntregaRetiradaViewModel Model { get; private set; } + + public BlocoLocalEntregaRetirada(DanfeViewModel viewModel, Estilo estilo, LocalEntregaRetiradaViewModel localModel) : base(viewModel, estilo) + { + Model = localModel ?? throw new ArgumentNullException(nameof(localModel)); + + AdicionarLinhaCampos() + .ComCampo(Strings.NomeRazaoSocial, Model.NomeRazaoSocial) + .ComCampo(Strings.CnpjCpf, Formatador.FormatarCpfCnpj(Model.CnpjCpf), AlinhamentoHorizontal.Centro) + .ComCampo(Strings.InscricaoEstadual, Model.InscricaoEstadual, AlinhamentoHorizontal.Centro) + .ComLarguras(0, 45F * Proporcao, 30F * Proporcao); + + AdicionarLinhaCampos() + .ComCampo(Strings.Endereco, Model.Endereco) + .ComCampo(Strings.BairroDistrito, Model.Bairro) + .ComCampo(Strings.Cep, Formatador.FormatarCEP(Model.Cep), AlinhamentoHorizontal.Centro) + .ComLarguras(0, 45F * Proporcao, 30F * Proporcao); + + AdicionarLinhaCampos() + .ComCampo(Strings.Municipio, Model.Municipio) + .ComCampo(Strings.UF, Model.Uf, AlinhamentoHorizontal.Centro) + .ComCampo(Strings.FoneFax, Formatador.FormatarTelefone(Model.Telefone), AlinhamentoHorizontal.Centro) + .ComLarguras(0, 7F * Proporcao, 30F * Proporcao); + } + + public override PosicaoBloco Posicao => PosicaoBloco.Topo; + + } +} + diff --git a/NFe.Danfe.PdfClown/Blocos/BlocoLocalRetirada.cs b/NFe.Danfe.PdfClown/Blocos/BlocoLocalRetirada.cs new file mode 100644 index 000000000..ba5228ddb --- /dev/null +++ b/NFe.Danfe.PdfClown/Blocos/BlocoLocalRetirada.cs @@ -0,0 +1,15 @@ +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Modelo; + +namespace NFe.Danfe.PdfClown.Blocos +{ + class BlocoLocalRetirada : BlocoLocalEntregaRetirada + { + public BlocoLocalRetirada(DanfeViewModel viewModel, Estilo estilo) + : base(viewModel, estilo, viewModel.LocalRetirada) + { + } + + public override string Cabecalho => "Informações do local de retirada"; + } +} diff --git a/NFe.Danfe.PdfClown/Blocos/BlocoTransportador.cs b/NFe.Danfe.PdfClown/Blocos/BlocoTransportador.cs new file mode 100644 index 000000000..a510e4cb6 --- /dev/null +++ b/NFe.Danfe.PdfClown/Blocos/BlocoTransportador.cs @@ -0,0 +1,52 @@ +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Modelo; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Blocos +{ + internal class BlocoTransportador : BlocoBase + { + public const float LarguraCampoPlacaVeiculo = 22F * Proporcao; + public const float LarguraCampoCodigoAntt = 30F * Proporcao; + public const float LarguraCampoCnpj = 31F * Proporcao; + public const float LarguraCampoUf = 7F * Proporcao; + public const float LarguraFrete = 34F * Proporcao; + + public BlocoTransportador(DanfeViewModel viewModel, Estilo campoEstilo) : base(viewModel, campoEstilo) + { + var transportadora = viewModel.Transportadora; + + AdicionarLinhaCampos() + .ComCampo(Strings.RazaoSocial, transportadora.RazaoSocial) + .ComCampo("Frete", transportadora.ModalidadeFreteString, AlinhamentoHorizontal.Centro) + .ComCampo("Código ANTT", transportadora.CodigoAntt, AlinhamentoHorizontal.Centro) + .ComCampo("Placa do Veículo", transportadora.Placa, AlinhamentoHorizontal.Centro) + .ComCampo(Strings.UF, transportadora.VeiculoUf, AlinhamentoHorizontal.Centro) + .ComCampo(Strings.CnpjCpf, Formatador.FormatarCnpj(transportadora.CnpjCpf), AlinhamentoHorizontal.Centro) + .ComLarguras(0, LarguraFrete, LarguraCampoCodigoAntt, LarguraCampoPlacaVeiculo, LarguraCampoUf, LarguraCampoCnpj); + + AdicionarLinhaCampos() + .ComCampo(Strings.Endereco, transportadora.EnderecoLogadrouro) + .ComCampo(Strings.Municipio, transportadora.Municipio) + .ComCampo(Strings.UF, transportadora.EnderecoUf, AlinhamentoHorizontal.Centro) + .ComCampo(Strings.InscricaoEstadual, transportadora.Ie, AlinhamentoHorizontal.Centro) + .ComLarguras(0, LarguraCampoPlacaVeiculo + LarguraCampoCodigoAntt, LarguraCampoUf, LarguraCampoCnpj); + + var l = (float)(LarguraCampoCodigoAntt + LarguraCampoPlacaVeiculo + LarguraCampoUf + LarguraCampoCnpj) / 3F; + + AdicionarLinhaCampos() + .ComCampoNumerico(Strings.Quantidade, transportadora.QuantidadeVolumes, 3) + .ComCampo("Espécie", transportadora.Especie) + .ComCampo("Marca", transportadora.Marca) + .ComCampo("Numeração", transportadora.Numeracao) + .ComCampoNumerico("Peso Bruto", transportadora.PesoBruto, 3) + .ComCampoNumerico("Peso Líquido", transportadora.PesoLiquido, 3) + .ComLarguras(20F / 200F * 100, 0, 0, l, l, l); + + } + + public override PosicaoBloco Posicao => PosicaoBloco.Topo; + public override string Cabecalho => "Transportador / Volumes Transportados"; + } +} diff --git a/NFe.Danfe.PdfClown/Blocos/TabelaProdutoServicos.cs b/NFe.Danfe.PdfClown/Blocos/TabelaProdutoServicos.cs new file mode 100644 index 000000000..1e27d2660 --- /dev/null +++ b/NFe.Danfe.PdfClown/Blocos/TabelaProdutoServicos.cs @@ -0,0 +1,108 @@ +using System.Drawing; +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; +using NFe.Danfe.PdfClown.Modelo; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Blocos +{ + internal class TabelaProdutosServicos : ElementoBase + { + public CabecalhoBloco CabecalhoBloco { get; private set; } + public Tabela Tabela { get; private set; } + public DanfeViewModel ViewModel { get; private set; } + + public TabelaProdutosServicos(DanfeViewModel viewModel, Estilo estilo) : base(estilo) + { + ViewModel = viewModel ?? throw new ArgumentNullException(nameof(viewModel)); + CabecalhoBloco = new CabecalhoBloco(estilo, "DADOS DOS PRODUTOS / SERVIÇOS"); + + var ad = AlinhamentoHorizontal.Direita; + var ac = AlinhamentoHorizontal.Centro; + var ae = AlinhamentoHorizontal.Esquerda; + + Tabela = new Tabela(Estilo); + var cabecalho4 = ViewModel.Emitente.CRT == "3" ? "O/CST" : "O/CSOSN"; + + if (ViewModel.Orientacao == Orientacao.Retrato) + { + Tabela + .ComColuna(8.5f, ac, "CÓDIGO", "PRODUTO") + .ComColuna(0, ae, "DESCRIÇÃO DO PRODUTO / SERVIÇO") + .ComColuna(5.6F, ac, "NCM/SH") + .ComColuna(3.9F, ac, cabecalho4) + .ComColuna(3.5F, ac, "CFOP") + .ComColuna(3.25F, ac, "UN") + .ComColuna(6F, ad, "QUANTI.") + .ComColuna(6F, ad, "VALOR", "UNIT.") + .ComColuna(6F, ad, "VALOR", "TOTAL") + .ComColuna(6F, ad, "B CÁLC", "ICMS") + .ComColuna(5, ad, "VALOR", "ICMS") + .ComColuna(5, ad, "VALOR", "IPI") + .ComColuna(3.5F, ad, "ALIQ.", "ICMS") + .ComColuna(3.5F, ad, "ALIQ.", "IPI"); + } + if (ViewModel.Orientacao == Orientacao.Paisagem) + { + Tabela + .ComColuna(8.1f, ac, "CÓDIGO PRODUTO") + .ComColuna(0, ae, "DESCRIÇÃO DO PRODUTO / SERVIÇO") + .ComColuna(5.5F, ac, "NCM/SH") + .ComColuna(3.1F, ac, cabecalho4) + .ComColuna(3.1F, ac, "CFOP") + .ComColuna(3F, ac, "UN") + .ComColuna(5.25F, ad, "QUANTI.") + .ComColuna(5.6F, ad, "VALOR UNIT.") + .ComColuna(5.6F, ad, "VALOR TOTAL") + .ComColuna(5.6F, ad, "B CÁLC ICMS") + .ComColuna(5.6F, ad, "VALOR ICMS") + .ComColuna(5.6F, ad, "VALOR IPI") + .ComColuna(3F, ad, "ALIQ.", "ICMS") + .ComColuna(3F, ad, "ALIQ.", "IPI"); + } + + Tabela.AjustarLarguraColunas(); + + foreach (var p in ViewModel.Produtos) + { + var linha = new List + { + p.Codigo, + p.DescricaoCompleta, + p.Ncm, + p.OCst, + p.Cfop.Formatar("N0"), + p.Unidade, + p.Quantidade.Formatar(), + p.ValorUnitario.Formatar(), + p.ValorTotal.Formatar(), + p.BaseIcms.Formatar(), + p.ValorIcms.Formatar(), + p.ValorIpi.Formatar(), + p.AliquotaIcms.Formatar(), + p.AliquotaIpi.Formatar() + }; + + Tabela.AdicionarLinha(linha); + } + } + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + + Tabela.SetPosition(RetanguloTabela.Location); + Tabela.SetSize(RetanguloTabela.Size); + Tabela.Draw(gfx); + + CabecalhoBloco.SetPosition(X, Y); + CabecalhoBloco.Width = Width; + CabecalhoBloco.Draw(gfx); + } + + public RectangleF RetanguloTabela => BoundingBox.CutTop(CabecalhoBloco.Height); + public Boolean CompletamenteDesenhada => Tabela.LinhaAtual == ViewModel.Produtos.Count; + public override bool PossuiContono => false; + } +} diff --git a/NFe.Danfe.PdfClown/Constantes.cs b/NFe.Danfe.PdfClown/Constantes.cs new file mode 100644 index 000000000..0ec4513d8 --- /dev/null +++ b/NFe.Danfe.PdfClown/Constantes.cs @@ -0,0 +1,13 @@ +namespace NFe.Danfe.PdfClown +{ + internal static class Constantes + { + /// + /// Altura do campo em milímetros. + /// + public const float CampoAltura = 6.75F; + + public const float A4Largura = 210; + public const float A4Altura = 297; + } +} diff --git a/NFe.Danfe.PdfClown/DanfeDoc.cs b/NFe.Danfe.PdfClown/DanfeDoc.cs new file mode 100644 index 000000000..f73b694a4 --- /dev/null +++ b/NFe.Danfe.PdfClown/DanfeDoc.cs @@ -0,0 +1,266 @@ +using NFe.Danfe.PdfClown.Blocos; +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Modelo; +using org.pdfclown.documents; +using org.pdfclown.documents.contents.fonts; +using org.pdfclown.files; +using File = org.pdfclown.files.File; + +namespace NFe.Danfe.PdfClown +{ + public class DanfeDoc : IDisposable + { + public DanfeViewModel ViewModel { get; private set; } + public File File { get; private set; } + internal Document PdfDocument { get; private set; } + + internal BlocoCanhoto Canhoto { get; private set; } + internal BlocoIdentificacaoEmitente IdentificacaoEmitente { get; private set; } + + internal List _Blocos; + internal Estilo EstiloPadrao { get; private set; } + + internal List Paginas { get; private set; } + + private StandardType1Font _FonteRegular; + private StandardType1Font _FonteNegrito; + private StandardType1Font _FonteItalico; + private StandardType1Font.FamilyEnum _FonteFamilia; + + private Boolean _FoiGerado; + + private org.pdfclown.documents.contents.xObjects.XObject _LogoObject = null; + + public DanfeDoc(DanfeViewModel viewModel) + { + ViewModel = viewModel ?? throw new ArgumentNullException(nameof(viewModel)); + + _Blocos = new List(); + File = new File(); + PdfDocument = File.Document; + + // De acordo com o item 7.7, a fonte deve ser Times New Roman ou Courier New. + _FonteFamilia = StandardType1Font.FamilyEnum.Times; + _FonteRegular = new StandardType1Font(PdfDocument, _FonteFamilia, false, false); + _FonteNegrito = new StandardType1Font(PdfDocument, _FonteFamilia, true, false); + _FonteItalico = new StandardType1Font(PdfDocument, _FonteFamilia, false, true); + + EstiloPadrao = CriarEstilo(); + + Paginas = new List(); + Canhoto = CriarBloco(); + IdentificacaoEmitente = AdicionarBloco(); + AdicionarBloco(); + + if (ViewModel.LocalRetirada != null && ViewModel.ExibirBlocoLocalRetirada) + AdicionarBloco(); + + if (ViewModel.LocalEntrega != null && ViewModel.ExibirBlocoLocalEntrega) + AdicionarBloco(); + + if (ViewModel.Duplicatas.Count > 0) + AdicionarBloco(); + + AdicionarBloco(ViewModel.Orientacao == Orientacao.Paisagem ? EstiloPadrao : CriarEstilo(4.75F)); + AdicionarBloco(); + AdicionarBloco(CriarEstilo(tFonteCampoConteudo: 8)); + + if (ViewModel.CalculoIssqn.Mostrar) + AdicionarBloco(); + + AdicionarMetadata(); + + _FoiGerado = false; + } + + public void AdicionarLogoImagem(System.IO.Stream stream) + { + if (stream == null) throw new ArgumentNullException(nameof(stream)); + + var img = org.pdfclown.documents.contents.entities.Image.Get(stream); + if (img == null) throw new InvalidOperationException("O logotipo não pode ser carregado, certifique-se que a imagem esteja no formato JPEG não progressivo."); + _LogoObject = img.ToXObject(PdfDocument); + } + + public void AdicionarLogoPdf(System.IO.Stream stream) + { + if (stream == null) throw new ArgumentNullException(nameof(stream)); + + using (var pdfFile = new org.pdfclown.files.File(new org.pdfclown.bytes.Stream(stream))) + { + _LogoObject = pdfFile.Document.Pages[0].ToXObject(PdfDocument); + } + } + + public void AdicionarLogoImagem(string path) + { + if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException(nameof(path)); + + using (var fs = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read)) + { + AdicionarLogoImagem(fs); + } + } + + public void AdicionarLogoPdf(string path) + { + if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException(nameof(path)); + + using (var fs = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read)) + { + AdicionarLogoPdf(fs); + } + } + + private void AdicionarMetadata() + { + var info = PdfDocument.Information; + info[new org.pdfclown.objects.PdfName("ChaveAcesso")] = ViewModel.ChaveAcesso; + info[new org.pdfclown.objects.PdfName("TipoDocumento")] = "DANFE"; + info.CreationDate = DateTime.Now; + info.Creator = string.Format("{0} {1} - {2}", "Zion.Danfe", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version, "https://github.com/Laranjeiras/Zion.Danfe"); + info.Title = "DANFE (Documento auxiliar da NFe)"; + } + + private Estilo CriarEstilo(float tFonteCampoCabecalho = 6, float tFonteCampoConteudo = 10) + { + return new Estilo(_FonteRegular, _FonteNegrito, _FonteItalico, tFonteCampoCabecalho, tFonteCampoConteudo); + } + + public void Gerar() + { + if (_FoiGerado) throw new InvalidOperationException("O Danfe já foi gerado."); + + IdentificacaoEmitente.Logo = _LogoObject; + var tabela = new TabelaProdutosServicos(ViewModel, EstiloPadrao); + + while (true) + { + DanfePagina p = CriarPagina(); + + tabela.SetPosition(p.RetanguloCorpo.Location); + tabela.SetSize(p.RetanguloCorpo.Size); + tabela.Draw(p.Gfx); + + p.Gfx.Stroke(); + p.Gfx.Flush(); + + if (tabela.CompletamenteDesenhada) break; + + } + + PreencherNumeroFolhas(); + _FoiGerado = true; + + } + + private DanfePagina CriarPagina() + { + DanfePagina p = new DanfePagina(this); + Paginas.Add(p); + p.DesenharBlocos(Paginas.Count == 1); + p.DesenharCreditos(); + + if (ViewModel.TipoAmbiente == 2) + p.DesenharAvisoHomologacao(); + + return p; + } + + internal T CriarBloco() where T : BlocoBase + { + return (T)Activator.CreateInstance(typeof(T), ViewModel, EstiloPadrao); + } + + internal T CriarBloco(Estilo estilo) where T : BlocoBase + { + return (T)Activator.CreateInstance(typeof(T), ViewModel, estilo); + } + + internal T AdicionarBloco() where T : BlocoBase + { + var bloco = CriarBloco(); + _Blocos.Add(bloco); + return bloco; + } + + internal T AdicionarBloco(Estilo estilo) where T : BlocoBase + { + var bloco = CriarBloco(estilo); + _Blocos.Add(bloco); + return bloco; + } + + internal void AdicionarBloco(BlocoBase bloco) + { + _Blocos.Add(bloco); + } + + internal void PreencherNumeroFolhas() + { + int nFolhas = Paginas.Count; + for (int i = 0; i < Paginas.Count; i++) + { + Paginas[i].DesenhaNumeroPaginas(i + 1, nFolhas); + } + } + + public void Salvar(string path) + { + if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException(nameof(path)); + + File.Save(path, SerializationModeEnum.Incremental); + } + + public void Salvar(System.IO.Stream stream) + { + if (stream == null) throw new ArgumentNullException(nameof(stream)); + + File.Save(new org.pdfclown.bytes.Stream(stream), SerializationModeEnum.Incremental); + } + + public Byte[] ObterPdfBytes(System.IO.Stream stream) + { + if (stream == null) throw new ArgumentNullException(nameof(stream)); + var pdfStrean = new org.pdfclown.bytes.Stream(stream); + File.Save(pdfStrean, SerializationModeEnum.Incremental); + return pdfStrean.ToByteArray(); + } + + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + File.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + + disposedValue = true; + } + } + + // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. + // ~Danfe() { + // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + // Dispose(false); + // } + + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + // TODO: uncomment the following line if the finalizer is overridden above. + // GC.SuppressFinalize(this); + } + #endregion + } +} diff --git a/NFe.Danfe.PdfClown/DanfePagina.cs b/NFe.Danfe.PdfClown/DanfePagina.cs new file mode 100644 index 000000000..76ea1e0a9 --- /dev/null +++ b/NFe.Danfe.PdfClown/DanfePagina.cs @@ -0,0 +1,141 @@ +using System.Drawing; +using NFe.Danfe.PdfClown.Blocos; +using NFe.Danfe.PdfClown.Elementos; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; +using NFe.Danfe.PdfClown.Tools; +using org.pdfclown.documents; +using org.pdfclown.documents.contents.composition; + +namespace NFe.Danfe.PdfClown +{ + internal class DanfePagina + { + public DanfeDoc Danfe { get; private set; } + public Page PdfPage { get; private set; } + public PrimitiveComposer PrimitiveComposer { get; private set; } + public Gfx Gfx { get; private set; } + public RectangleF RetanguloNumeroFolhas { get; set; } + public RectangleF RetanguloCorpo { get; private set; } + public RectangleF RetanguloDesenhavel { get; private set; } + public RectangleF RetanguloCreditos { get; private set; } + public RectangleF Retangulo { get; private set; } + + public DanfePagina(DanfeDoc danfe) + { + Danfe = danfe ?? throw new ArgumentNullException(nameof(danfe)); + PdfPage = new Page(Danfe.PdfDocument); + Danfe.PdfDocument.Pages.Add(PdfPage); + + PrimitiveComposer = new PrimitiveComposer(PdfPage); + Gfx = new Gfx(PrimitiveComposer); + + if (Danfe.ViewModel.Orientacao == Orientacao.Retrato) + Retangulo = new RectangleF(0, 0, Constantes.A4Largura, Constantes.A4Altura); + else + Retangulo = new RectangleF(0, 0, Constantes.A4Altura, Constantes.A4Largura); + + RetanguloDesenhavel = Retangulo.InflatedRetangle(Danfe.ViewModel.Margem); + RetanguloCreditos = new RectangleF(RetanguloDesenhavel.X, RetanguloDesenhavel.Bottom + Danfe.EstiloPadrao.PaddingSuperior, RetanguloDesenhavel.Width, Retangulo.Height - RetanguloDesenhavel.Height - Danfe.EstiloPadrao.PaddingSuperior); + PdfPage.Size = new SizeF(Retangulo.Width.ToPoint(), Retangulo.Height.ToPoint()); + } + + public void DesenharCreditos() + { + //Gfx.DrawString($"[Zion.Danfe] {Strings.TextoCreditos}", RetanguloCreditos, Danfe.EstiloPadrao.CriarFonteItalico(6), AlinhamentoHorizontal.Direita); + } + + private void DesenharCanhoto() + { + if (Danfe.ViewModel.QuantidadeCanhotos == 0) return; + + var canhoto = Danfe.Canhoto; + canhoto.SetPosition(RetanguloDesenhavel.Location); + + if (Danfe.ViewModel.Orientacao == Orientacao.Retrato) + { + canhoto.Width = RetanguloDesenhavel.Width; + + for (int i = 0; i < Danfe.ViewModel.QuantidadeCanhotos; i++) + { + canhoto.Draw(Gfx); + canhoto.Y += canhoto.Height; + } + + RetanguloDesenhavel = RetanguloDesenhavel.CutTop(canhoto.Height * Danfe.ViewModel.QuantidadeCanhotos); + } + else + { + canhoto.Width = RetanguloDesenhavel.Height; + Gfx.PrimitiveComposer.BeginLocalState(); + Gfx.PrimitiveComposer.Rotate(90, new PointF(0, canhoto.Width + canhoto.X + canhoto.Y).ToPointMeasure()); + + for (int i = 0; i < Danfe.ViewModel.QuantidadeCanhotos; i++) + { + canhoto.Draw(Gfx); + canhoto.Y += canhoto.Height; + } + + Gfx.PrimitiveComposer.End(); + RetanguloDesenhavel = RetanguloDesenhavel.CutLeft(canhoto.Height * Danfe.ViewModel.QuantidadeCanhotos); + + } + } + + public void DesenhaNumeroPaginas(int n, int total) + { + if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n)); + if (total <= 0) throw new ArgumentOutOfRangeException(nameof(n)); + if (n > total) throw new ArgumentOutOfRangeException("O número da página atual deve ser menor que o total."); + + Gfx.DrawString($"Folha {n}/{total}", RetanguloNumeroFolhas, Danfe.EstiloPadrao.FonteNumeroFolhas, AlinhamentoHorizontal.Centro); + Gfx.Flush(); + } + + public void DesenharAvisoHomologacao() + { + TextStack ts = new TextStack(RetanguloCorpo) { AlinhamentoVertical = AlinhamentoVertical.Centro, AlinhamentoHorizontal = AlinhamentoHorizontal.Centro, LineHeightScale = 0.9F } + .AddLine("SEM VALOR FISCAL", Danfe.EstiloPadrao.CriarFonteRegular(48)) + .AddLine("AMBIENTE DE HOMOLOGAÇÃO", Danfe.EstiloPadrao.CriarFonteRegular(30)); + + Gfx.PrimitiveComposer.BeginLocalState(); + Gfx.PrimitiveComposer.SetFillColor(new org.pdfclown.documents.contents.colorSpaces.DeviceRGBColor(0.35, 0.35, 0.35)); + ts.Draw(Gfx); + Gfx.PrimitiveComposer.End(); + } + + public void DesenharBlocos(bool isPrimeirapagina = false) + { + if (isPrimeirapagina && Danfe.ViewModel.QuantidadeCanhotos > 0) DesenharCanhoto(); + + var blocos = isPrimeirapagina ? Danfe._Blocos : Danfe._Blocos.Where(x => x.VisivelSomentePrimeiraPagina == false); + + foreach (var bloco in blocos) + { + bloco.Width = RetanguloDesenhavel.Width; + + if (bloco.Posicao == PosicaoBloco.Topo) + { + bloco.SetPosition(RetanguloDesenhavel.Location); + RetanguloDesenhavel = RetanguloDesenhavel.CutTop(bloco.Height); + } + else + { + bloco.SetPosition(RetanguloDesenhavel.X, RetanguloDesenhavel.Bottom - bloco.Height); + RetanguloDesenhavel = RetanguloDesenhavel.CutBottom(bloco.Height); + } + + bloco.Draw(Gfx); + + if (bloco is BlocoIdentificacaoEmitente) + { + var rf = (bloco as BlocoIdentificacaoEmitente).RetanguloNumeroFolhas; + RetanguloNumeroFolhas = rf; + } + } + + RetanguloCorpo = RetanguloDesenhavel; + Gfx.Flush(); + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/Barcode128C.cs b/NFe.Danfe.PdfClown/Elementos/Barcode128C.cs new file mode 100644 index 000000000..524688bad --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/Barcode128C.cs @@ -0,0 +1,220 @@ +using System.Drawing; +using System.Text.RegularExpressions; +using NFe.Danfe.PdfClown.Graphics; + +namespace NFe.Danfe.PdfClown.Elementos +{ + internal class Barcode128C : ElementoBase + { + private static byte[][] Dic; + + public static readonly float MargemVertical = 2; + + /// + /// Código a ser codificado em barras. + /// + public string Code { get; private set; } + + /// + /// Largura do código de barras. + /// + public float Largura { get; set; } + + static Barcode128C() + { + + Dic = new byte[][] + { + new byte[] { 2,1,2,2,2,2}, + new byte[] { 2,2,2,1,2,2}, + new byte[] { 2,2,2,2,2,1}, + new byte[] { 1,2,1,2,2,3}, + new byte[] { 1,2,1,3,2,2}, + new byte[] { 1,3,1,2,2,2}, + new byte[] { 1,2,2,2,1,3}, + new byte[] { 1,2,2,3,1,2}, + new byte[] { 1,3,2,2,1,2}, + new byte[] { 2,2,1,2,1,3}, + new byte[] { 2,2,1,3,1,2}, + new byte[] { 2,3,1,2,1,2}, + new byte[] { 1,1,2,2,3,2}, + new byte[] { 1,2,2,1,3,2}, + new byte[] { 1,2,2,2,3,1}, + new byte[] { 1,1,3,2,2,2}, + new byte[] { 1,2,3,1,2,2}, + new byte[] { 1,2,3,2,2,1}, + new byte[] { 2,2,3,2,1,1}, + new byte[] { 2,2,1,1,3,2}, + new byte[] { 2,2,1,2,3,1}, + new byte[] { 2,1,3,2,1,2}, + new byte[] { 2,2,3,1,1,2}, + new byte[] { 3,1,2,1,3,1}, + new byte[] { 3,1,1,2,2,2}, + new byte[] { 3,2,1,1,2,2}, + new byte[] { 3,2,1,2,2,1}, + new byte[] { 3,1,2,2,1,2}, + new byte[] { 3,2,2,1,1,2}, + new byte[] { 3,2,2,2,1,1}, + new byte[] { 2,1,2,1,2,3}, + new byte[] { 2,1,2,3,2,1}, + new byte[] { 2,3,2,1,2,1}, + new byte[] { 1,1,1,3,2,3}, + new byte[] { 1,3,1,1,2,3}, + new byte[] { 1,3,1,3,2,1}, + new byte[] { 1,1,2,3,1,3}, + new byte[] { 1,3,2,1,1,3}, + new byte[] { 1,3,2,3,1,1}, + new byte[] { 2,1,1,3,1,3}, + new byte[] { 2,3,1,1,1,3}, + new byte[] { 2,3,1,3,1,1}, + new byte[] { 1,1,2,1,3,3}, + new byte[] { 1,1,2,3,3,1}, + new byte[] { 1,3,2,1,3,1}, + new byte[] { 1,1,3,1,2,3}, + new byte[] { 1,1,3,3,2,1}, + new byte[] { 1,3,3,1,2,1}, + new byte[] { 3,1,3,1,2,1}, + new byte[] { 2,1,1,3,3,1}, + new byte[] { 2,3,1,1,3,1}, + new byte[] { 2,1,3,1,1,3}, + new byte[] { 2,1,3,3,1,1}, + new byte[] { 2,1,3,1,3,1}, + new byte[] { 3,1,1,1,2,3}, + new byte[] { 3,1,1,3,2,1}, + new byte[] { 3,3,1,1,2,1}, + new byte[] { 3,1,2,1,1,3}, + new byte[] { 3,1,2,3,1,1}, + new byte[] { 3,3,2,1,1,1}, + new byte[] { 3,1,4,1,1,1}, + new byte[] { 2,2,1,4,1,1}, + new byte[] { 4,3,1,1,1,1}, + new byte[] { 1,1,1,2,2,4}, + new byte[] { 1,1,1,4,2,2}, + new byte[] { 1,2,1,1,2,4}, + new byte[] { 1,2,1,4,2,1}, + new byte[] { 1,4,1,1,2,2}, + new byte[] { 1,4,1,2,2,1}, + new byte[] { 1,1,2,2,1,4}, + new byte[] { 1,1,2,4,1,2}, + new byte[] { 1,2,2,1,1,4}, + new byte[] { 1,2,2,4,1,1}, + new byte[] { 1,4,2,1,1,2}, + new byte[] { 1,4,2,2,1,1}, + new byte[] { 2,4,1,2,1,1}, + new byte[] { 2,2,1,1,1,4}, + new byte[] { 4,1,3,1,1,1}, + new byte[] { 2,4,1,1,1,2}, + new byte[] { 1,3,4,1,1,1}, + new byte[] { 1,1,1,2,4,2}, + new byte[] { 1,2,1,1,4,2}, + new byte[] { 1,2,1,2,4,1}, + new byte[] { 1,1,4,2,1,2}, + new byte[] { 1,2,4,1,1,2}, + new byte[] { 1,2,4,2,1,1}, + new byte[] { 4,1,1,2,1,2}, + new byte[] { 4,2,1,1,1,2}, + new byte[] { 4,2,1,2,1,1}, + new byte[] { 2,1,2,1,4,1}, + new byte[] { 2,1,4,1,2,1}, + new byte[] { 4,1,2,1,2,1}, + new byte[] { 1,1,1,1,4,3}, + new byte[] { 1,1,1,3,4,1}, + new byte[] { 1,3,1,1,4,1}, + new byte[] { 1,1,4,1,1,3}, + new byte[] { 1,1,4,3,1,1}, + new byte[] { 4,1,1,1,1,3}, + new byte[] { 4,1,1,3,1,1}, + new byte[] { 1,1,3,1,4,1}, + new byte[] { 1,1,4,1,3,1}, + new byte[] { 3,1,1,1,4,1}, + new byte[] { 4,1,1,1,3,1}, + new byte[] { 2,1,1,4,1,2}, + new byte[] { 2,1,1,2,1,4}, + new byte[] { 2,1,1,2,3,2}, + new byte[] { 2,3,3,1,1,1,2} + + }; + + } + + public Barcode128C(string code, Estilo estilo, float largura = 75F) : base(estilo) + { + if (string.IsNullOrWhiteSpace(code)) + { + throw new ArgumentException("O código não pode ser vazio.", "code"); + } + + if (!Regex.IsMatch(code, @"^\d+$")) + { + throw new ArgumentException("O código deve apenas conter digítos numéricos.", "code"); + } + + if (code.Length % 2 != 0) + { + Code = "0" + code; + } + else + { + Code = code; + } + + Largura = largura; + } + + private void DrawBarcode(RectangleF rect, Gfx gfx) + { + + List codeBytes = new List(); + + codeBytes.Add(105); + + for (int i = 0; i < this.Code.Length; i += 2) + { + byte b = byte.Parse(this.Code.Substring(i, 2)); + codeBytes.Add(b); + } + + // Calcular dígito verificador + int cd = 105; + + for (int i = 1; i < codeBytes.Count; i++) + { + cd += i * codeBytes[i]; + cd %= 103; + } + + codeBytes.Add((byte)cd); + codeBytes.Add(106); + + float n = codeBytes.Count * 11 + 2; + float w = rect.Width / n; + + float x = 0; + + for (int i = 0; i < codeBytes.Count; i++) + { + byte[] pt = Barcode128C.Dic[codeBytes[i]]; + + for (int i2 = 0; i2 < pt.Length; i2++) + { + if (i2 % 2 == 0) + { + gfx.DrawRectangle(rect.X + x, rect.Y, w * pt[i2], rect.Height); + } + + x += w * pt[i2]; + } + } + + gfx.Fill(); + } + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + + float w2 = (Width - Largura) / 2F; + DrawBarcode(new RectangleF(X + w2, Y + MargemVertical, Largura, Height - 2 * MargemVertical), gfx); + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/CabecalhoBloco.cs b/NFe.Danfe.PdfClown/Elementos/CabecalhoBloco.cs new file mode 100644 index 000000000..2def70674 --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/CabecalhoBloco.cs @@ -0,0 +1,29 @@ +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; + +namespace NFe.Danfe.PdfClown.Elementos +{ + /// + /// Cabeçalho do bloco, normalmente um texto em caixa alta. + /// + internal class CabecalhoBloco : ElementoBase + { + public const float MargemSuperior = 0.8F; + public string Cabecalho { get; set; } + + public CabecalhoBloco(Estilo estilo, string cabecalho) : base(estilo) + { + Cabecalho = cabecalho ?? throw new ArgumentNullException(cabecalho); + } + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + gfx.DrawString(Cabecalho.ToUpper(), BoundingBox, Estilo.FonteBlocoCabecalho, + AlinhamentoHorizontal.Esquerda, AlinhamentoVertical.Base); + } + + public override float Height { get => MargemSuperior + Estilo.FonteBlocoCabecalho.AlturaLinha; set => throw new NotSupportedException(); } + public override bool PossuiContono => false; + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/Campo.cs b/NFe.Danfe.PdfClown/Elementos/Campo.cs new file mode 100644 index 000000000..5e9d885ec --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/Campo.cs @@ -0,0 +1,97 @@ +using System.Drawing; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Elementos +{ + /// + /// Campo de única linha. + /// + internal class Campo : ElementoBase + { + public virtual string Cabecalho { get; set; } + public virtual string Conteudo { get; set; } + + public AlinhamentoHorizontal AlinhamentoHorizontalConteudo { get; set; } + + public Boolean IsConteudoNegrito { get; set; } + + public Campo(string cabecalho, string conteudo, Estilo estilo, AlinhamentoHorizontal alinhamentoHorizontalConteudo = AlinhamentoHorizontal.Esquerda) : base(estilo) + { + Cabecalho = cabecalho; + this.Conteudo = conteudo; + AlinhamentoHorizontalConteudo = alinhamentoHorizontalConteudo; + IsConteudoNegrito = true; + Height = Constantes.CampoAltura; + } + + protected virtual void DesenharCabecalho(Gfx gfx) + { + if (!string.IsNullOrWhiteSpace(Cabecalho)) + { + gfx.DrawString(Cabecalho.ToUpper(), RetanguloDesenhvael, Estilo.FonteCampoCabecalho, AlinhamentoHorizontal.Esquerda, AlinhamentoVertical.Topo); + } + } + + protected virtual void DesenharConteudo(Gfx gfx) + { + var rDesenhavel = RetanguloDesenhvael; + var texto = Conteudo; + + var fonte = IsConteudoNegrito ? Estilo.FonteCampoConteudoNegrito : Estilo.FonteCampoConteudo; + fonte = fonte.Clonar(); + + if (!string.IsNullOrWhiteSpace(Conteudo)) + { + var textWidth = fonte.MedirLarguraTexto(Conteudo); + + // Trata o overflown + if (textWidth > rDesenhavel.Width) + { + fonte.Tamanho = rDesenhavel.Width * fonte.Tamanho / textWidth; + + if (fonte.Tamanho < Estilo.FonteTamanhoMinimo) + { + fonte.Tamanho = Estilo.FonteTamanhoMinimo; + + texto = "..."; + string texto2; + + for (int i = 1; i <= Conteudo.Length; i++) + { + texto2 = Conteudo.Substring(0, i) + "..."; + if (fonte.MedirLarguraTexto(texto2) < rDesenhavel.Width) + { + texto = texto2; + } + else + { + break; + } + + } + } + } + + gfx.DrawString(texto, rDesenhavel, fonte, AlinhamentoHorizontalConteudo, AlinhamentoVertical.Base); + + } + } + + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + DesenharCabecalho(gfx); + DesenharConteudo(gfx); + } + + public RectangleF RetanguloDesenhvael => BoundingBox.InflatedRetangle(Estilo.PaddingSuperior, Estilo.PaddingInferior, Estilo.PaddingHorizontal); + + public override string ToString() + { + return Cabecalho; + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/CampoMultilinha.cs b/NFe.Danfe.PdfClown/Elementos/CampoMultilinha.cs new file mode 100644 index 000000000..80b170498 --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/CampoMultilinha.cs @@ -0,0 +1,45 @@ +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; + +namespace NFe.Danfe.PdfClown.Elementos +{ + /// + /// Campo multilinha. + /// + internal class CampoMultilinha : Campo + { + TextBlock _tbConteudo; + + public CampoMultilinha(string cabecalho, string conteudo, Estilo estilo, AlinhamentoHorizontal alinhamentoHorizontalConteudo = AlinhamentoHorizontal.Esquerda) + : base(cabecalho, conteudo, estilo, alinhamentoHorizontalConteudo) + { + _tbConteudo = new TextBlock(conteudo, estilo.FonteCampoConteudo); + IsConteudoNegrito = false; + } + + protected override void DesenharConteudo(Gfx gfx) + { + if (!string.IsNullOrWhiteSpace(Conteudo)) + { + _tbConteudo.SetPosition(RetanguloDesenhvael.X, RetanguloDesenhvael.Y + Estilo.FonteCampoCabecalho.AlturaLinha + Estilo.PaddingInferior); + _tbConteudo.Draw(gfx); + } + } + + public override float Height + { + get + { + return Math.Max(_tbConteudo.Height + Estilo.FonteCampoCabecalho.AlturaLinha + Estilo.PaddingSuperior + 2 * Estilo.PaddingInferior, base.Height); + } + set + { + base.Height = value; + } + } + + public override string Conteudo { get => base.Conteudo; set { base.Conteudo = value; } } + public override float Width { get => base.Width; set { base.Width = value; _tbConteudo.Width = value - 2 * Estilo.PaddingHorizontal; } } + + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/CampoNumerico.cs b/NFe.Danfe.PdfClown/Elementos/CampoNumerico.cs new file mode 100644 index 000000000..dc2185159 --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/CampoNumerico.cs @@ -0,0 +1,27 @@ +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Elementos +{ + /// + /// Campo para valores numéricos. + /// + internal class CampoNumerico : Campo + { + private double? ConteudoNumerico { get; set; } + public int CasasDecimais { get; set; } + + public CampoNumerico(string cabecalho, double? conteudoNumerico, Estilo estilo, int casasDecimais = 2) : base(cabecalho, null, estilo, AlinhamentoHorizontal.Direita) + { + CasasDecimais = casasDecimais; + ConteudoNumerico = conteudoNumerico; + } + + protected override void DesenharConteudo(Gfx gfx) + { + base.Conteudo = ConteudoNumerico.HasValue ? ConteudoNumerico.Value.ToString($"N{CasasDecimais}", Formatador.Cultura) : null; + base.DesenharConteudo(gfx); + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/DrawableBase.cs b/NFe.Danfe.PdfClown/Elementos/DrawableBase.cs new file mode 100644 index 000000000..eb2e2f5e4 --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/DrawableBase.cs @@ -0,0 +1,51 @@ +using System.Drawing; +using NFe.Danfe.PdfClown.Graphics; + +namespace NFe.Danfe.PdfClown.Elementos +{ + /// + /// Define um objeto desenhável. + /// + internal abstract class DrawableBase + { + public virtual float X { get; set; } + public virtual float Y { get; set; } + + public virtual float Width { get; set; } + public virtual float Height { get; set; } + + public PointF Position => new PointF(X, Y); + public SizeF Size => new SizeF(Width, Height); + + public DrawableBase() + { + } + + public virtual void Draw(Gfx gfx) + { + if (gfx == null) throw new ArgumentNullException(nameof(gfx)); + if (Width <= 0) throw new InvalidOperationException("Width is invalid."); + if (Height <= 0) throw new InvalidOperationException("Height is invalid."); + if (X < 0) throw new InvalidOperationException("X is invalid."); + if (Y < 0) throw new InvalidOperationException("X is invalid."); + } + + public virtual void SetPosition(float x, float y) + { + X = x; + Y = y; + } + + public virtual void SetPosition(PointF p) => SetPosition(p.X, p.Y); + + public virtual void SetSize(float w, float h) + { + Width = w; + if (Height != h) + Height = h; + } + public virtual void SetSize(SizeF s) => SetSize(s.Width, s.Height); + + public RectangleF BoundingBox => new RectangleF(X, Y, Width, Height); + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/Duplicata.cs b/NFe.Danfe.PdfClown/Elementos/Duplicata.cs new file mode 100644 index 000000000..0f970b7c5 --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/Duplicata.cs @@ -0,0 +1,48 @@ +using NFe.Danfe.PdfClown.Atributos; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; +using NFe.Danfe.PdfClown.Modelo; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Elementos +{ + [AlturaFixa] + internal class Duplicata : ElementoBase + { + public Fonte FonteA { get; private set; } + public Fonte FonteB { get; private set; } + public DuplicataViewModel ViewModel { get; private set; } + + private static readonly string[] Chaves = { "Número", "Vencimento:", "Valor:" }; + + public Duplicata(Estilo estilo, DuplicataViewModel viewModel) : base(estilo) + { + ViewModel = viewModel; + FonteA = estilo.CriarFonteRegular(7.5F); + FonteB = estilo.CriarFonteNegrito(7.5F); + } + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + + var r = BoundingBox.InflatedRetangle(Estilo.PaddingSuperior, Estilo.PaddingInferior, Estilo.PaddingHorizontal); + + string[] valores = { ViewModel.Numero, ViewModel.Vecimento.Formatar(), ViewModel.Valor.FormatarMoeda() }; + + for (int i = 0; i < Chaves.Length; i++) + { + gfx.DrawString(Chaves[i], r, FonteA, AlinhamentoHorizontal.Esquerda); + gfx.DrawString(valores[i], r, FonteB, AlinhamentoHorizontal.Direita); + r = r.CutTop(FonteB.AlturaLinha); + } + + } + + public override float Height + { + get => 3 * FonteB.AlturaLinha + Estilo.PaddingSuperior + Estilo.PaddingInferior; + set => throw new NotSupportedException(); + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/ElementoBase.cs b/NFe.Danfe.PdfClown/Elementos/ElementoBase.cs new file mode 100644 index 000000000..b8ea8f0f8 --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/ElementoBase.cs @@ -0,0 +1,25 @@ +using NFe.Danfe.PdfClown.Graphics; + +namespace NFe.Danfe.PdfClown.Elementos +{ + /// + /// Elemento básico no DANFE. + /// + internal abstract class ElementoBase : DrawableBase + { + public Estilo Estilo { get; protected set; } + public virtual bool PossuiContono => true; + + public ElementoBase(Estilo estilo) + { + Estilo = estilo ?? throw new ArgumentNullException(nameof(estilo)); + } + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + if (PossuiContono) + gfx.StrokeRectangle(BoundingBox, 0.25f); + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/ElementoVazio.cs b/NFe.Danfe.PdfClown/Elementos/ElementoVazio.cs new file mode 100644 index 000000000..e09b9dbf8 --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/ElementoVazio.cs @@ -0,0 +1,11 @@ +using NFe.Danfe.PdfClown.Graphics; + +namespace NFe.Danfe.PdfClown.Elementos +{ + internal class ElementoVazio : DrawableBase + { + public override void Draw(Gfx gfx) + { + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/Estilo.cs b/NFe.Danfe.PdfClown/Elementos/Estilo.cs new file mode 100644 index 000000000..cc62508de --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/Estilo.cs @@ -0,0 +1,49 @@ +using NFe.Danfe.PdfClown.Graphics; +using pcf = org.pdfclown.documents.contents.fonts; + +namespace NFe.Danfe.PdfClown.Elementos +{ + /// + /// Coleção de fontes e medidas a serem compartilhadas entre os elementos básicos. + /// + internal class Estilo + { + public float PaddingSuperior { get; set; } + public float PaddingInferior { get; set; } + public float PaddingHorizontal { get; set; } + public float FonteTamanhoMinimo { get; set; } + + public pcf.Font FonteInternaRegular { get; set; } + public pcf.Font FonteInternaNegrito { get; set; } + public pcf.Font FonteInternaItalico { get; set; } + + public Fonte FonteCampoCabecalho { get; private set; } + public Fonte FonteCampoConteudo { get; private set; } + public Fonte FonteCampoConteudoNegrito { get; private set; } + public Fonte FonteBlocoCabecalho { get; private set; } + public Fonte FonteNumeroFolhas { get; private set; } + + public Estilo(pcf.Font fontRegular, pcf.Font fontBold, pcf.Font fontItalic, float tamanhoFonteCampoCabecalho = 6, float tamanhoFonteConteudo = 10) + { + PaddingHorizontal = 0.75F; + PaddingSuperior = 0.65F; + PaddingInferior = 0.3F; + + FonteInternaRegular = fontRegular; + FonteInternaNegrito = fontBold; + FonteInternaItalico = fontItalic; + + FonteCampoCabecalho = CriarFonteRegular(tamanhoFonteCampoCabecalho); + FonteCampoConteudo = CriarFonteRegular(tamanhoFonteConteudo); + FonteCampoConteudoNegrito = CriarFonteNegrito(tamanhoFonteConteudo); + FonteBlocoCabecalho = CriarFonteRegular(7); + FonteNumeroFolhas = CriarFonteNegrito(10F); + FonteTamanhoMinimo = 5.75F; + } + + public Fonte CriarFonteRegular(float emSize) => new Fonte(FonteInternaRegular, emSize); + public Fonte CriarFonteNegrito(float emSize) => new Fonte(FonteInternaNegrito, emSize); + public Fonte CriarFonteItalico(float emSize) => new Fonte(FonteInternaItalico, emSize); + + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/FlexibleLine.cs b/NFe.Danfe.PdfClown/Elementos/FlexibleLine.cs new file mode 100644 index 000000000..5b495e664 --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/FlexibleLine.cs @@ -0,0 +1,108 @@ +using NFe.Danfe.PdfClown.Atributos; +using NFe.Danfe.PdfClown.Graphics; + +namespace NFe.Danfe.PdfClown.Elementos +{ + /// + /// Linha flexível que posiciona e muda a largura dos seus elementos de forma proporcional. + /// + internal class FlexibleLine : DrawableBase + { + /// + /// Elementos em ordem da linha. + /// + public List Elementos { get; private set; } + + /// + /// Largura dos elementos, em porcentagem. + /// + public List ElementosLargurasP { get; private set; } + + public FlexibleLine() + { + Elementos = new List(); + ElementosLargurasP = new List(); + } + + public FlexibleLine(float width, float height) : this() + { + Width = width; + Height = height; + } + + public virtual FlexibleLine ComElemento(DrawableBase db) + { + Elementos.Add(db ?? throw new ArgumentNullException(nameof(db))); + return this; + } + + public virtual FlexibleLine ComLarguras(params float[] elementosLarguras) + { + if (elementosLarguras.Length != Elementos.Count) throw new ArgumentException("A quantidade de larguras deve ser igual a de elementos."); + + float somaLarguras = elementosLarguras.Sum(); + if (somaLarguras > 100) throw new ArgumentOutOfRangeException("A soma das larguras passam de 100%."); + + var p = (100 - somaLarguras) / elementosLarguras.Where(x => x == 0).Count(); + + for (int i = 0; i < elementosLarguras.Length; i++) + { + if (elementosLarguras[i] == 0) + elementosLarguras[i] = p; + } + + ElementosLargurasP = elementosLarguras.ToList(); + return this; + } + + public virtual FlexibleLine ComLargurasIguais() + { + float w = 100F / Elementos.Count; + + for (int i = 0; i < Elementos.Count; i++) + { + ElementosLargurasP.Add(w); + } + + return this; + } + + public void Posicionar() + { + float wTotal = Elementos.Sum(s => s.Width); + + float x = X, y = Y; + + for (int i = 0; i < Elementos.Count; i++) + { + var e = Elementos[i]; + var ew = (Width * ElementosLargurasP[i]) / 100F; + + if (Attribute.IsDefined(e.GetType(), typeof(AlturaFixaAttribute))) + { + e.Width = ew; + } + else + { + e.SetSize(ew, Height); + } + + e.SetPosition(x, y); + x += e.Width; + } + } + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + + Posicionar(); + + foreach (var elemento in Elementos) + { + elemento.Draw(gfx); + } + + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/IdentificacaoEmitente.cs b/NFe.Danfe.PdfClown/Elementos/IdentificacaoEmitente.cs new file mode 100644 index 000000000..364b595c8 --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/IdentificacaoEmitente.cs @@ -0,0 +1,85 @@ +using System.Drawing; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; +using NFe.Danfe.PdfClown.Modelo; +using NFe.Danfe.PdfClown.Tools; +using org.pdfclown.documents.contents.xObjects; + +namespace NFe.Danfe.PdfClown.Elementos +{ + internal class IdentificacaoEmitente : ElementoBase + { + public DanfeViewModel ViewModel { get; private set; } + public XObject Logo { get; set; } + + public IdentificacaoEmitente(Estilo estilo, DanfeViewModel viewModel) : base(estilo) + { + ViewModel = viewModel; + Logo = null; + } + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + + // 7.7.6 Conteúdo do Quadro Dados do Emitente + // Deverá estar impresso em negrito.A razão social e/ ou nome fantasia deverá ter tamanho + // mínimo de doze(12) pontos, ou 17 CPP e os demais dados do emitente, endereço, + // município, CEP, fone / fax deverão ter tamanho mínimo de oito(8) pontos, ou 17 CPP. + + var rp = BoundingBox.InflatedRetangle(0.75F); + float alturaMaximaLogoHorizontal = 14F; + + Fonte f2 = Estilo.CriarFonteNegrito(12); + Fonte f3 = Estilo.CriarFonteRegular(8); + + if (Logo == null) + { + var f1 = Estilo.CriarFonteRegular(6); + gfx.DrawString("IDENTIFICAÇÃO DO EMITENTE", rp, f1, AlinhamentoHorizontal.Centro); + rp = rp.CutTop(f1.AlturaLinha); + } + else + { + RectangleF rLogo; + + //Logo Horizontal + if (Logo.Size.Width > Logo.Size.Height) + { + rLogo = new RectangleF(rp.X, rp.Y, rp.Width, alturaMaximaLogoHorizontal); + rp = rp.CutTop(alturaMaximaLogoHorizontal); + } + //Logo Vertical/Quadrado + else + { + float lw = rp.Height * Logo.Size.Width / Logo.Size.Height; + rLogo = new RectangleF(rp.X, rp.Y, lw, rp.Height); + rp = rp.CutLeft(lw); + } + + gfx.ShowXObject(Logo, rLogo); + + } + + var emitente = ViewModel.Emitente; + + string nome = emitente.RazaoSocial; + + if (ViewModel.PreferirEmitenteNomeFantasia) + { + nome = !string.IsNullOrWhiteSpace(emitente.NomeFantasia) ? emitente.NomeFantasia : emitente.RazaoSocial; + } + var ts = new TextStack(rp) { LineHeightScale = 1 } + .AddLine(nome, f2) + .AddLine(emitente.EnderecoLinha1.Trim(), f3) + .AddLine(emitente.EnderecoLinha2.Trim(), f3) + .AddLine(emitente.EnderecoLinha3.Trim(), f3); + + ts.AlinhamentoHorizontal = AlinhamentoHorizontal.Centro; + ts.AlinhamentoVertical = AlinhamentoVertical.Centro; + ts.Draw(gfx); + + + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/LinhaCampos.cs b/NFe.Danfe.PdfClown/Elementos/LinhaCampos.cs new file mode 100644 index 000000000..b3bd9eaf4 --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/LinhaCampos.cs @@ -0,0 +1,37 @@ +using NFe.Danfe.PdfClown.Enumeracoes; + +namespace NFe.Danfe.PdfClown.Elementos +{ + /// + /// Linha de campos, posiciona e muda a largura desses elementos de forma proporcional. + /// + internal class LinhaCampos : FlexibleLine + { + public Estilo Estilo { get; private set; } + + public LinhaCampos(Estilo estilo, float width, float height = Constantes.CampoAltura) : base() + { + Estilo = estilo; + SetSize(width, height); + } + + public LinhaCampos(Estilo estilo) : base() + { + Estilo = estilo; + } + + public virtual LinhaCampos ComCampo(string cabecalho, string conteudo, AlinhamentoHorizontal alinhamentoHorizontalConteudo = AlinhamentoHorizontal.Esquerda) + { + var campo = new Campo(cabecalho, conteudo, Estilo, alinhamentoHorizontalConteudo); + Elementos.Add(campo); + return this; + } + + public virtual LinhaCampos ComCampoNumerico(string cabecalho, double? conteudoNumerico, int casasDecimais = 2) + { + var campo = new CampoNumerico(cabecalho, conteudoNumerico, Estilo, casasDecimais); + Elementos.Add(campo); + return this; + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/LinhaTracejada.cs b/NFe.Danfe.PdfClown/Elementos/LinhaTracejada.cs new file mode 100644 index 000000000..e5543bf0f --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/LinhaTracejada.cs @@ -0,0 +1,31 @@ +using System.Drawing; +using NFe.Danfe.PdfClown.Graphics; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Elementos +{ + internal class LinhaTracejada : DrawableBase + { + public float Margin { get; set; } + public double[] DashPattern { get; set; } + + public LinhaTracejada(float margin) + { + Margin = margin; + } + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + + gfx.PrimitiveComposer.BeginLocalState(); + gfx.PrimitiveComposer.SetLineDash(new org.pdfclown.documents.contents.LineDash(new double[] { 3, 2 })); + gfx.PrimitiveComposer.DrawLine(new PointF(BoundingBox.Left, Y + Margin).ToPointMeasure(), new PointF(BoundingBox.Right, Y + Margin).ToPointMeasure()); + gfx.PrimitiveComposer.Stroke(); + gfx.PrimitiveComposer.End(); + + } + + public override float Height { get => 2 * Margin; set => throw new NotSupportedException(); } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/NumeroNfSerie.cs b/NFe.Danfe.PdfClown/Elementos/NumeroNfSerie.cs new file mode 100644 index 000000000..18db5ee70 --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/NumeroNfSerie.cs @@ -0,0 +1,44 @@ +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Elementos +{ + class NumeroNfSerie : ElementoBase + { + public string NfNumero { get; private set; } + public string NfSerie { get; private set; } + + public NumeroNfSerie(Estilo estilo, string nfNumero, string nfSerie) : base(estilo) + { + NfNumero = nfNumero; + NfSerie = nfSerie; + } + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + + var r = BoundingBox.InflatedRetangle(1); + + var f1 = Estilo.CriarFonteNegrito(14); + var f2 = Estilo.CriarFonteNegrito(11F); + + gfx.DrawString("NF-e", r, f1, AlinhamentoHorizontal.Centro); + + r = r.CutTop(f1.AlturaLinha); + + TextStack ts = new TextStack(r) + { + AlinhamentoHorizontal = AlinhamentoHorizontal.Centro, + AlinhamentoVertical = AlinhamentoVertical.Centro, + LineHeightScale = 1F + } + .AddLine($"Nº.: {NfNumero}", f2) + .AddLine($"Série: {NfSerie}", f2); + + ts.Draw(gfx); + + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/NumeroNfSerie2.cs b/NFe.Danfe.PdfClown/Elementos/NumeroNfSerie2.cs new file mode 100644 index 000000000..212486f7b --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/NumeroNfSerie2.cs @@ -0,0 +1,100 @@ +using System.Drawing; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; +using NFe.Danfe.PdfClown.Modelo; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Elementos +{ + class NumeroNfSerie2 : ElementoBase + { + public RectangleF RetanguloNumeroFolhas { get; private set; } + public DanfeViewModel ViewModel { get; private set; } + + public NumeroNfSerie2(Estilo estilo, DanfeViewModel viewModel) : base(estilo) + { + ViewModel = viewModel; + } + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + + // 7.7.4 Conteúdo do Bloco de Campos de Identificação do Documento + // O conteúdo dos campos “DANFE”, “entrada ou saída”, “número”, “série” e “folhas do + // documento” deverá ser impresso em caixa alta(maiúsculas). Além disto: + // a descrição “DANFE” deverá estar impressa em negrito e ter tamanho mínimo de + // doze(12) pontos, ou 10 CPP; + // + // a série e número da NF-e, o número de ordem da folha, o total de folhas do + // DANFE e o número identificador do tipo de operação(se “ENTRADA” ou + // “SAÍDA”, conforme tag “tpNF”) deverão estar impressos em negrito e ter + // tamanho mínimo de dez(10) pontos, ou 10 CPP; + // + // a identificação “DOCUMENTO AUXILIAR DA NOTA FISCAL ELETRÔNICA” e as + // descrições do tipo de operação, “ENTRADA” ou “SAÍDA” deverão ter tamanho + // mínimo de oito(8) pontos, ou 17 CPP. + + float paddingHorizontal = ViewModel.Orientacao == Orientacao.Retrato ? 2.5F : 5F; + + var rp1 = BoundingBox.InflatedRetangle(1F, 0.5F, paddingHorizontal); + var rp2 = rp1; + + var f1 = Estilo.CriarFonteNegrito(12); + var f1h = f1.AlturaLinha; + gfx.DrawString("DANFE", rp2, f1, AlinhamentoHorizontal.Centro); + + rp2 = rp2.CutTop(f1h + 0.5F); + + var f2 = Estilo.CriarFonteRegular(8F); + var f2h = (float)f2.AlturaLinha; + + var ts = new TextStack(rp2) + { + AlinhamentoVertical = AlinhamentoVertical.Topo + } + .AddLine("Documento Auxiliar da", f2) + .AddLine("Nota Fiscal Eletrônica", f2); + + ts.Draw(gfx); + + rp2 = rp2.CutTop(2F * f2h + 1.5F); + + + ts = new TextStack(rp2) + { + AlinhamentoVertical = AlinhamentoVertical.Topo, + AlinhamentoHorizontal = AlinhamentoHorizontal.Esquerda + } + .AddLine("0 - ENTRADA", f2) + .AddLine("1 - SAÍDA", f2); + ts.Draw(gfx); + + float rectEsSize = 1.75F * f2h; + var rectEs = new RectangleF(rp2.Right - rectEsSize, rp2.Y + (2F * f2h - rectEsSize) / 2F, rectEsSize, rectEsSize); + + gfx.StrokeRectangle(rectEs, 0.25F); + + gfx.DrawString(ViewModel.TipoNF.ToString(), rectEs, Estilo.FonteNumeroFolhas, AlinhamentoHorizontal.Centro, AlinhamentoVertical.Centro); + + + var f4 = Estilo.FonteNumeroFolhas; + var f4h = Estilo.FonteNumeroFolhas.AlturaLinha; + + rp2.Height = 2F * f4h * TextStack.DefaultLineHeightScale + f2h; + rp2.Y = rp1.Bottom - rp2.Height; + + ts = new TextStack(rp2) + { + AlinhamentoVertical = AlinhamentoVertical.Topo, + AlinhamentoHorizontal = AlinhamentoHorizontal.Centro + } + .AddLine("Nº.: " + ViewModel.NfNumero.ToString(Formatador.FormatoNumeroNF), f4) + .AddLine($"Série: {ViewModel.NfSerie}", f4); + + ts.Draw(gfx); + + RetanguloNumeroFolhas = new RectangleF(rp1.Left, rp1.Bottom - Estilo.FonteNumeroFolhas.AlturaLinha, rp1.Width, Estilo.FonteNumeroFolhas.AlturaLinha); + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/Tabela.cs b/NFe.Danfe.PdfClown/Elementos/Tabela.cs new file mode 100644 index 000000000..0b8d6128d --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/Tabela.cs @@ -0,0 +1,194 @@ +using System.Drawing; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; +using NFe.Danfe.PdfClown.Tools; +using org.pdfclown.documents.contents.colorSpaces; + +namespace NFe.Danfe.PdfClown.Elementos +{ + internal class Tabela : ElementoBase + { + public List Colunas { get; private set; } + public List> Linhas { get; private set; } + public float PaddingSuperior { get; private set; } + public float PaddingInferior { get; private set; } + public float PaddingHorizontal { get; private set; } + + public int LinhaAtual { get; private set; } + public float TamanhoFonteCabecalho { get; private set; } + + private float _DY; + private float _DY1; + + public Fonte FonteCorpo { get; private set; } + public Fonte FonteCabecalho { get; private set; } + + public Tabela(Estilo estilo) : base(estilo) + { + Colunas = new List(); + Linhas = new List>(); + LinhaAtual = 0; + TamanhoFonteCabecalho = 6; + + PaddingHorizontal = 0.6F; + PaddingSuperior = 0.75F; + PaddingInferior = 0.3F; + + // 7.7.7 Conteúdo dos Campos do Quadro “Dados dos Produtos/Serviços” + // Deverá ter tamanho mínimo de seis(6) pontos, ou 17 CPP. + + FonteCorpo = estilo.CriarFonteRegular(6F); + FonteCabecalho = estilo.CriarFonteRegular(6F); + } + + public Tabela ComColuna(float larguraP, AlinhamentoHorizontal ah, params string[] cabecalho) + { + Colunas.Add(new TabelaColuna(cabecalho, larguraP, ah)); + return this; + } + + public void AdicionarLinha(List linha) + { + if (linha.Count != Colunas.Count) throw new ArgumentException(nameof(linha)); + Linhas.Add(linha); + } + + public void AjustarLarguraColunas() + { + var sw = Colunas.Sum(x => x.PorcentagemLargura); + + if (sw > 100F) throw new InvalidOperationException(); + + var w = (100F - sw) / (float)Colunas.Where(x => x.PorcentagemLargura == 0).Count(); + + foreach (var c in Colunas.Where(x => x.PorcentagemLargura == 0)) + c.PorcentagemLargura = w; + + } + + private Boolean DesenharLinha(Gfx gfx) + { + float x = X; + _DY1 = _DY; + + TextBlock[] tb = new TextBlock[Colunas.Count]; + + for (int i = 0; i < Colunas.Count; i++) + { + var c = Colunas[i]; + var v = Linhas[LinhaAtual][i]; + + float w = (Width * c.PorcentagemLargura) / 100F; + + if (!string.IsNullOrWhiteSpace(v)) + { + + tb[i] = new TextBlock(v, FonteCorpo) + { + Width = w - 2F * Estilo.PaddingHorizontal, + X = x + PaddingHorizontal, + Y = _DY + PaddingSuperior, + AlinhamentoHorizontal = c.AlinhamentoHorizontal + }; + } + + x += w; + + } + + var tbm = tb.Where(t => t != null).Max(t => t.Height); + if (tbm + _DY + PaddingInferior + PaddingSuperior > BoundingBox.Bottom) return false; + + for (int i = 0; i < Colunas.Count; i++) + { + if (tb[i] != null) + tb[i].Draw(gfx); + } + + _DY += Math.Max(tbm, FonteCorpo.AlturaLinha) + PaddingSuperior + PaddingInferior; + + return true; + } + + public void DesenharCabecalho(Gfx gfx) + { + var ml = Colunas.Max(c => c.Cabecalho.Length); + float ac = ml * FonteCabecalho.AlturaLinha + 2F; + + float x = X; + _DY = Y; + + foreach (var coluna in Colunas) + { + float w = (Width * coluna.PorcentagemLargura) / 100F; + var r = new RectangleF(x, _DY, w, ac); + + var tb = new TextStack(r.InflatedRetangle(1F)); + tb.AlinhamentoVertical = AlinhamentoVertical.Centro; + tb.AlinhamentoHorizontal = AlinhamentoHorizontal.Centro; + + foreach (var item in coluna.Cabecalho) + { + tb.AddLine(item, FonteCabecalho); + } + + tb.Draw(gfx); + + x += w; + + gfx.DrawRectangle(r); + gfx.DrawRectangle(r.X, BoundingBox.Y, r.Width, BoundingBox.Height); + } + + _DY += ac; + + gfx.Stroke(); + } + + + public override void Draw(Gfx gfx) + { + + base.Draw(gfx); + gfx.SetLineWidth(0.25F); + + DesenharCabecalho(gfx); + + + while (LinhaAtual < Linhas.Count) + { + Boolean r = DesenharLinha(gfx); + + if (r) + { + if (LinhaAtual > 0) + { + gfx.PrimitiveComposer.BeginLocalState(); + gfx.PrimitiveComposer.SetStrokeColor(new DeviceRGBColor(0.5, 0.5, 0.5)); + gfx.PrimitiveComposer.SetLineDash(new org.pdfclown.documents.contents.LineDash(new double[] { 6, 1 })); + gfx.PrimitiveComposer.DrawLine(new PointF(BoundingBox.Left, _DY1).ToPointMeasure(), new PointF(BoundingBox.Right, _DY1).ToPointMeasure()); + gfx.PrimitiveComposer.Stroke(); + gfx.PrimitiveComposer.End(); + } + + LinhaAtual++; + } + else + { + break; + } + } + + + + + + + + } + + + + public override bool PossuiContono => false; + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/TabelaColuna.cs b/NFe.Danfe.PdfClown/Elementos/TabelaColuna.cs new file mode 100644 index 000000000..cf1f82c81 --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/TabelaColuna.cs @@ -0,0 +1,23 @@ +using NFe.Danfe.PdfClown.Enumeracoes; + +namespace NFe.Danfe.PdfClown.Elementos +{ + internal class TabelaColuna + { + public string[] Cabecalho { get; private set; } + public float PorcentagemLargura { get; set; } + public AlinhamentoHorizontal AlinhamentoHorizontal { get; private set; } + + public TabelaColuna(string[] cabecalho, float porcentagemLargura, AlinhamentoHorizontal alinhamentoHorizontal = AlinhamentoHorizontal.Esquerda) + { + Cabecalho = cabecalho ?? throw new ArgumentNullException(nameof(cabecalho)); + PorcentagemLargura = porcentagemLargura; + AlinhamentoHorizontal = alinhamentoHorizontal; + } + + public override string ToString() + { + return string.Join(" ", Cabecalho); + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/TextBlock.cs b/NFe.Danfe.PdfClown/Elementos/TextBlock.cs new file mode 100644 index 000000000..72c8a0e0b --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/TextBlock.cs @@ -0,0 +1,166 @@ +using System.Drawing; +using System.Text; +using System.Text.RegularExpressions; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; + +namespace NFe.Danfe.PdfClown.Elementos +{ + /// + /// Define um bloco de texto de largura fixa e altura dinâmica. + /// + internal class TextBlock : DrawableBase + { + private string _Text; + public string Text { get => _Text; set { _Text = value; _Height = null; } } + + public List Blocks { get; private set; } + public List BlocksW { get; private set; } + + public Fonte Fonte { get; private set; } + + public List Lines { get; private set; } + private float? _Height; + + public override float Width { get => base.Width; set { base.Width = value; _Height = null; } } + public AlinhamentoHorizontal AlinhamentoHorizontal { get; set; } + + public override float Height + { + get + { + if (!_Height.HasValue) CalculateLines(); + return _Height.Value; + } + set => throw new NotSupportedException(); + } + + + + public override void Draw(Gfx gfx) + { + if (string.IsNullOrEmpty(_Text)) return; + + base.Draw(gfx); + if (!_Height.HasValue) CalculateLines(); + + float y = Y; + + foreach (var item in Lines) + { + gfx.DrawString(item, new RectangleF(X, y, Width, _Height.Value), Fonte, AlinhamentoHorizontal, AlinhamentoVertical.Topo); + y += Fonte.AlturaLinha; + } + } + + private void BreakLongText(string str) + { + char[] c = str.ToCharArray(); + float w1 = 0; + StringBuilder sb1 = new StringBuilder(); + + for (int i2 = 0; i2 < c.Length; i2++) + { + float cw = Fonte.MedirLarguraChar(c[i2]); + if (cw + w1 > Width) + { + Lines.Add(sb1.ToString()); + sb1.Clear(); + w1 = 0; + } + + w1 += cw; + sb1.Append(c[i2]); + } + + Lines.Add(sb1.ToString()); + } + + private void CalculateBlocks(string[] blocks, float[] blocksW) + { + float x = 0; + StringBuilder sb = new StringBuilder(); + + int i = 0; + + while (i < blocks.Length) + { + var w = blocks[i]; + var wl = blocksW[i]; + + if (wl > Width) + { + + if (sb.Length > 0) + { + Lines.Add(sb.ToString()); + sb.Clear(); + } + + BreakLongText(w); + i++; + x = 0; + } + else if (x + wl <= Width) + { + x += wl; + sb.Append(w); + i++; + } + else + { + if (w == " ") i++; + + x = 0; + Lines.Add(sb.ToString().TrimEnd()); + sb.Clear(); + } + } + + if (sb.Length > 0) Lines.Add(sb.ToString()); + } + + private void CalculateLines() + { + Lines.Clear(); + + for (int i = 0; i < Blocks.Count; i++) + { + CalculateBlocks(Blocks[i], BlocksW[i]); + } + + _Height = Lines.Count * Fonte.AlturaLinha; + } + + private void SplitText() + { + var lines = Text.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None); + + for (int i = 0; i < lines.Length; i++) + { + var line = lines[i].Trim(); + var blocks = Regex.Split(line, @"(\s)"); + var blocksW = new float[blocks.Length]; + + for (int i2 = 0; i2 < blocks.Length; i2++) + { + blocksW[i2] = Fonte.MedirLarguraTexto(blocks[i2]); + } + + Blocks.Add(blocks); + BlocksW.Add(blocksW); + } + } + + public TextBlock(string text, Fonte f) + { + Text = text; + Fonte = f; + Blocks = new List(); + BlocksW = new List(); + SplitText(); + AlinhamentoHorizontal = AlinhamentoHorizontal.Esquerda; + Lines = new List(); + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/TextStack.cs b/NFe.Danfe.PdfClown/Elementos/TextStack.cs new file mode 100644 index 000000000..f87a9751d --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/TextStack.cs @@ -0,0 +1,84 @@ +using System.Drawing; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; + +namespace NFe.Danfe.PdfClown.Elementos +{ + /// + /// Define uma pilha de texto. + /// + internal class TextStack : DrawableBase + { + public const float DefaultLineHeightScale = 1.25F; + + private List _Lines; + private List _Fonts; + public AlinhamentoHorizontal AlinhamentoHorizontal { get; set; } + public AlinhamentoVertical AlinhamentoVertical { get; set; } + public float LineHeightScale { get; set; } + + public TextStack(RectangleF boundingBox) + { + SetPosition(boundingBox.Location); + SetSize(boundingBox.Size); + _Lines = new List(); + _Fonts = new List(); + AlinhamentoHorizontal = AlinhamentoHorizontal.Centro; + AlinhamentoVertical = AlinhamentoVertical.Centro; + LineHeightScale = DefaultLineHeightScale; + } + + public TextStack AddLine(string text, Fonte font) + { + _Lines.Add(text); + _Fonts.Add(font); + return this; + } + + public override void Draw(Gfx gfx) + { + var fonts = new Fonte[_Fonts.Count]; + + //adjust font size to prevent horizontal overflown + for (int i = 0; i < _Lines.Count; i++) + { + var w = _Fonts[i].MedirLarguraTexto(_Lines[i]); + + if (w > BoundingBox.Width) + { + fonts[i] = new Fonte(_Fonts[i].FonteInterna, BoundingBox.Width * _Fonts[i].Tamanho / w); + } + else + { + fonts[i] = _Fonts[i]; + } + } + + float totalH = (float)fonts.Last().AlturaLinha; + + for (int i = 0; i < _Lines.Count - 1; i++) + { + totalH += (float)fonts[i].AlturaLinha * LineHeightScale; + } + + // float totalH = (float)fonts.Sum(x => x.AlturaEmMm()); + var h2 = (BoundingBox.Height - totalH) / 2D; + var r = BoundingBox; + + if (AlinhamentoVertical == AlinhamentoVertical.Centro) + r.Y += (float)h2; + else if (AlinhamentoVertical == AlinhamentoVertical.Base) + r.Y = r.Bottom - totalH; + + for (int i = 0; i < _Lines.Count; i++) + { + var l = _Lines[i]; + var f = fonts[i]; + + gfx.DrawString(l, r, f, AlinhamentoHorizontal); + r.Y += f.AlturaLinha * LineHeightScale; + } + + } + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/TextoSimples.cs b/NFe.Danfe.PdfClown/Elementos/TextoSimples.cs new file mode 100644 index 000000000..7ad3571bb --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/TextoSimples.cs @@ -0,0 +1,48 @@ +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Graphics; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Elementos +{ + class TextoSimples : ElementoBase + { + public string Texto { get; private set; } + public AlinhamentoHorizontal AlinhamentoHorizontal { get; set; } + public AlinhamentoVertical AlinhamentoVertical { get; set; } + public float TamanhoFonte { get; set; } + + public TextoSimples(Estilo estilo, string texto) : base(estilo) + { + Texto = texto; + AlinhamentoHorizontal = AlinhamentoHorizontal.Esquerda; + AlinhamentoVertical = AlinhamentoVertical.Topo; + TamanhoFonte = 6; + + } + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + + if (!string.IsNullOrWhiteSpace(Texto)) + { + var r = BoundingBox.InflatedRetangle(0.75F); + + var tb = new TextBlock(Texto, Estilo.CriarFonteRegular(TamanhoFonte)); + tb.AlinhamentoHorizontal = AlinhamentoHorizontal; + tb.Width = r.Width; + + var y = r.Y; + + if (AlinhamentoVertical == AlinhamentoVertical.Centro) + y += (r.Height - tb.Height) / 2F; + + tb.SetPosition(r.X, y); + tb.Draw(gfx); + } + + } + + + } +} diff --git a/NFe.Danfe.PdfClown/Elementos/VerticalStack.cs b/NFe.Danfe.PdfClown/Elementos/VerticalStack.cs new file mode 100644 index 000000000..db513ee6e --- /dev/null +++ b/NFe.Danfe.PdfClown/Elementos/VerticalStack.cs @@ -0,0 +1,62 @@ +using NFe.Danfe.PdfClown.Atributos; +using NFe.Danfe.PdfClown.Graphics; + +namespace NFe.Danfe.PdfClown.Elementos +{ + /// + /// Define uma pilha vertical de elementos, de forma que todos eles fiquem com a mesma largura. + /// + [AlturaFixa] + internal class VerticalStack : DrawableBase + { + public List Drawables { get; private set; } + + public VerticalStack() + { + Drawables = new List(); + } + + public VerticalStack(float width) : this() + { + Width = width; + } + + public void Add(params DrawableBase[] db) + { + foreach (var item in db) + { + if (item == this) throw new InvalidOperationException(); + + Drawables.Add(item); + } + } + + public override void Draw(Gfx gfx) + { + base.Draw(gfx); + + float x = X, y = Y; + + for (int i = 0; i < Drawables.Count; i++) + { + var db = Drawables[i]; + db.Width = Width; + db.SetPosition(x, y); + db.Draw(gfx); + y += db.Height; + } + } + + /// + /// Soma das alturas de todos os elementos. + /// + public override float Height { get => Drawables.Sum(x => x.Height); set => throw new NotSupportedException(); } + + /// + /// Somente é possível mudar a largura. + /// + public override void SetSize(float w, float h) => throw new NotSupportedException(); + + + } +} diff --git a/NFe.Danfe.PdfClown/Enumeracoes/AlinhamentoHorizontal.cs b/NFe.Danfe.PdfClown/Enumeracoes/AlinhamentoHorizontal.cs new file mode 100644 index 000000000..c02780318 --- /dev/null +++ b/NFe.Danfe.PdfClown/Enumeracoes/AlinhamentoHorizontal.cs @@ -0,0 +1,9 @@ +namespace NFe.Danfe.PdfClown.Enumeracoes +{ + internal enum AlinhamentoHorizontal + { + Esquerda, + Centro, + Direita + } +} diff --git a/NFe.Danfe.PdfClown/Enumeracoes/AlinhamentoVertical.cs b/NFe.Danfe.PdfClown/Enumeracoes/AlinhamentoVertical.cs new file mode 100644 index 000000000..4db7979f0 --- /dev/null +++ b/NFe.Danfe.PdfClown/Enumeracoes/AlinhamentoVertical.cs @@ -0,0 +1,9 @@ +namespace NFe.Danfe.PdfClown.Enumeracoes +{ + internal enum AlinhamentoVertical + { + Topo, + Centro, + Base + } +} diff --git a/NFe.Danfe.PdfClown/Enumeracoes/Orientacao.cs b/NFe.Danfe.PdfClown/Enumeracoes/Orientacao.cs new file mode 100644 index 000000000..4a9770952 --- /dev/null +++ b/NFe.Danfe.PdfClown/Enumeracoes/Orientacao.cs @@ -0,0 +1,8 @@ +namespace NFe.Danfe.PdfClown.Enumeracoes +{ + public enum Orientacao + { + Retrato, + Paisagem + } +} diff --git a/NFe.Danfe.PdfClown/Enumeracoes/PosicaoBloco.cs b/NFe.Danfe.PdfClown/Enumeracoes/PosicaoBloco.cs new file mode 100644 index 000000000..eb243fcc1 --- /dev/null +++ b/NFe.Danfe.PdfClown/Enumeracoes/PosicaoBloco.cs @@ -0,0 +1,8 @@ +namespace NFe.Danfe.PdfClown.Enumeracoes +{ + internal enum PosicaoBloco + { + Topo, + Base + } +} diff --git a/NFe.Danfe.PdfClown/Esquemas/NFReferenciada.cs b/NFe.Danfe.PdfClown/Esquemas/NFReferenciada.cs new file mode 100644 index 000000000..56eed7e40 --- /dev/null +++ b/NFe.Danfe.PdfClown/Esquemas/NFReferenciada.cs @@ -0,0 +1,93 @@ +using System.Xml.Serialization; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Esquemas +{ + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public class NFReferenciada + { + [XmlElement("refCTe", typeof(string))] + [XmlElement("refECF", typeof(RefECF))] + [XmlElement("refNF", typeof(RefNF))] + [XmlElement("refNFP", typeof(RefNFP))] + [XmlElement("refNFe", typeof(string))] + [XmlChoiceIdentifier("TipoNFReferenciada")] + public object Item; + + [XmlIgnore] + public TipoNFReferenciada TipoNFReferenciada { get; set; } + + public override string ToString() + { + if (TipoNFReferenciada == TipoNFReferenciada.refCTe || TipoNFReferenciada == TipoNFReferenciada.refNFe) + { + string chaveAcesso = Item.ToString(); + return $"{Utils.TipoDFeDeChaveAcesso(chaveAcesso)} Ref.: {Formatador.FormatarChaveAcesso(Item.ToString())}"; + } + else + return Item.ToString(); + } + + } + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public enum TipoNFReferenciada + { + refCTe, + refECF, + refNF, + refNFP, + refNFe, + } + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public class RefECF + { + public string mod { get; set; } + public string nECF { get; set; } + public string nCOO { get; set; } + + public override string ToString() + { + return $"ECF Ref.: Modelo: {mod} ECF: {nECF} COO: {nCOO}"; + } + } + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public class RefNF + { + public string AAMM { get; set; } + public string CNPJ { get; set; } + public string mod { get; set; } + public string serie { get; set; } + public string nNF { get; set; } + + public override string ToString() + { + return $"NF Ref.: Série: {serie} Número: {nNF} Emitente: {Formatador.FormatarCnpj(CNPJ)} Modelo: {mod}"; + } + } + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public class RefNFP + { + public string AAMM { get; set; } + public string CNPJ { get; set; } + public string CPF { get; set; } + public string IE { get; set; } + public string mod { get; set; } + public string serie { get; set; } + public string nNF { get; set; } + + public override string ToString() + { + string cpfCnpj = !string.IsNullOrWhiteSpace(CNPJ) ? CNPJ : CPF; + return $"NFP Ref.: Série: {serie} Número: {nNF} Emitente: {Formatador.FormatarCpfCnpj(cpfCnpj)} Modelo: {mod} IE: {IE}"; + } + } +} diff --git a/NFe.Danfe.PdfClown/Esquemas/Namespaces.cs b/NFe.Danfe.PdfClown/Esquemas/Namespaces.cs new file mode 100644 index 000000000..b2609616d --- /dev/null +++ b/NFe.Danfe.PdfClown/Esquemas/Namespaces.cs @@ -0,0 +1,7 @@ +namespace NFe.Danfe.PdfClown.Esquemas +{ + public static class Namespaces + { + public const string NFe = "http://www.portalfiscal.inf.br/nfe"; + } +} diff --git a/NFe.Danfe.PdfClown/Esquemas/ProcNFe.cs b/NFe.Danfe.PdfClown/Esquemas/ProcNFe.cs new file mode 100644 index 000000000..5621cfeb0 --- /dev/null +++ b/NFe.Danfe.PdfClown/Esquemas/ProcNFe.cs @@ -0,0 +1,850 @@ +using System.Xml.Serialization; +using NFe.Danfe.PdfClown.Structs; + +namespace NFe.Danfe.PdfClown.Esquemas +{ + /// + /// NF-e processada + /// + [XmlType(Namespace = Namespaces.NFe)] + [XmlRoot("nfeProc", Namespace = Namespaces.NFe, IsNullable = false)] + public class ProcNFe + { + public NFe NFe { get; set; } + + public ProtNFe protNFe { get; set; } + + [XmlAttribute] + public string versao { get; set; } + } + + + /// + /// Identificação do Ambiente + /// + [Serializable] + [XmlType(Namespace = Namespaces.NFe)] + public enum TAmb + { + [XmlEnum("1")] + Producao = 1, + + [XmlEnum("2")] + Homologacao = 2, + } + + /// + /// Dados do protocolo de status + /// + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public class InfProt + { + + /// + /// Identificação do Ambiente + /// + public TAmb tpAmb { get; set; } + + public string verAplic { get; set; } + public string chNFe { get; set; } + + public DateTimeOffsetIso8601 dhRecbto { get; set; } + + public string nProt { get; set; } + public int cStat { get; set; } + public string xMotivo { get; set; } + + [XmlAttribute(DataType = "ID")] + public string Id { get; set; } + } + + + /// + /// Tipo Protocolo de status resultado do processamento da NF-e< + /// + [Serializable] + [XmlType(Namespace = Namespaces.NFe)] + public partial class ProtNFe + { + public InfProt infProt { get; set; } + + [XmlAttribute] + public string versao { get; set; } + } + + + + [Serializable] + [XmlType(Namespace = Namespaces.NFe)] + public class NFe + { + public InfNFe infNFe { get; set; } + } + + + [Serializable] + [XmlType(Namespace = Namespaces.NFe)] + public class Endereco + { + /// + /// Logradouro + /// + public string xLgr { get; set; } + + /// + /// Número + /// + public string nro { get; set; } + + /// + /// Complemento + /// + public string xCpl { get; set; } + + /// + /// Bairro + /// + public string xBairro { get; set; } + + /// + /// Código do município + /// + public string cMun { get; set; } + + /// + /// Nome do município + /// + public string xMun { get; set; } + + /// + /// Sigla da UF + /// + public string UF { get; set; } + + /// + /// Código do CEP + /// + public string CEP { get; set; } + + /// + /// Telefone + /// + public string fone { get; set; } + } + + /// + /// Nota Técnica 2018.005 + /// + [Serializable] + [XmlType(Namespace = Namespaces.NFe)] + public class LocalEntregaRetirada : Endereco + { + public string CNPJ { get; set; } + public string CPF { get; set; } + + /// + /// Razão Social ou Nome do Expedidor/Recebedor + /// + public string xNome { get; set; } + + /// + /// Inscrição Estadual do Estabelecimento Expedidor/Recebedor + /// + public string IE { get; set; } + } + + + public class Empresa + { + public string CNPJ { get; set; } + public string CPF { get; set; } + public string xNome { get; set; } + public string IE { get; set; } + public string IEST { get; set; } + public string email { get; set; } + + [XmlIgnore] + public virtual Endereco Endereco { get; set; } + } + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class Destinatario : Empresa + { + public string ISUF { get; set; } + //public string email; + + [XmlElement("enderDest")] + public override Endereco Endereco { get; set; } + } + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class Emitente : Empresa + { + public string xFant { get; set; } + public string IM { get; set; } + public string CNAE { get; set; } + + [XmlElement("enderEmit")] + public override Endereco Endereco { get; set; } + + /// + /// Código de Regime Tributário + /// + public string CRT { get; set; } + } + + + /// + /// Dados dos produtos e serviços da NF-e + /// + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class Produto + { + public string cProd { get; set; } + public string cEAN { get; set; } + public string xProd { get; set; } + public string NCM { get; set; } + public string EXTIPI { get; set; } + public int CFOP { get; set; } + public string uCom { get; set; } + public double qCom { get; set; } + public double vUnCom { get; set; } + public double vProd { get; set; } + public string cEANTrib { get; set; } + public string uTrib { get; set; } + public string qTrib { get; set; } + public string vUnTrib { get; set; } + public string vFrete { get; set; } + public string vSeg { get; set; } + public string vDesc { get; set; } + public string vOutro { get; set; } + public string xPed { get; set; } + public string nItemPed { get; set; } + public string nFCI { get; set; } + } + + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public class ImpostoICMS + { + public string orig { get; set; } + public string CST { get; set; } + public string CSOSN { get; set; } + public double vBC { get; set; } + public double pICMS { get; set; } + public double vICMS { get; set; } + } + + public class ImpostoICMS00 : ImpostoICMS { } + public class ImpostoICMS02 : ImpostoICMS { } + public class ImpostoICMS10 : ImpostoICMS { } + public class ImpostoICMS15 : ImpostoICMS { } + public class ImpostoICMS20 : ImpostoICMS { } + public class ImpostoICMS30 : ImpostoICMS { } + public class ImpostoICMS40 : ImpostoICMS { } + public class ImpostoICMS51 : ImpostoICMS { } + public class ImpostoICMS53 : ImpostoICMS { } + public class ImpostoICMS60 : ImpostoICMS { } + public class ImpostoICMS61 : ImpostoICMS { } + public class ImpostoICMS70 : ImpostoICMS { } + public class ImpostoICMS90 : ImpostoICMS { } + public class ImpostoICMSPart : ImpostoICMS { } + public class ImpostoICMSSN101 : ImpostoICMS { } + public class ImpostoICMSSN102 : ImpostoICMS { } + public class ImpostoICMSSN201 : ImpostoICMS { } + public class ImpostoICMSSN202 : ImpostoICMS { } + public class ImpostoICMSSN500 : ImpostoICMS { } + public class ImpostoICMSSN900 : ImpostoICMS { } + public class ImpostoICMSST : ImpostoICMS { } + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class ProdutoICMS + { + + [XmlElement("ICMS00", typeof(ImpostoICMS00))] + [XmlElement("ICMS02", typeof(ImpostoICMS02))] + [XmlElement("ICMS10", typeof(ImpostoICMS10))] + [XmlElement("ICMS15", typeof(ImpostoICMS15))] + [XmlElement("ICMS20", typeof(ImpostoICMS20))] + [XmlElement("ICMS30", typeof(ImpostoICMS30))] + [XmlElement("ICMS40", typeof(ImpostoICMS40))] + [XmlElement("ICMS51", typeof(ImpostoICMS51))] + [XmlElement("ICMS53", typeof(ImpostoICMS53))] + [XmlElement("ICMS60", typeof(ImpostoICMS60))] + [XmlElement("ICMS61", typeof(ImpostoICMS61))] + [XmlElement("ICMS70", typeof(ImpostoICMS70))] + [XmlElement("ICMS90", typeof(ImpostoICMS90))] + [XmlElement("ICMSPart", typeof(ImpostoICMSPart))] + [XmlElement("ICMSSN101", typeof(ImpostoICMSSN101))] + [XmlElement("ICMSSN102", typeof(ImpostoICMSSN102))] + [XmlElement("ICMSSN201", typeof(ImpostoICMSSN201))] + [XmlElement("ICMSSN202", typeof(ImpostoICMSSN202))] + [XmlElement("ICMSSN500", typeof(ImpostoICMSSN500))] + [XmlElement("ICMSSN900", typeof(ImpostoICMSSN900))] + [XmlElement("ICMSST", typeof(ImpostoICMSST))] + public ImpostoICMS ICMS; + } + + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class ProdutoIPI + { + public string clEnq { get; set; } + public string CNPJProd { get; set; } + public string cSelo { get; set; } + public string qSelo { get; set; } + public string cEnq { get; set; } + public IPITrib IPITrib { get; set; } + } + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class IPITrib + { + public string CST { get; set; } + public double? pIPI { get; set; } + public double? qUnid { get; set; } + public double? vBC { get; set; } + public double? vUnid { get; set; } + public double? vIPI { get; set; } + } + + /// + /// Tributos incidentes nos produtos ou serviços da NF-e + /// + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class ProdutoImposto + { + public string vTotTrib { get; set; } + public ProdutoICMS ICMS { get; set; } + public ProdutoIPI IPI { get; set; } + } + + /// + /// Dados dos detalhes da NF-e + /// + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public class Detalhe + { + public Produto prod { get; set; } + public ProdutoImposto imposto { get; set; } + public string infAdProd { get; set; } + + [XmlAttribute] + public string nItem { get; set; } + } + + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class Duplicata + { + public string nDup { get; set; } + public DateTime? dVenc { get; set; } + public double? vDup { get; set; } + } + + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class Fatura + { + public string nFat { get; set; } + public string vOrig { get; set; } + public string vDesc { get; set; } + public string vLiq { get; set; } + } + + + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class Cobranca + { + public Fatura fat { get; set; } + + [XmlElement("dup")] + public List dup { get; set; } + + public Cobranca() + { + dup = new List(); + } + } + + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class ObsCont + { + public string xTexto { get; set; } + + [XmlAttribute] + public string xCampo { get; set; } + } + + + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class ObsFisco + { + public string xTexto { get; set; } + + [XmlAttribute] + public string xCampo { get; set; } + } + + + /// + /// Informações adicionais da NF-e + /// + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class InfAdic + { + public string infAdFisco { get; set; } + public string infCpl { get; set; } + + [XmlElement("obsCont")] + public List obsCont { get; set; } + + [XmlElement("obsFisco")] + public List obsFisco { get; set; } + + public InfAdic() + { + obsCont = new List(); + obsFisco = new List(); + } + + } + + + /// + /// Grupo de Valores Totais referentes ao ICMS + /// + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class ICMSTotal + { + /// + /// Base de Cálculo do ICMS + /// + public double vBC { get; set; } + + /// + /// Valor Total do ICMS + /// + public double vICMS { get; set; } + + /// + /// Valor total do ICMS Interestadual para a UF de destino + /// + public double? vICMSUFDest { get; set; } + + /// + /// Valor total do ICMS Interestadual para a UF do remetente + /// + public double? vICMSUFRemet { get; set; } + + /// + /// Valor total do ICMS relativo Fundo de Combate à Pobreza(FCP) da UF de destino + /// + public double? vFCPUFDest { get; set; } + + /// + /// Base de Cálculo do ICMS ST + /// + public double vBCST { get; set; } + + /// + /// Valor Total do ICMS ST + /// + public double vST { get; set; } + + /// + /// Valor Total dos produtos e serviços + /// + public double vProd { get; set; } + + /// + /// Valor Total do Frete + /// + public double vFrete { get; set; } + + /// + /// Valor Total do Seguro + /// + public double vSeg { get; set; } + + /// + /// Valor Total do Desconto + /// + public double vDesc { get; set; } + + /// + /// Valor Total do II + /// + public double vII { get; set; } + + /// + /// Valor Total do IPI + /// + public double vIPI { get; set; } + + /// + /// Valor do PIS + /// + public double vPIS { get; set; } + + /// + /// Valor do COFINS + /// + public double vCOFINS { get; set; } + + /// + /// Outras Despesas acessórias + /// + public double vOutro { get; set; } + + /// + /// Valor Total da NF-e + /// + public double vNF { get; set; } + + + public double? vTotTrib { get; set; } + } + + /// + /// Totais referentes ao ISSQN + /// + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class ISSQNTotal + { + public double? vServ { get; set; } + public double? vBC { get; set; } + public double? vISS { get; set; } + public double? vPIS { get; set; } + public double? vCOFINS { get; set; } + } + + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class Total + { + public ICMSTotal ICMSTot { get; set; } + public ISSQNTotal ISSQNtot { get; set; } + } + + + /// + /// Modalidade do frete + /// + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public enum ModalidadeFrete + { + + [XmlEnum("0")] + PorContaRemetente = 0, + + [XmlEnum("1")] + PorContaDestinatario = 1, + + [XmlEnum("2")] + PorContaTerceiros = 2, + + [XmlEnum("3")] + ProprioContaRemetente = 3, + + [XmlEnum("4")] + ProprioContaDestinatario = 4, + + [XmlEnum("9")] + SemTransporte = 9, + } + + + /// + /// Dados do transportador + /// + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class Transportador + { + public string CNPJ { get; set; } + public string CPF { get; set; } + public string xNome { get; set; } + public string IE { get; set; } + public string xEnder { get; set; } + public string xMun { get; set; } + public string UF { get; set; } + } + + + [Serializable] + [XmlType(Namespace = Namespaces.NFe)] + public partial class Veiculo + { + public string placa { get; set; } + public string UF { get; set; } + public string RNTC { get; set; } + } + + + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class Volume + { + public double? qVol { get; set; } + public string esp { get; set; } + public string marca { get; set; } + public string nVol { get; set; } + public double? pesoL { get; set; } + public double? pesoB { get; set; } + } + + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class Transporte + { + public ModalidadeFrete modFrete { get; set; } + public Transportador transporta { get; set; } + + public string balsa { get; set; } + public string vagao { get; set; } + + public Veiculo reboque { get; set; } + public Veiculo veicTransp { get; set; } + + [XmlElement("vol")] + public List vol { get; set; } + + public Transporte() + { + vol = new List(); + } + } + + + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class InfNFe + { + public Identificacao ide { get; set; } + public Emitente emit { get; set; } + public Destinatario dest { get; set; } + + /// + /// Identificação do Local de retirada + /// + public LocalEntregaRetirada retirada { get; set; } + + /// + /// Identificação do Local de entrega + /// + public LocalEntregaRetirada entrega { get; set; } + + + [XmlElement("det")] + public List det { get; set; } + + public Total total { get; set; } + public Transporte transp { get; set; } + public Cobranca cobr { get; set; } + public InfAdic infAdic { get; set; } + [XmlAttribute] + public string versao { get; set; } + + /// + /// Informação adicional de compra + /// + public InfCompra compra { get; set; } + + [XmlAttribute(DataType = "ID")] + public string Id { get; set; } + + public InfNFe() + { + det = new List(); + } + + [XmlIgnore] + public Versao Versao + { + get + { + return Versao.Parse(versao); + } + } + } + + /// + /// Informações de Compras + /// + [XmlType(AnonymousType = true, Namespace = "http://www.portalfiscal.inf.br/nfe")] + public partial class InfCompra + { + + /// + /// Nota de Empenho + /// + public string xNEmp { get; set; } + + /// + /// Pedido + /// + public string xPed { get; set; } + + /// + /// Contrato + /// + public string xCont { get; set; } + } + + /// + /// Forma de emissão da NF-e + /// + [Serializable] + [XmlType(AnonymousType = true, Namespace = "http://www.portalfiscal.inf.br/nfe")] + public enum FormaEmissao + { + + [XmlEnum("1")] + Normal = 1, + + [XmlEnum("2")] + ContingenciaFS = 2, + + [XmlEnum("3")] + ContingenciaSCAN = 3, + + [XmlEnum("4")] + ContingenciaDPEC = 4, + + [XmlEnum("5")] + ContingenciaFSDA = 5, + + [XmlEnum("6")] + ContingenciaSVCAN = 6, + + [XmlEnum("7")] + ContingenciaSVCRS = 7, + + [XmlEnum("9")] + ContingenciaOffLineNFCe = 9, + } + + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public partial class Identificacao + { + + public string natOp { get; set; } + + /// + /// Código do modelo do Documento Fiscal. 55 = NF-e; 65 = NFC-e. + /// + public int mod { get; set; } + + public short serie { get; set; } + public int nNF { get; set; } + public DateTime? dEmi { get; set; } + + + #region DataHora Emissão e Saída v2- + + /// + /// Data de Saída/Entrada, NFe2 + /// + public DateTime? dSaiEnt { get; set; } + + /// + /// Hora de Saída/Entrada, NFe2 + /// + public string hSaiEnt { get; set; } + + #endregion + + #region DataHora Emissão e Saída v3+ + + /// + /// Data e Hora de Emissão, NFe v3 + /// + public DateTimeOffsetIso8601? dhEmi { get; set; } + + /// + /// Data e Hora de Saída/Entrada, NFe v3 + /// + public DateTimeOffsetIso8601? dhSaiEnt { get; set; } + + #endregion + + public Tipo tpNF { get; set; } + + /// + /// Tipo de Impressao + /// + public int tpImp { get; set; } + + /// + /// Forma de emissão da NF-e + /// + public FormaEmissao tpEmis { get; set; } + + public TAmb tpAmb { get; set; } + + /// + /// Data e Hora da entrada em contingência + /// + public DateTimeOffsetIso8601? dhCont { get; set; } + + /// + /// Justificativa da entrada em contingência + /// + public string xJust { get; set; } + + /// + /// Grupo de informação das NF/NF-e referenciadas + /// + [XmlElement("NFref")] + public List NFref { get; set; } + + public Identificacao() + { + NFref = new List(); + } + } + + + + /// + /// Tipo do Documento Fiscal + /// + [Serializable] + [XmlType(AnonymousType = true, Namespace = Namespaces.NFe)] + public enum Tipo + { + + [XmlEnum("0")] + Entrada = 0, + [XmlEnum("1")] + Saida = 1, + } +} diff --git a/NFe.Danfe.PdfClown/Esquemas/Versao.cs b/NFe.Danfe.PdfClown/Esquemas/Versao.cs new file mode 100644 index 000000000..caf89c420 --- /dev/null +++ b/NFe.Danfe.PdfClown/Esquemas/Versao.cs @@ -0,0 +1,40 @@ +using System.Diagnostics; +using System.Text.RegularExpressions; + +namespace NFe.Danfe.PdfClown.Esquemas +{ + public class Versao + { + private const string _Pattern = @"(\d+)\.(\d+)"; + public int Maior { get; protected set; } + public int Menor { get; protected set; } + + public Versao(int maior, int menor) + { + Maior = maior; + Menor = menor; + } + + [DebuggerStepThrough] + public static Versao Parse(string str) + { + if (string.IsNullOrWhiteSpace(str)) + { + throw new ArgumentException("O parâmetro str não pode ser nulo ou vazio.", "str"); + } + + Match m = Regex.Match(str, _Pattern); + Versao v = new Versao(0, 0); + + if (!m.Success) + { + throw new ArgumentException("A versão não pode ser interpretada.", "str"); + } + + v.Maior = int.Parse(m.Groups[1].Value); + v.Menor = int.Parse(m.Groups[2].Value); + + return v; + } + } +} diff --git a/NFe.Danfe.PdfClown/Graphics/Fonte.cs b/NFe.Danfe.PdfClown/Graphics/Fonte.cs new file mode 100644 index 000000000..03589361c --- /dev/null +++ b/NFe.Danfe.PdfClown/Graphics/Fonte.cs @@ -0,0 +1,63 @@ +using NFe.Danfe.PdfClown.Tools; +using pcf = org.pdfclown.documents.contents.fonts; + +namespace NFe.Danfe.PdfClown.Graphics +{ + /// + /// Define uma fonte do PDF Clown e um tamanho. + /// + internal class Fonte + { + private float _Tamanho; + + /// + /// Fonte do PDF Clown. + /// + public pcf.Font FonteInterna { get; private set; } + + public Fonte(pcf.Font font, float tamanho) + { + FonteInterna = font ?? throw new ArgumentNullException(nameof(font)); + Tamanho = tamanho; + } + + /// + /// Tamanho da fonte. + /// + public float Tamanho + { + get => _Tamanho; + set + { + if (value <= 0) throw new InvalidOperationException("O tamanho deve ser maior que zero."); + _Tamanho = value; + } + } + + /// + /// Mede a largura ocupada por uma string. + /// + /// String + /// Largura em mm. + public float MedirLarguraTexto(string str) + { + if (string.IsNullOrEmpty(str)) return 0; + return (float)FonteInterna.GetWidth(str, Tamanho).ToMm(); + } + + /// + /// Mese a largura ocupada por um Char. + /// + /// Char + /// Largura em mm. + public float MedirLarguraChar(char c) => (float)FonteInterna.GetWidth(c, Tamanho).ToMm(); + + /// + /// Medida da altura da linha. + /// + public float AlturaLinha => (float)FonteInterna.GetLineHeight(Tamanho).ToMm(); + + public Fonte Clonar() => new Fonte(FonteInterna, Tamanho); + + } +} diff --git a/NFe.Danfe.PdfClown/Graphics/Gfx.cs b/NFe.Danfe.PdfClown/Graphics/Gfx.cs new file mode 100644 index 000000000..78fc8cf2d --- /dev/null +++ b/NFe.Danfe.PdfClown/Graphics/Gfx.cs @@ -0,0 +1,132 @@ +using System.Drawing; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Tools; +using org.pdfclown.documents.contents.composition; +using org.pdfclown.documents.contents.xObjects; + +namespace NFe.Danfe.PdfClown.Graphics +{ + internal class Gfx + { + public PrimitiveComposer PrimitiveComposer { get; private set; } + + public Gfx(PrimitiveComposer primitiveComposer) + { + PrimitiveComposer = primitiveComposer ?? throw new ArgumentNullException(nameof(primitiveComposer)); + } + + internal void DrawString(string str, RectangleF rect, Fonte fonte, AlinhamentoHorizontal ah = AlinhamentoHorizontal.Esquerda, AlinhamentoVertical av = AlinhamentoVertical.Topo) + { + if (fonte == null) throw new ArgumentNullException(nameof(fonte)); + if (fonte.Tamanho <= 0) throw new ArgumentOutOfRangeException(nameof(fonte)); + CheckRectangle(rect); + + var p = rect.Location; + + if (av == AlinhamentoVertical.Base) + p.Y = rect.Bottom - fonte.AlturaLinha; + else if (av == AlinhamentoVertical.Centro) + p.Y += (rect.Height - fonte.AlturaLinha) / 2F; + + if (ah == AlinhamentoHorizontal.Direita) + p.X = rect.Right - fonte.MedirLarguraTexto(str); + if (ah == AlinhamentoHorizontal.Centro) + p.X += (rect.Width - fonte.MedirLarguraTexto(str)) / 2F; + + SetFont(fonte); + ShowText(str, p); + } + + public void SetFont(Fonte fonte) + { + if (fonte == null) throw new ArgumentNullException(nameof(fonte)); + if (fonte.FonteInterna == null) throw new ArgumentNullException(nameof(fonte)); + if (fonte.Tamanho <= 0) throw new ArgumentNullException(nameof(fonte)); + PrimitiveComposer.SetFont(fonte.FonteInterna, fonte.Tamanho); + } + + public void ShowText(string text, PointF point) + { + CheckPoint(point); + PrimitiveComposer.ShowText(text, point.ToPointMeasure()); + } + + public void ShowXObject(XObject xobj, RectangleF r) + { + if (xobj == null) throw new ArgumentNullException(nameof(xobj)); + CheckRectangle(r); + + PointF p = new PointF(); + SizeF s = new SizeF(); + SizeF xs = xobj.Size.ToMm(); + + if (r.Height >= r.Width) + { + if (xs.Height >= xs.Width) + { + s.Height = r.Height; + s.Width = (s.Height * xs.Width) / xs.Height; + } + else + { + s.Width = r.Width; + s.Height = (s.Width * xs.Height) / xs.Width; + } + } + else + { + if (xs.Height >= xs.Width) + { + s.Width = r.Width; + s.Height = (s.Width * xs.Height) / xs.Width; + } + else + { + s.Height = r.Height; + s.Width = (s.Height * xs.Width) / xs.Height; + } + } + + p.X = r.X + Math.Abs(r.Width - s.Width) / 2F; + p.Y = r.Y + Math.Abs(r.Height - s.Height) / 2F; + + PrimitiveComposer.ShowXObject(xobj, p.ToPointMeasure(), s.ToPointMeasure()); + } + + public void StrokeRectangle(RectangleF rect, float width) + { + SetLineWidth(width); + DrawRectangle(rect); + Stroke(); + } + + public void SetLineWidth(float w) + { + if (w < 0) throw new ArgumentOutOfRangeException(nameof(w)); + PrimitiveComposer.SetLineWidth(w); + } + + public void DrawRectangle(RectangleF rect) + { + CheckRectangle(rect); + PrimitiveComposer.DrawRectangle(rect.ToPointMeasure()); + } + + private void CheckRectangle(RectangleF r) + { + if (r.X < 0 || r.Y < 0 || r.Width <= 0 || r.Height <= 0) throw new ArgumentException(nameof(r)); + } + + private void CheckPoint(PointF p) + { + if (p.X < 0 || p.Y < 0) throw new ArgumentException(nameof(p)); + } + + public void Stroke() => PrimitiveComposer.Stroke(); + public void Flush() => PrimitiveComposer.Flush(); + public void Fill() => PrimitiveComposer.Fill(); + public void DrawRectangle(float x, float y, float w, float h) => DrawRectangle(new RectangleF(x, y, w, h)); + + + } +} diff --git a/NFe.Danfe.PdfClown/Modelo/CalculoImpostoViewModel.cs b/NFe.Danfe.PdfClown/Modelo/CalculoImpostoViewModel.cs new file mode 100644 index 000000000..1dcf698a0 --- /dev/null +++ b/NFe.Danfe.PdfClown/Modelo/CalculoImpostoViewModel.cs @@ -0,0 +1,108 @@ +namespace NFe.Danfe.PdfClown.Modelo +{ + public class CalculoImpostoViewModel + { + /// + /// Base de Cálculo do ICMS + /// Tag vBC + /// + public double BaseCalculoIcms { get; set; } + + /// + /// Valor Total do ICMS + /// Tag vICMS + /// + public double ValorIcms { get; set; } + + /// + /// Tag vICMSUFDest + /// + public double? vICMSUFDest { get; set; } + + /// + /// Tag vICMSUFRemet + /// + public double? vICMSUFRemet { get; set; } + + /// + /// Tag vFCPUFDest + /// + public double? vFCPUFDest { get; set; } + + /// + /// Base de Cálculo do ICMS ST + /// Tag vBCST + /// + public double BaseCalculoIcmsSt { get; set; } + + /// + /// Valor Total do ICMS ST + /// Tag vST + /// + public double ValorIcmsSt { get; set; } + + /// + /// Valor Total dos produtos e serviços + /// Tag vProd + /// + public double? ValorTotalProdutos { get; set; } + + /// + /// Valor Total do Frete + /// Tag vFrete + /// + public double ValorFrete { get; set; } + + /// + /// Valor Total do Seguro + /// Tag vSeg + /// + public double ValorSeguro { get; set; } + + /// + /// Valor Total do Desconto + /// Tag vDesc + /// + public double Desconto { get; set; } + + /// + /// Outras Despesas acessórias + /// Tag vOutro + /// + public double OutrasDespesas { get; set; } + + /// + /// Valor do imposto de importação. + /// + public double ValorII { get; set; } + + /// + /// Valor Total do IPI + /// Tag vIPI + /// + public double ValorIpi { get; set; } + + /// + /// Valor do PIS + /// + public double ValorPis { get; set; } + + /// + /// Valor do COFINS + /// + public double ValorCofins { get; set; } + + /// + /// Valor Total da NF-e + /// Tag vNF + /// + public double ValorTotalNota { get; set; } + + + /// + /// Valor aproximado total de tributos federais, estaduais e municipais (NT 2013.003) + /// Tag vTotTrib + /// + public double? ValorAproximadoTributos { get; set; } + } +} diff --git a/NFe.Danfe.PdfClown/Modelo/CalculoIssqnViewModel.cs b/NFe.Danfe.PdfClown/Modelo/CalculoIssqnViewModel.cs new file mode 100644 index 000000000..63b8cd420 --- /dev/null +++ b/NFe.Danfe.PdfClown/Modelo/CalculoIssqnViewModel.cs @@ -0,0 +1,35 @@ +namespace NFe.Danfe.PdfClown.Modelo +{ + public class CalculoIssqnViewModel + { + public string InscricaoMunicipal { get; set; } + + /// + /// Valor Total dos Serviços sob não-incidência ou não tributados pelo ICMS + /// Tag vServ + /// + public Double? ValorTotalServicos { get; set; } + + /// + /// Base de Cálculo do ISS + /// Tag vBC + /// + public Double? BaseIssqn { get; set; } + + /// + /// Valor Total do ISS + /// Tag vISS + /// + public Double? ValorIssqn { get; set; } + + /// + /// Mostrar ou não o Bloco. + /// + public Boolean Mostrar { get; set; } + + public CalculoIssqnViewModel() + { + Mostrar = false; + } + } +} diff --git a/NFe.Danfe.PdfClown/Modelo/DanfeViewModel.cs b/NFe.Danfe.PdfClown/Modelo/DanfeViewModel.cs new file mode 100644 index 000000000..0f909ad1a --- /dev/null +++ b/NFe.Danfe.PdfClown/Modelo/DanfeViewModel.cs @@ -0,0 +1,370 @@ +using System.Text; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Esquemas; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Modelo +{ + /// + /// Modelo de dados utilizado para o DANFE. + /// + public class DanfeViewModel + { + private int _QuantidadeCanhoto; + + /// + /// Quantidade de canhotos a serem impressos. + /// + public int QuantidadeCanhotos + { + get => _QuantidadeCanhoto; + set + { + if (value >= 0 && value <= 2) + _QuantidadeCanhoto = value; + else + throw new ArgumentOutOfRangeException("A quantidade de canhotos deve de 0 a 2."); + } + } + + private float _Margem; + + /// + /// Magens horizontais e verticais do DANFE. + /// + public float Margem + { + get => _Margem; + set + { + if (value >= 2 && value <= 5) + _Margem = value; + else + throw new ArgumentOutOfRangeException("A margem deve ser entre 2 e 5."); + } + } + + /// + /// Número do Documento Fiscal + /// Tag nNF + /// + public int NfNumero { get; set; } + + /// + /// Série do Documento Fiscal + /// Tag serie + /// + public int NfSerie { get; set; } + + public Orientacao Orientacao { get; set; } + + /// + /// Chave de Acesso + /// + public string ChaveAcesso { get; set; } + + + /// + /// Descrição da Natureza da Operação + /// Tag natOp + /// + public string NaturezaOperacao { get; set; } + + /// + /// Informações Complementares de interesse do Contribuinte + /// Tag infCpl + /// + public string InformacoesComplementares { get; set; } + + /// + /// Informações adicionais de interesse do Fisco + /// Tag infAdFisco + /// + public string InformacoesAdicionaisFisco { get; set; } + + /// + /// Data e Hora de emissão do Documento Fiscal + /// Tag dhEmi ou dEmi + /// + public DateTime? DataHoraEmissao { get; set; } + + /// + /// Data de Saída ou da Entrada da Mercadoria/Produto + /// Tag dSaiEnt e dhSaiEnt + /// + public DateTime? DataSaidaEntrada { get; set; } + + /// + /// Hora de Saída ou da Entrada da Mercadoria/Produto + /// Tag dSaiEnt e hSaiEnt + /// + public TimeSpan? HoraSaidaEntrada { get; set; } + + /// + /// Dados do Emitente + /// + public EmpresaViewModel Emitente { get; set; } + + /// + /// Dados do Destinatário + /// + public EmpresaViewModel Destinatario { get; set; } + + /// + /// Tipo de Operação - 0-entrada / 1-saída + /// Tag tpNF + /// + public int TipoNF { get; set; } + + /// + /// Tipo de emissão + /// + public FormaEmissao TipoEmissao { get; set; } + + /// + /// Numero do protocolo com sua data e hora + /// + public string ProtocoloAutorizacao { get; set; } + + /// + /// Faturas da Nota Fiscal + /// + public List Duplicatas { get; set; } + + /// + /// Dados da Transportadora + /// + public TransportadoraViewModel Transportadora { get; set; } + + /// + /// View Model do bloco Cálculo do Imposto + /// + public CalculoImpostoViewModel CalculoImposto { get; set; } + + /// + /// Produtos da Nota Fiscal + /// + public List Produtos { get; set; } + + /// + /// View Model do Bloco Cálculo do Issqn + /// + public CalculoIssqnViewModel CalculoIssqn { get; set; } + + /// + /// Tipo de Ambiente + /// + public int TipoAmbiente { get; set; } + + /// + /// Código do status da resposta, cStat, do elemento infProt. + /// + public int? CodigoStatusReposta { get; set; } + + /// + /// Descrição do status da resposta, xMotivo, do elemento infProt. + /// + public string DescricaoStatusReposta { get; set; } + + /// + /// Informações de Notas Fiscais referenciadas que serão levadas no texto adicional. + /// + public List NotasFiscaisReferenciadas { get; set; } + + #region Local Retirada e Entrega + + public LocalEntregaRetiradaViewModel LocalRetirada { get; set; } + + public LocalEntregaRetiradaViewModel LocalEntrega { get; set; } + + #endregion + + #region Informações adicionais de compra + + /// + /// Tag xNEmp + /// + public string NotaEmpenho { get; set; } + + /// + /// Tag xPed + /// + public string Pedido { get; set; } + + /// + /// Tag xCont + /// + public string Contrato { get; set; } + + #endregion + + + #region Opções de exibição + + /// + /// Exibi os valores do ICMS Interestadual e Valor Total dos Impostos no bloco Cálculos do Imposto. + /// + public bool ExibirIcmsInterestadual { get; set; } = true; + + /// + /// Exibi os valores do PIS e COFINS no bloco Cálculos do Imposto. + /// + public bool ExibirPisConfins { get; set; } = true; + + /// + /// Exibi o bloco "Informações do local de entrega" quando o elemento "entrega" estiver disponível. + /// + public bool ExibirBlocoLocalEntrega { get; set; } = true; + + /// + /// Exibi o bloco "Informações do local de retirada" quando o elemento "retirada" estiver disponível. + /// + public bool ExibirBlocoLocalRetirada { get; set; } = true; + + + /// + /// Exibe o Nome Fantasia, caso disponível, ao invés da Razão Social no quadro identificação do emitente. + /// + public bool PreferirEmitenteNomeFantasia { get; set; } = true; + + #endregion + + #region Contingencia + + public DateTime? ContingenciaDataHora { get; set; } + + public string ContingenciaJustificativa { get; set; } + + #endregion + + public DanfeViewModel() + { + QuantidadeCanhotos = 1; + Margem = 4; + Orientacao = Orientacao.Retrato; + CalculoImposto = new CalculoImpostoViewModel(); + Emitente = new EmpresaViewModel(); + Destinatario = new EmpresaViewModel(); + Duplicatas = new List(); + Produtos = new List(); + Transportadora = new TransportadoraViewModel(); + CalculoIssqn = new CalculoIssqnViewModel(); + NotasFiscaisReferenciadas = new List(); + } + + public Boolean MostrarCalculoIssqn { get; set; } + + + /// + /// Substitui o ponto e vírgula (;) por uma quebra de linha. + /// + private string BreakLines(string str) + { + return str == null ? string.Empty : str.Replace(';', '\n'); + } + + public static DanfeViewModel CreateFromXmlFile(string path) + { + return DanfeViewModelCreator.CriarDeArquivoXml(path); + } + + public static DanfeViewModel CreateFromXmlString(string xml) + { + return DanfeViewModelCreator.CreateFromXmlString(xml); + } + + public virtual string TextoRecebimento + { + get + { + return $"Recebemos de {Emitente.RazaoSocial} os produtos e/ou serviços constantes na Nota Fiscal Eletrônica indicada {(Orientacao == Orientacao.Retrato ? "abaixo" : "ao lado")}. Emissão: {DataHoraEmissao.Formatar()} Valor Total: R$ {CalculoImposto.ValorTotalNota.Formatar()} Destinatário: {Destinatario.RazaoSocial}"; + } + } + + public virtual string TextoAdicionalFisco() + { + StringBuilder sb = new StringBuilder(); + + if (TipoEmissao == FormaEmissao.ContingenciaSVCAN || TipoEmissao == FormaEmissao.ContingenciaSVCRS) + { + sb.Append("Contingência "); + + if (TipoEmissao == FormaEmissao.ContingenciaSVCAN) + sb.Append("SVC-AN"); + + if (TipoEmissao == FormaEmissao.ContingenciaSVCRS) + sb.Append("SVC-RS"); + + if (ContingenciaDataHora.HasValue) + { + sb.Append($" - {ContingenciaDataHora.FormatarDataHora()}"); + } + + if (!string.IsNullOrWhiteSpace(ContingenciaJustificativa)) + { + sb.Append($" - {ContingenciaJustificativa}"); + } + + sb.Append("."); + + } + + return sb.ToString(); + } + + public virtual string TextoAdicional() + { + StringBuilder sb = new StringBuilder(); + + if (!string.IsNullOrEmpty(InformacoesComplementares)) + sb.Append(InformacoesComplementares).Replace(";", "\r\n"); + + /*if (!string.IsNullOrEmpty(Destinatario.Email)) + { + // Adiciona um espaço após a virgula caso necessário, isso facilita a quebra de linha. + var destEmail = Regex.Replace(Destinatario.Email, @"(?<=\S)([,;])(?=\S)", "$1 ").Trim(new char[] { ' ', ',', ';' }); + sb.AppendChaveValor("Email do Destinatário", destEmail); + } + + if (!string.IsNullOrEmpty(InformacoesAdicionaisFisco)) + sb.AppendChaveValor("Inf. fisco", InformacoesAdicionaisFisco); + + if (!string.IsNullOrEmpty(Pedido) && !Utils.StringContemChaveValor(InformacoesComplementares, "Pedido", Pedido)) + sb.AppendChaveValor("Pedido", Pedido); + + if (!string.IsNullOrEmpty(Contrato) && !Utils.StringContemChaveValor(InformacoesComplementares, "Contrato", Contrato)) + sb.AppendChaveValor("Contrato", Contrato); + + if (!string.IsNullOrEmpty(NotaEmpenho)) + sb.AppendChaveValor("Nota de Empenho", NotaEmpenho);*/ + + + foreach (var nfref in NotasFiscaisReferenciadas.Take(5)) + { + if (sb.Length > 0) sb.Append(" "); + sb.Append(nfref); + } + + + #region NT 2013.003 Lei da Transparência + + /*if (CalculoImposto.ValorAproximadoTributos.HasValue && (string.IsNullOrEmpty(InformacoesComplementares) || + !Regex.IsMatch(InformacoesComplementares, @"((valor|vlr?\.?)\s+(aprox\.?|aproximado)\s+(dos\s+)?(trib\.?|tributos))|((trib\.?|tributos)\s+(aprox\.?|aproximado))", RegexOptions.IgnoreCase))) + { + if (sb.Length > 0) sb.Append("\r\n"); + sb.Append("Valor Aproximado dos Tributos: "); + sb.Append(CalculoImposto.ValorAproximadoTributos.FormatarMoeda()); + }*/ + + #endregion + + + return sb.ToString(); + } + + public void DefinirTextoCreditos(string textoCreditos) + { + Strings.TextoCreditos = textoCreditos; + } + } +} diff --git a/NFe.Danfe.PdfClown/Modelo/DanfeViewModelCreator.cs b/NFe.Danfe.PdfClown/Modelo/DanfeViewModelCreator.cs new file mode 100644 index 000000000..5de61dfee --- /dev/null +++ b/NFe.Danfe.PdfClown/Modelo/DanfeViewModelCreator.cs @@ -0,0 +1,389 @@ +using System.Text; +using System.Xml; +using System.Xml.Serialization; +using NFe.Danfe.PdfClown.Enumeracoes; +using NFe.Danfe.PdfClown.Esquemas; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Modelo +{ + public static class DanfeViewModelCreator + { + public readonly static IEnumerable FormasEmissaoSuportadas = new FormaEmissao[] { FormaEmissao.Normal, FormaEmissao.ContingenciaSVCAN, FormaEmissao.ContingenciaSVCRS }; + + private static EmpresaViewModel CreateEmpresaFrom(Empresa empresa) + { + EmpresaViewModel model = new EmpresaViewModel(); + + model.RazaoSocial = empresa.xNome; + model.CnpjCpf = !string.IsNullOrWhiteSpace(empresa.CNPJ) ? empresa.CNPJ : empresa.CPF; + model.Ie = empresa.IE; + model.IeSt = empresa.IEST; + model.Email = empresa.email; + + var end = empresa.Endereco; + + if (end != null) + { + model.EnderecoLogadrouro = end.xLgr; + model.EnderecoNumero = end.nro; + model.EnderecoBairro = end.xBairro; + model.Municipio = end.xMun; + model.EnderecoUf = end.UF; + model.EnderecoCep = end.CEP; + model.Telefone = end.fone; + model.EnderecoComplemento = end.xCpl; + } + + if (empresa is Emitente) + { + var emit = empresa as Emitente; + model.IM = emit.IM; + model.CRT = emit.CRT; + model.NomeFantasia = emit.xFant; + } + + return model; + } + + internal static DanfeViewModel CreateFromXmlString(string xml) + { + ProcNFe nfe = null; + XmlSerializer serializer = new XmlSerializer(typeof(ProcNFe)); + + try + { + using (TextReader reader = new StringReader(xml)) + { + nfe = (ProcNFe)serializer.Deserialize(reader); + } + + return CreateFromXml(nfe); + } + catch (System.InvalidOperationException e) + { + throw new Exception("Não foi possível interpretar o texto Xml.", e); + } + } + + /// + /// Cria o modelo a partir de um arquivo xml. + /// + /// + /// + public static DanfeViewModel CriarDeArquivoXml(string caminho) + { + using (StreamReader sr = new StreamReader(caminho, true)) + { + return CriarDeArquivoXmlInternal(sr); + } + } + + /// + /// Cria o modelo a partir de um arquivo xml contido num stream. + /// + /// + /// Modelo + public static DanfeViewModel CriarDeArquivoXml(Stream stream) + { + if (stream == null) throw new ArgumentNullException(nameof(stream)); + + using (StreamReader sr = new StreamReader(stream, true)) + { + return CriarDeArquivoXmlInternal(sr); + } + } + + /// + /// Cria o modelo a partir de uma string xml. + /// + public static DanfeViewModel CriarDeStringXml(string str) + { + if (str == null) + throw new ArgumentNullException(nameof(str)); + + using (StringReader sr = new StringReader(str)) + { + return CriarDeArquivoXmlInternal(sr); + } + } + + + private static DanfeViewModel CriarDeArquivoXmlInternal(TextReader reader) + { + ProcNFe nfe = null; + XmlSerializer serializer = new XmlSerializer(typeof(ProcNFe)); + + try + { + nfe = (ProcNFe)serializer.Deserialize(reader); + return CreateFromXml(nfe); + } + catch (InvalidOperationException e) + { + if (e.InnerException is XmlException) + { + var ex = (XmlException)e.InnerException; + throw new Exception(string.Format("Não foi possível interpretar o Xml. Linha {0} Posição {1}.", ex.LineNumber, ex.LinePosition)); + } + + throw new XmlException("O Xml não parece ser uma NF-e processada.", e); + } + } + + internal static void ExtrairDatas(DanfeViewModel model, InfNFe infNfe) + { + var ide = infNfe.ide; + + if (infNfe.Versao.Maior >= 3) + { + if (ide.dhEmi.HasValue) model.DataHoraEmissao = ide.dhEmi?.DateTimeOffsetValue.DateTime; + if (ide.dhSaiEnt.HasValue) model.DataSaidaEntrada = ide.dhSaiEnt?.DateTimeOffsetValue.DateTime; + + if (model.DataSaidaEntrada.HasValue) + model.HoraSaidaEntrada = model.DataSaidaEntrada?.TimeOfDay; + } + else + { + model.DataHoraEmissao = ide.dEmi; + model.DataSaidaEntrada = ide.dSaiEnt; + + if (!string.IsNullOrWhiteSpace(ide.hSaiEnt)) + model.HoraSaidaEntrada = TimeSpan.Parse(ide.hSaiEnt); + + } + } + + internal static CalculoImpostoViewModel CriarCalculoImpostoViewModel(ICMSTotal i) + { + return new CalculoImpostoViewModel() + { + ValorAproximadoTributos = i.vTotTrib, + BaseCalculoIcms = i.vBC, + ValorIcms = i.vICMS, + BaseCalculoIcmsSt = i.vBCST, + ValorIcmsSt = i.vST, + ValorTotalProdutos = i.vProd, + ValorFrete = i.vFrete, + ValorSeguro = i.vSeg, + Desconto = i.vDesc, + ValorII = i.vII, + ValorIpi = i.vIPI, + ValorPis = i.vPIS, + ValorCofins = i.vCOFINS, + OutrasDespesas = i.vOutro, + ValorTotalNota = i.vNF, + vFCPUFDest = i.vFCPUFDest, + vICMSUFDest = i.vICMSUFDest, + vICMSUFRemet = i.vICMSUFRemet + }; + } + + public static DanfeViewModel CreateFromXml(ProcNFe procNfe) + { + DanfeViewModel model = new DanfeViewModel(); + + var nfe = procNfe.NFe; + var infNfe = nfe.infNFe; + var ide = infNfe.ide; + model.TipoEmissao = ide.tpEmis; + + if (ide.mod != 55) + throw new NotSupportedException("Somente o mod==55 está implementado."); + + if (!FormasEmissaoSuportadas.Contains(model.TipoEmissao)) + throw new NotSupportedException($"O tpEmis {ide.tpEmis} não é suportado."); + + model.Orientacao = ide.tpImp == 1 ? Orientacao.Retrato : Orientacao.Paisagem; + + var infProt = procNfe.protNFe.infProt; + model.CodigoStatusReposta = infProt.cStat; + model.DescricaoStatusReposta = infProt.xMotivo; + + model.TipoAmbiente = (int)ide.tpAmb; + model.NfNumero = ide.nNF; + model.NfSerie = ide.serie; + model.NaturezaOperacao = ide.natOp; + model.ChaveAcesso = procNfe.NFe.infNFe.Id.Substring(3); + model.TipoNF = (int)ide.tpNF; + + model.Emitente = CreateEmpresaFrom(infNfe.emit); + model.Destinatario = CreateEmpresaFrom(infNfe.dest); + + // Local retirada e entrega + if (infNfe.retirada != null) + model.LocalRetirada = CreateLocalRetiradaEntrega(infNfe.retirada); + + if (infNfe.entrega != null) + model.LocalEntrega = CreateLocalRetiradaEntrega(infNfe.entrega); + + model.NotasFiscaisReferenciadas = ide.NFref.Select(x => x.ToString()).ToList(); + + // Informações adicionais de compra + if (infNfe.compra != null) + { + model.Contrato = infNfe.compra.xCont; + model.NotaEmpenho = infNfe.compra.xNEmp; + model.Pedido = infNfe.compra.xPed; + } + + foreach (var det in infNfe.det) + { + ProdutoViewModel produto = new ProdutoViewModel(); + produto.Codigo = det.prod.cProd; + produto.Descricao = det.prod.xProd; + produto.Ncm = det.prod.NCM; + produto.Cfop = det.prod.CFOP; + produto.Unidade = det.prod.uCom; + produto.Quantidade = det.prod.qCom; + produto.ValorUnitario = det.prod.vUnCom; + produto.ValorTotal = det.prod.vProd; + produto.InformacoesAdicionais = det.infAdProd; + + var imposto = det.imposto; + + if (imposto != null) + { + if (imposto.ICMS != null) + { + var icms = imposto.ICMS.ICMS; + + if (icms != null) + { + produto.ValorIcms = icms.vICMS; + produto.BaseIcms = icms.vBC; + produto.AliquotaIcms = icms.pICMS; + produto.OCst = icms.orig + icms.CST + icms.CSOSN; + } + } + + if (imposto.IPI != null) + { + var ipi = imposto.IPI.IPITrib; + + if (ipi != null) + { + produto.ValorIpi = ipi.vIPI; + produto.AliquotaIpi = ipi.pIPI; + } + } + } + + model.Produtos.Add(produto); + } + + if (infNfe.cobr != null) + { + var duplicatas = infNfe.cobr.dup.Select(x => new DuplicataViewModel + { + Numero = x.nDup, + Valor = x.vDup, + Vecimento = x.dVenc + }); + model.Duplicatas = duplicatas.ToList(); + } + + model.CalculoImposto = CriarCalculoImpostoViewModel(infNfe.total.ICMSTot); + + var issqnTotal = infNfe.total.ISSQNtot; + + if (issqnTotal != null) + { + var c = model.CalculoIssqn; + c.InscricaoMunicipal = infNfe.emit.IM; + c.BaseIssqn = issqnTotal.vBC; + c.ValorTotalServicos = issqnTotal.vServ; + c.ValorIssqn = issqnTotal.vISS; + c.Mostrar = true; + } + + var transp = infNfe.transp; + var transportadora = transp.transporta; + var transportadoraModel = model.Transportadora; + + transportadoraModel.ModalidadeFrete = (int)transp.modFrete; + + if (transp.veicTransp != null) + { + transportadoraModel.VeiculoUf = transp.veicTransp.UF; + transportadoraModel.CodigoAntt = transp.veicTransp.RNTC; + transportadoraModel.Placa = transp.veicTransp.placa; + } + + if (transportadora != null) + { + transportadoraModel.RazaoSocial = transportadora.xNome; + transportadoraModel.EnderecoUf = transportadora.UF; + transportadoraModel.CnpjCpf = !string.IsNullOrWhiteSpace(transportadora.CNPJ) ? transportadora.CNPJ : transportadora.CPF; + transportadoraModel.EnderecoLogadrouro = transportadora.xEnder; + transportadoraModel.Municipio = transportadora.xMun; + transportadoraModel.Ie = transportadora.IE; + } + + + var vol = transp.vol.FirstOrDefault(); + + if (vol != null) + { + transportadoraModel.QuantidadeVolumes = vol.qVol; + transportadoraModel.Especie = vol.esp; + transportadoraModel.Marca = vol.marca; + transportadoraModel.Numeracao = vol.nVol; + transportadoraModel.PesoBruto = vol.pesoB; + transportadoraModel.PesoLiquido = vol.pesoL; + } + + + var infAdic = infNfe.infAdic; + if (infAdic != null) + { + model.InformacoesComplementares = procNfe.NFe.infNFe.infAdic.infCpl; + model.InformacoesAdicionaisFisco = procNfe.NFe.infNFe.infAdic.infAdFisco; + } + + var infoProto = procNfe.protNFe.infProt; + + model.ProtocoloAutorizacao = string.Format(Formatador.Cultura, "{0} - {1}", infoProto.nProt, infoProto.dhRecbto.DateTimeOffsetValue.DateTime); + + ExtrairDatas(model, infNfe); + + // Contingência SVC-AN e SVC-RS + if (model.TipoEmissao == FormaEmissao.ContingenciaSVCAN || model.TipoEmissao == FormaEmissao.ContingenciaSVCRS) + { + model.ContingenciaDataHora = ide.dhCont?.DateTimeOffsetValue.DateTime; + model.ContingenciaJustificativa = ide.xJust; + } + + return model; + } + + private static LocalEntregaRetiradaViewModel CreateLocalRetiradaEntrega(LocalEntregaRetirada local) + { + var m = new LocalEntregaRetiradaViewModel + { + NomeRazaoSocial = local.xNome, + CnpjCpf = !string.IsNullOrWhiteSpace(local.CNPJ) ? local.CNPJ : local.CPF, + InscricaoEstadual = local.IE, + Bairro = local.xBairro, + Municipio = local.xMun, + Uf = local.UF, + Cep = local.CEP, + Telefone = local.fone + }; + + StringBuilder sb = new StringBuilder(); + sb.Append(local.xLgr); + + if (!string.IsNullOrWhiteSpace(local.nro)) + sb.Append(", ").Append(local.nro); + + if (!string.IsNullOrWhiteSpace(local.xCpl)) + sb.Append(" - ").Append(local.xCpl); + + m.Endereco = sb.ToString(); + + return m; + } + + } +} diff --git a/NFe.Danfe.PdfClown/Modelo/DuplicataViewModel.cs b/NFe.Danfe.PdfClown/Modelo/DuplicataViewModel.cs new file mode 100644 index 000000000..c08c87aba --- /dev/null +++ b/NFe.Danfe.PdfClown/Modelo/DuplicataViewModel.cs @@ -0,0 +1,23 @@ +namespace NFe.Danfe.PdfClown.Modelo +{ + public class DuplicataViewModel + { + /// + /// Número da Duplicata + /// Tag nDup + /// + public string Numero { get; set; } + + /// + /// Data de vencimento + /// Tag dVenc + /// + public DateTime? Vecimento { get; set; } + + /// + /// Valor da duplicata + /// Tag vDup + /// + public Double? Valor { get; set; } + } +} diff --git a/NFe.Danfe.PdfClown/Modelo/EmpresaViewModel.cs b/NFe.Danfe.PdfClown/Modelo/EmpresaViewModel.cs new file mode 100644 index 000000000..2380e1407 --- /dev/null +++ b/NFe.Danfe.PdfClown/Modelo/EmpresaViewModel.cs @@ -0,0 +1,142 @@ +using System.Text; +using NFe.Danfe.PdfClown.Tools; + +namespace NFe.Danfe.PdfClown.Modelo +{ + public class EmpresaViewModel + { + /// + /// Razão Social ou Nome + /// Tag xNome + /// + public string RazaoSocial { get; set; } + + /// + /// Nome fantasia + /// Tag xFant + /// + public string NomeFantasia { get; set; } + + /// + /// Logradouro + /// Tag xLgr + /// + public string EnderecoLogadrouro { get; set; } + + /// + /// Complemento + /// Tag xCpl + /// + public string EnderecoComplemento { get; set; } + + /// + /// Número + /// Tag nro + /// + public string EnderecoNumero { get; set; } + + /// + /// Código do CEP + /// Tag CEP + /// + public string EnderecoCep { get; set; } + + /// + /// Bairro + /// Tag xBairro + /// + public string EnderecoBairro { get; set; } + + /// + /// Sigla da UF + /// Tag UF + /// + public string EnderecoUf { get; set; } + + /// + /// Nome do município + /// Tag xMun + /// + public string Municipio { get; set; } + + /// + /// Telefone + /// Tag fone + /// + public string Telefone { get; set; } + + /// + /// CNPJ ou CPF + /// Tag CNPJ ou CPF + /// + public string CnpjCpf { get; set; } + + /// + /// Inscrição Estadual + /// Tag IE + /// + public string Ie { get; set; } + + /// + /// IE do Substituto Tributário + /// Tag IEST + /// + public string IeSt { get; set; } + + /// + /// Inscrição Municipal + /// Tag IM + /// + public string IM { get; set; } + + /// + /// Email + /// Tag email + /// + public string Email { get; set; } + + /// + /// Código de Regime Tributário + /// + public string CRT { get; set; } + + /// + /// Linha 1 do Endereço + /// + public string EnderecoLinha1 + { + get + { + StringBuilder sb = new StringBuilder(); + sb.Append(EnderecoLogadrouro); + if (!string.IsNullOrWhiteSpace(EnderecoNumero)) sb.Append(", ").Append(EnderecoNumero); + if (!string.IsNullOrWhiteSpace(EnderecoComplemento)) sb.Append(" - ").Append(EnderecoComplemento); + return sb.ToString(); + } + } + + /// + /// Linha 1 do Endereço + /// + public string EnderecoLinha2 => $"{EnderecoBairro} - CEP: {Formatador.FormatarCEP(EnderecoCep)}"; + + + /// + /// Linha 3 do Endereço + /// + public string EnderecoLinha3 + { + get + { + StringBuilder sb = new StringBuilder() + .Append(Municipio).Append(" - ").Append(EnderecoUf); + + if (!string.IsNullOrWhiteSpace(Telefone)) + sb.Append(" Fone: ").Append(Formatador.FormatarTelefone(Telefone)); + + return sb.ToString(); + } + } + + } +} diff --git a/NFe.Danfe.PdfClown/Modelo/LocalEntregaRetiradaViewModel.cs b/NFe.Danfe.PdfClown/Modelo/LocalEntregaRetiradaViewModel.cs new file mode 100644 index 000000000..83ba4b748 --- /dev/null +++ b/NFe.Danfe.PdfClown/Modelo/LocalEntregaRetiradaViewModel.cs @@ -0,0 +1,15 @@ +namespace NFe.Danfe.PdfClown.Modelo +{ + public class LocalEntregaRetiradaViewModel + { + public string NomeRazaoSocial { get; set; } + public string CnpjCpf { get; set; } + public string InscricaoEstadual { get; set; } + public string Endereco { get; set; } + public string Bairro { get; set; } + public string Cep { get; set; } + public string Municipio { get; set; } + public string Uf { get; set; } + public string Telefone { get; set; } + } +} \ No newline at end of file diff --git a/NFe.Danfe.PdfClown/Modelo/ProdutoViewModel.cs b/NFe.Danfe.PdfClown/Modelo/ProdutoViewModel.cs new file mode 100644 index 000000000..f0f187024 --- /dev/null +++ b/NFe.Danfe.PdfClown/Modelo/ProdutoViewModel.cs @@ -0,0 +1,123 @@ +namespace NFe.Danfe.PdfClown.Modelo +{ + public class ProdutoViewModel + { + /// + /// Código do produto ou serviço + /// Tag cProd + /// + public string Codigo { get; set; } + + /// + /// Informações Adicionais do Produto + /// Tag infAdProd + /// + public string InformacoesAdicionais { get; set; } + + /// + /// Descrição do produto ou serviço + /// Tag xProd + /// + public string Descricao { get; set; } + + /// + /// Código NCM com 8 dígitos ou 2 dígitos (gênero) + /// Tag NCM + /// + public string Ncm { get; set; } + + + /// + /// Origem da mercadoria + Tributação do ICMS + /// Tag orig e CST + /// + public string OCst { get; set; } + + /// + /// Código Fiscal de Operações e Prestações + /// Tag CFOP + /// + public int Cfop { get; set; } + + /// + /// Unidade Comercial + /// Tag uCom + /// + public string Unidade { get; set; } + + /// + /// Quantidade Comercial + /// Tag qCom + /// + public double Quantidade { get; set; } + + /// + /// Valor Unitário de Comercialização + /// Tag vUnCom + /// + public double ValorUnitario { get; set; } + + /// + /// Valor Total Bruto dos Produtos ou Serviços + /// Tag vProd + /// + public double ValorTotal { get; set; } + + /// + /// Valor da BC do ICMS + /// Tag vBC + /// + public double BaseIcms { get; set; } + + /// + /// Valor do ICMS + /// Tag vICMS + /// + public double ValorIcms { get; set; } + + /// + /// Alíquota do imposto + /// Tag pICMS + /// + public double AliquotaIcms { get; set; } + + /// + /// Valor do IPI + /// Tag vIPI + /// + public double? ValorIpi { get; set; } + + /// + /// Alíquota do IPI + /// Tag pIPI + /// + public double? AliquotaIpi { get; set; } + + /// + /// Valor aproximado total de tributos federais, estaduais e municipais. [NT2013.003] + /// Tag vTotTrib + /// + public double? ValorAproximadoTributos { get; set; } + + public ProdutoViewModel() + { + AliquotaIpi = null; + ValorIpi = null; + } + + public string DescricaoCompleta + { + get + { + string descriCaoCompleta = Descricao; + + if (!string.IsNullOrWhiteSpace(InformacoesAdicionais)) + { + descriCaoCompleta += "\r\n" + InformacoesAdicionais; + } + + return descriCaoCompleta; + } + } + } +} diff --git a/NFe.Danfe.PdfClown/Modelo/TransportadoraViewModel.cs b/NFe.Danfe.PdfClown/Modelo/TransportadoraViewModel.cs new file mode 100644 index 000000000..cc6353bfb --- /dev/null +++ b/NFe.Danfe.PdfClown/Modelo/TransportadoraViewModel.cs @@ -0,0 +1,97 @@ +namespace NFe.Danfe.PdfClown.Modelo +{ + public class TransportadoraViewModel : EmpresaViewModel + { + public static readonly Dictionary ModalidadesFrete = new Dictionary() + { + {0, "Por conta Remetente"}, + {1, "Por conta Destinatário"}, + {2, "Por conta Terceiros"}, + {3, "Próprio, por conta Rem."}, + {4, "Próprio, por conta Dest."}, + {9, "Sem Transporte"} + }; + + /// + /// Modalidade do frete. + /// Tag modFrete + /// + public int ModalidadeFrete { get; set; } + + /// + /// Registro Nacional de Transportador de Carga (ANTT). + /// Tag RNTC + /// + public string CodigoAntt { get; set; } + + /// + /// Placa do Veículo. + /// Tag placa + /// + public string Placa { get; set; } + + /// + /// Sigla da UF do Veículo + /// Tag UF + /// + public string VeiculoUf { get; set; } + + /// + /// Quantidade de volumes transportados. + /// Tag qVol + /// + public Double? QuantidadeVolumes { get; set; } + + /// + /// Espécie dos volumes transportados. + /// Tag esp + /// + public string Especie { get; set; } + + /// + /// Marca dos volumes transportados. + /// Tag marca + /// + public string Marca { get; set; } + + /// + /// Numeração dos volumes transportados. + /// Tag nVol + /// + public string Numeracao { get; set; } + + /// + /// Peso Líquido (em kg). + /// Tag pesoL + /// + public Double? PesoLiquido { get; set; } + + /// + /// Peso Bruto (em kg). + /// Tag pesoB + /// + public Double? PesoBruto { get; set; } + + public string ModalidadeFreteString + { + get + { + string result = ""; + + if (ModalidadesFrete.ContainsKey(ModalidadeFrete)) + { + result = $"{ModalidadeFrete}-{ModalidadesFrete[ModalidadeFrete]}"; + if (ModalidadeFrete == 9) result = string.Empty; + if (ModalidadeFrete == 0) result = "0 - CIF"; + if (ModalidadeFrete == 1) result = "1 - FOB"; + } + else + { + result = $"({ModalidadeFrete})"; + } + + return result; + } + } + } +} diff --git a/NFe.Danfe.PdfClown/NFe.Danfe.PdfClown.csproj b/NFe.Danfe.PdfClown/NFe.Danfe.PdfClown.csproj new file mode 100644 index 000000000..3855ab5ce --- /dev/null +++ b/NFe.Danfe.PdfClown/NFe.Danfe.PdfClown.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + latest + + + + + + + + diff --git a/NFe.Danfe.PdfClown/Strings.cs b/NFe.Danfe.PdfClown/Strings.cs new file mode 100644 index 000000000..091a0d8b1 --- /dev/null +++ b/NFe.Danfe.PdfClown/Strings.cs @@ -0,0 +1,19 @@ +namespace NFe.Danfe.PdfClown +{ + internal static class Strings + { + public const string InscricaoEstadual = "Inscrição Estadual"; + public const string Endereco = "Endereço"; + public const string Municipio = "Município"; + public const string UF = "UF"; + public const string CnpjCpf = "Cnpj / Cpf"; + public const string Quantidade = "Quantidade"; + public const string RazaoSocial = "Razão Social"; + public const string NomeRazaoSocial = "Nome / Razão Social"; + public const string FoneFax = "Fone / Fax"; + public const string Cep = "CEP"; + public const string BairroDistrito = "Bairro"; + public const string TextoConsulta = "Consulta de autenticidade no portal nacional da NF-e www.nfe.fazenda.gov.br/portal ou no site da Sefaz Autorizadora"; + public static string TextoCreditos = "Desenvolvido por [ www.laranjeiras.dev / (21)997706037 ]"; + } +} diff --git a/NFe.Danfe.PdfClown/Structs/DateTimeOffsetIso8601.cs b/NFe.Danfe.PdfClown/Structs/DateTimeOffsetIso8601.cs new file mode 100644 index 000000000..fc3ec6a5e --- /dev/null +++ b/NFe.Danfe.PdfClown/Structs/DateTimeOffsetIso8601.cs @@ -0,0 +1,95 @@ +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; + +namespace NFe.Danfe.PdfClown.Structs +{ + // Source: https://stackoverflow.com/questions/3377036/how-can-i-xml-serialize-a-datetimeoffset-property + /// + /// The default value is DateTimeOffset.MinValue. This is a value + /// type and has the same hash code as DateTimeOffset! Implicit + /// assignment from DateTime is neither implemented nor desirable! + /// + public struct DateTimeOffsetIso8601 : IXmlSerializable + { + public DateTimeOffset DateTimeOffsetValue { private set; get; } + + public DateTimeOffsetIso8601(DateTimeOffset value) + { + this.DateTimeOffsetValue = value; + } + + public static implicit operator DateTimeOffsetIso8601(DateTimeOffset value) + { + return new DateTimeOffsetIso8601(value); + } + + public static implicit operator DateTimeOffset(DateTimeOffsetIso8601 instance) + { + return instance.DateTimeOffsetValue; + } + + public static bool operator ==(DateTimeOffsetIso8601 a, DateTimeOffsetIso8601 b) + { + return a.DateTimeOffsetValue == b.DateTimeOffsetValue; + } + + public static bool operator !=(DateTimeOffsetIso8601 a, DateTimeOffsetIso8601 b) + { + return a.DateTimeOffsetValue != b.DateTimeOffsetValue; + } + + public static bool operator <(DateTimeOffsetIso8601 a, DateTimeOffsetIso8601 b) + { + return a.DateTimeOffsetValue < b.DateTimeOffsetValue; + } + + public static bool operator >(DateTimeOffsetIso8601 a, DateTimeOffsetIso8601 b) + { + return a.DateTimeOffsetValue > b.DateTimeOffsetValue; + } + + public override bool Equals(object o) + { + if (o is DateTimeOffsetIso8601) + return DateTimeOffsetValue.Equals(((DateTimeOffsetIso8601)o).DateTimeOffsetValue); + else if (o is DateTimeOffset) + return DateTimeOffsetValue.Equals((DateTimeOffset)o); + else + return false; + } + + public override int GetHashCode() + { + return DateTimeOffsetValue.GetHashCode(); + } + + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + var text = reader.ReadElementString(); + + if (!string.IsNullOrWhiteSpace(text)) + DateTimeOffsetValue = XmlConvert.ToDateTimeOffset(text); + } + + public override string ToString() + { + return DateTimeOffsetValue.ToString(format: "o"); + } + + public string ToString(string format) + { + return DateTimeOffsetValue.ToString(format); + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteString(DateTimeOffsetValue.ToString(format: "o")); + } + } +} diff --git a/NFe.Danfe.PdfClown/Tools/Extensions.cs b/NFe.Danfe.PdfClown/Tools/Extensions.cs new file mode 100644 index 000000000..9d9990e08 --- /dev/null +++ b/NFe.Danfe.PdfClown/Tools/Extensions.cs @@ -0,0 +1,126 @@ +using System.Drawing; +using System.Text; +using NFe.Danfe.PdfClown.Enumeracoes; +using org.pdfclown.documents.contents.composition; + +namespace NFe.Danfe.PdfClown.Tools +{ + internal static class Extentions + { + private const float PointFactor = 72F / 25.4F; + + /// + /// Converts Millimeters to Point + /// + /// + /// + public static float ToPoint(this float mm) + { + return PointFactor * mm; + } + + /// + /// Converts Point to Millimeters + /// + /// + /// + public static float ToMm(this float point) + { + return point / PointFactor; + } + + /// + /// Converts Point to Millimeters + /// + /// + /// + public static SizeF ToMm(this SizeF s) + { + return new SizeF(s.Width.ToMm(), s.Height.ToMm()); + } + + /// + /// Converts Point to Millimeters + /// + /// + /// + public static SizeF ToPointMeasure(this SizeF s) + { + return new SizeF(s.Width.ToPoint(), s.Height.ToPoint()); + } + + /// + /// Converts Millimeters to Point + /// + /// + /// + public static double ToPoint(this double mm) + { + return PointFactor * mm; + } + + /// + /// Converts Point to Millimeters + /// + /// + /// + public static double ToMm(this double point) + { + return point / PointFactor; + } + + public static RectangleF InflatedRetangle(this RectangleF rect, float top, float button, float horizontal) + { + return new RectangleF(rect.X + horizontal, rect.Y + top, rect.Width - 2 * horizontal, rect.Height - top - button); + } + + public static RectangleF InflatedRetangle(this RectangleF rect, float value) => rect.InflatedRetangle(value, value, value); + + public static RectangleF ToPointMeasure(this RectangleF r) => new RectangleF(r.X.ToPoint(), r.Y.ToPoint(), r.Width.ToPoint(), r.Height.ToPoint()); + + public static RectangleF CutTop(this RectangleF r, float height) => new RectangleF(r.X, r.Y + height, r.Width, r.Height - height); + public static RectangleF CutBottom(this RectangleF r, float height) => new RectangleF(r.X, r.Y, r.Width, r.Height - height); + public static RectangleF CutLeft(this RectangleF r, float width) => new RectangleF(r.X + width, r.Y, r.Width - width, r.Height); + + public static PointF ToPointMeasure(this PointF r) => new PointF(r.X.ToPoint(), r.Y.ToPoint()); + + public static StringBuilder AppendChaveValor(this StringBuilder sb, string chave, string valor) + { + if (sb.Length > 0) sb.Append(' '); + return sb.Append(chave).Append(": ").Append(valor); + } + + public static XAlignmentEnum ToPdfClownAlignment(this AlinhamentoHorizontal ah) + { + switch (ah) + { + case AlinhamentoHorizontal.Esquerda: + return XAlignmentEnum.Left; + case AlinhamentoHorizontal.Centro: + return XAlignmentEnum.Center; + case AlinhamentoHorizontal.Direita: + return XAlignmentEnum.Right; + } + + throw new InvalidOperationException(); + } + + public static YAlignmentEnum ToPdfClownAlignment(this AlinhamentoVertical av) + { + switch (av) + { + case AlinhamentoVertical.Topo: + return YAlignmentEnum.Top; + case AlinhamentoVertical.Centro: + return YAlignmentEnum.Middle; + case AlinhamentoVertical.Base: + return YAlignmentEnum.Bottom; + } + + throw new InvalidOperationException(); + } + + + + } +} diff --git a/NFe.Danfe.PdfClown/Tools/Formatador.cs b/NFe.Danfe.PdfClown/Tools/Formatador.cs new file mode 100644 index 000000000..20b01c532 --- /dev/null +++ b/NFe.Danfe.PdfClown/Tools/Formatador.cs @@ -0,0 +1,223 @@ +using System.Globalization; +using System.Text.RegularExpressions; + +namespace NFe.Danfe.PdfClown.Tools +{ + /// + /// Classe que ajuda na formatação de dados. + /// + public static class Formatador + { + /// + /// Cultura pt-BR + /// + public static readonly CultureInfo Cultura = new CultureInfo(1046); + + static Formatador() + { + Cultura.NumberFormat.CurrencyPositivePattern = 2; + Cultura.NumberFormat.CurrencyNegativePattern = 9; + } + + public const string FormatoNumeroNF = @"000\.000\.000"; + + public const string CEP = @"^(\d{5})\-?(\d{3})$"; + public const string CNPJ = @"^(\d{2})\.?(\d{3})\.?(\d{3})\/?(\d{4})\-?(\d{2})$"; + public const string CPF = @"^(\d{3})\.?(\d{3})\.?(\d{3})\-?(\d{2})$"; + public const string Telefone = @"^\(?(\d{2})\)?\s*(\d{4,5})\s*\-?\s*(\d{4})$"; + + public const string FormatoMoeda = "#,0.00##"; + public const string FormatoNumero = "#,0.####"; + + private static string InternalRegexReplace(string input, string pattern, string replacement) + { + string result = input; + + if (!string.IsNullOrWhiteSpace(input)) + { + input = input.Trim(); + + Regex rgx = new Regex(pattern); + + if (rgx.IsMatch(input)) + { + result = rgx.Replace(input, replacement); + } + } + + return result; + } + + /// + /// Formata a linha 1 do endereço. Ex. Floriano Peixoto, 512 + /// + /// + /// + /// + public static string FormatarEnderecoLinha1(string endereco, int? numero, string complemento = null) + { + string sNumero = numero.HasValue ? numero.Value.ToString() : null; + return FormatarEnderecoLinha1(endereco, sNumero, complemento); + } + + /// + /// Formata a linha 1 do endereço. Ex. Floriano Peixoto, 512 + /// + /// + /// + /// + public static string FormatarEnderecoLinha1(string endereco, string numero = null, string complemento = null) + { + string linha1 = string.Empty; + + if (!string.IsNullOrWhiteSpace(endereco)) + { + linha1 = string.Format("{0}, {1}", endereco.Trim(), string.IsNullOrWhiteSpace(numero) ? "S/N" : numero.Trim()); + + if (!string.IsNullOrWhiteSpace(complemento)) + { + linha1 += " - " + complemento.Trim(); + } + } + + return linha1; + } + + /// + /// Formata um CEP + /// + /// CEP + /// CEP Formatado ou vazio caso cep inválido + public static string FormatarCEP(string cep) + { + return InternalRegexReplace(cep, CEP, "$1-$2"); + } + + public static string FormatarCEP(int cep) + { + if (cep < 0) + { + throw new ArgumentOutOfRangeException("cep", "o cep não pode ser negativo."); + } + + return FormatarCEP(cep.ToString().PadLeft(8, '0')); + } + + public static string FormatarCnpj(string cnpj) + { + return InternalRegexReplace(cnpj, CNPJ, "$1.$2.$3/$4-$5"); + } + + public static string FormatarCpf(string cpf) + { + return InternalRegexReplace(cpf, CPF, "$1.$2.$3-$4"); + } + + /// + /// Formata um número de documento + /// + /// + /// + public static string FormatarCpfCnpj(string cpfCnpj) + { + string result; + + if (!string.IsNullOrWhiteSpace(cpfCnpj)) + { + result = cpfCnpj.Trim(); + + if (Regex.IsMatch(result, CPF)) + { + result = FormatarCpf(result); + } + else if (Regex.IsMatch(result, CNPJ)) + { + result = FormatarCnpj(result); + } + } + else + { + result = string.Empty; + } + + return result; + } + + /// + /// Formata uma string de município com a uf, ex Caçapava do Sul - RS + /// + /// Município + /// UF + /// Separador + /// String formatada. + public static string FormatarMunicipioUf(string municipio, string uf, string separador = " - ") + { + string result = ""; + + if (!string.IsNullOrWhiteSpace(municipio) && !string.IsNullOrWhiteSpace(uf)) + { + result = string.Format("{0}{1}{2}", municipio.Trim(), separador, uf.Trim()); + } + else if (!string.IsNullOrWhiteSpace(municipio)) + { + result = municipio.Trim(); + } + else if (!string.IsNullOrWhiteSpace(uf)) + { + result = uf.Trim(); + } + + return result; + } + + public static string FormatarTelefone(string telefone) + { + return InternalRegexReplace(telefone, Telefone, "($1) $2-$3"); + } + + public static string FormatarChaveAcesso(string chaveAcesso) + { + return Regex.Replace(chaveAcesso, ".{4}", "$0 ").TrimEnd(); + } + + public static string Formatar(this Double number, string formato = FormatoMoeda) + { + return number.ToString(formato, Cultura); + } + + public static string Formatar(this int number, string formato = FormatoMoeda) + { + return number.ToString(formato, Cultura); + } + + public static string Formatar(this int? number, string formato = FormatoMoeda) + { + return number.HasValue ? number.Value.Formatar(formato) : string.Empty; + } + + public static string Formatar(this Double? number, string formato = FormatoMoeda) + { + return number.HasValue ? number.Value.Formatar(formato) : string.Empty; + } + + public static string FormatarMoeda(this Double? number) + { + return number.HasValue ? number.Value.ToString("C", Cultura) : string.Empty; + } + + public static string Formatar(this DateTime? dateTime) + { + return dateTime.HasValue ? dateTime.Value.ToString("dd/MM/yyyy") : string.Empty; + } + + public static string FormatarDataHora(this DateTime? dateTime) + { + return dateTime.HasValue ? dateTime.Value.ToString("dd/MM/yyyy hh:mm:ss") : string.Empty; + } + + public static string Formatar(this TimeSpan? timeSpan) + { + return timeSpan.HasValue ? timeSpan.Value.ToString() : string.Empty; + } + } +} diff --git a/NFe.Danfe.PdfClown/Tools/Utils.cs b/NFe.Danfe.PdfClown/Tools/Utils.cs new file mode 100644 index 000000000..88b6d4a7a --- /dev/null +++ b/NFe.Danfe.PdfClown/Tools/Utils.cs @@ -0,0 +1,34 @@ +using System.Text.RegularExpressions; + +namespace NFe.Danfe.PdfClown.Tools +{ + public static class Utils + { + /// + /// Verifica se uma string contém outra string no formato chave: valor. + /// + public static bool StringContemChaveValor(string str, string chave, string valor) + { + if (string.IsNullOrWhiteSpace(chave)) throw new ArgumentException(nameof(chave)); + if (string.IsNullOrWhiteSpace(str)) return false; + + return Regex.IsMatch(str, $@"({chave}):?\s*{valor}", RegexOptions.IgnoreCase); + } + + public static string TipoDFeDeChaveAcesso(string chaveAcesso) + { + if (string.IsNullOrWhiteSpace(chaveAcesso)) throw new ArgumentException(nameof(chaveAcesso)); + + if (chaveAcesso.Length == 44) + { + string f = chaveAcesso.Substring(20, 2); + + if (f == "55") return "NF-e"; + else if (f == "57") return "CT-e"; + else if (f == "65") return "NFC-e"; + } + + return "DF-e Desconhecido"; + } + } +} diff --git a/NFe.Danfe.QuestPdf/Fontes/Arial.ttf b/NFe.Danfe.QuestPdf/Fontes/Arial.ttf new file mode 100644 index 0000000000000000000000000000000000000000..7ff88f22869126cc992030f18e0eeff65ec8bbac GIT binary patch literal 275572 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 YR_Tt=P6 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

L%Yj0kwC)JKzW*Z{;59WN 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≪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 NC1tZOiJIot{vZ5_jpG0S literal 0 HcmV?d00001 diff --git a/NFe.Danfe.QuestPdf/ImpressaoEventoNfe/EventoNfeDocument.cs b/NFe.Danfe.QuestPdf/ImpressaoEventoNfe/EventoNfeDocument.cs new file mode 100644 index 000000000..82103edb5 --- /dev/null +++ b/NFe.Danfe.QuestPdf/ImpressaoEventoNfe/EventoNfeDocument.cs @@ -0,0 +1,654 @@ +using System.Text; +using System.Text.RegularExpressions; +using BarcodeStandard; +using DFe.Utils; +using NFe.Classes; +using NFe.Classes.Servicos.Consulta; +using QuestPDF.Fluent; +using QuestPDF.Helpers; +using QuestPDF.Infrastructure; + +namespace NFe.Danfe.QuestPdf.ImpressaoEventoNfe; + +public class EventoNfeDocument : IDocument +{ + private nfeProc _nfeProc; + private NFe.Classes.NFe _nfe; + private procEventoNFe _procEventoNFe; + private readonly byte[]? _logo; + private static string _fontFamily = "Times New Roman"; + + public EventoNfeDocument(string xmlNfe, string xmlCce, byte[]? logo) + { + _logo = logo; + CarregarXmlNfe(xmlNfe); + CarregarXmlCce(xmlCce); + } + + public void Compose(IDocumentContainer container) + { + container + .Page(page => + { + page.Size(PageSizes.A4); + page.Margin(20); + + page.Header().Element(Cabecalho); + + page.Content().Element(Conteudo); + + page.Footer().Element(Rodape); + }); + } + + private void Cabecalho(IContainer container) + { + container.Column(column => + { + column.Item().Border(0.5f).Row(x => + { + x.RelativeItem().Column(c => + { + c.Item().AlignCenter().Text(_procEventoNFe.evento.infEvento.detEvento.descEvento).FontSize(14).FontFamily(_fontFamily).Bold(); + c.Item().AlignCenter().Text("Não possui valor fiscal. Simples representação do evento indicado abaixo.\r\nCONSULTE A AUTENTICIDADE NO SITE DA SEFAZ AUTORIZADORA").FontSize(10).FontFamily(_fontFamily); + }); + }); + + column.Item().PaddingTop(10); + + column.Item().Row(x => + { + x.RelativeItem().Column(c => + { + c.Item().AlignLeft().Text("NOTA FISCAL ELETRÔNICA - NF-e").FontSize(10).FontFamily(_fontFamily) + .Bold(); + }); + }); + + column.Item().Border(0.5f).Row(x => + { + x.RelativeItem().Table(t => + { + t.ColumnsDefinition(cd => + { + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + }); + + + t.Cell().Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("MODELO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignCenter().AlignTop().Text(_nfe.infNFe.ide.mod).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("SÉRIE").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignCenter().AlignTop().Text(_nfe.infNFe.ide.serie.ToString("D3")).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(2).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("NÚMERO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignCenter().AlignTop().Text(_nfe.infNFe.ide.nNF.ToString("D9")).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(2).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("MÊS/ANO DA EMISSÃO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignCenter().AlignTop().Text(_nfe.infNFe.ide.dhEmi.ToString("MM/yyyy")).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + var b = new Barcode(); + + b.Encode(BarcodeStandard.Type.Code128, _nfe.infNFe.Id.Substring(3), 300, 30); + + t.Cell().ColumnSpan(6).RowSpan(2).Border(0.5f).Padding(5).Image(b.GetImageData(SaveTypes.Png)); + + t.Cell().ColumnSpan(6).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("CHAVE DE ACESSO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignCenter().AlignTop().Text(_nfe.infNFe.Id.Substring(3)).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + }); + }); + + column.Item().PaddingTop(10); + + column.Item().Row(x => + { + x.RelativeItem().Column(c => + { + c.Item().AlignLeft().Text("CARTA DE CORREÇÃO ELETRÔNICA").FontSize(10).FontFamily(_fontFamily) + .Bold(); + }); + }); + + column.Item().Border(0.5f).Row(x => + { + x.RelativeItem().Table(t => + { + t.ColumnsDefinition(cd => + { + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + }); + + + t.Cell().Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("ÓRGÃO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignCenter().AlignTop().Text(_procEventoNFe.evento.infEvento.cOrgao).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(9).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("AMBIENTE").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_procEventoNFe.evento.infEvento.tpAmb).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(2).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("DATA E HORÁRIO DO EVENTO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignCenter().AlignTop().Text(_procEventoNFe.evento.infEvento.dhEvento.ToString("G")).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(2).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("EVENTO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignCenter().AlignTop().Text(_procEventoNFe.evento.infEvento.tpEvento).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(6).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("DESCRIÇÃO DO EVENTO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_procEventoNFe.evento.infEvento.detEvento.descEvento).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(2).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("SEQUÊNCIA DO EVENTO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignRight().AlignTop().PaddingRight(2).Text(_procEventoNFe.evento.infEvento.nSeqEvento).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(2).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("VERSÃO DO EVENTO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignRight().AlignTop().PaddingRight(2).Text(_procEventoNFe.evento.infEvento.verEvento).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(6).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("STATUS").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text($"{_procEventoNFe.retEvento.infEvento.cStat} - {_procEventoNFe.retEvento.infEvento.xMotivo}").FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(3).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("PROTOCOLO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignCenter().AlignTop().Text($"{_procEventoNFe.retEvento.infEvento.nProt}").FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(3).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("DATA E HORÁRIO DO REGISTRO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignCenter().AlignTop().Text($"{_procEventoNFe.retEvento.infEvento.dhRegEvento:G}").FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + }); + }); + }); + } + + private void Conteudo(IContainer container) + { + container.Column(column => + { + column.Item().PaddingTop(10); + + column.Item().Row(x => + { + x.RelativeItem().Column(c => + { + c.Item().AlignLeft().Text("EMITENTE").FontSize(10).FontFamily(_fontFamily) + .Bold(); + }); + }); + + column.Item().Border(0.5f).Row(x => + { + x.RelativeItem().Table(t => + { + t.ColumnsDefinition(cd => + { + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + }); + + + t.Cell().ColumnSpan(9).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("NOME / RAZÃO SOCIAL").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_nfe.infNFe.emit.xNome).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(3).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("CNPJ / CPF").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_nfe.infNFe.emit.CNPJ ?? _nfe.infNFe.emit.CPF).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(6).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("ENDEREÇO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text($"{_nfe.infNFe.emit.enderEmit.xLgr} {_nfe.infNFe.emit.enderEmit.nro} {_nfe.infNFe.emit.enderEmit.xCpl}").FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(4).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("BAIRRO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_nfe.infNFe.emit.enderEmit.xBairro).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(2).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("CEP").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_nfe.infNFe.emit.enderEmit.CEP).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(5).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("MUNICÍPIO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_nfe.infNFe.emit.enderEmit.xMun).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("ESTADO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignCenter().AlignTop().Text(_nfe.infNFe.emit.enderEmit.UF).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(3).Column(tx => + { + var temTelefone = _nfe.infNFe.emit.enderEmit.fone.HasValue; + + tx.Item().Border(temTelefone ? 0.5f : 0).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("FONE / FAX").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_nfe.infNFe.emit.enderEmit.fone).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(3).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("INSCRIÇÃO ESTADUAL").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_nfe.infNFe.emit.IE).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + }); + }); + + column.Item().PaddingTop(10); + + column.Item().Row(x => + { + x.RelativeItem().Column(c => + { + c.Item().AlignLeft().Text("DESTINATÁRIO / REMETENTE").FontSize(10).FontFamily(_fontFamily) + .Bold(); + }); + }); + + column.Item().Border(0.5f).Row(x => + { + x.RelativeItem().Table(t => + { + t.ColumnsDefinition(cd => + { + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + }); + + + t.Cell().ColumnSpan(9).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("NOME / RAZÃO SOCIAL").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_nfe.infNFe.dest.xNome).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(3).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("CNPJ / CPF").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_nfe.infNFe.dest.CNPJ ?? _nfe.infNFe.dest.CPF).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(6).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("ENDEREÇO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text($"{_nfe.infNFe.dest.enderDest.xLgr} {_nfe.infNFe.dest.enderDest.nro} {_nfe.infNFe.dest.enderDest.xCpl}").FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(4).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("BAIRRO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_nfe.infNFe.dest.enderDest.xBairro).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(2).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("CEP").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_nfe.infNFe.dest.enderDest.CEP).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(5).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("MUNICÍPIO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_nfe.infNFe.dest.enderDest.xMun).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("ESTADO").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignCenter().AlignTop().Text(_nfe.infNFe.dest.enderDest.UF).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(3).Column(tx => + { + var temTelefone = _nfe.infNFe.dest.enderDest.fone.HasValue; + + tx.Item().Border(temTelefone ? 0.5f : 0).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("FONE / FAX").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_nfe.infNFe.dest.enderDest.fone).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + + t.Cell().ColumnSpan(3).Column(tx => + { + tx.Item().Border(0.5f).Column(txc => + { + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text("INSCRIÇÃO ESTADUAL").FontSize(5).FontFamily(_fontFamily); + txc.Item().AlignLeft().AlignTop().PaddingLeft(2).Text(_nfe.infNFe.dest.IE).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + }); + }); + + column.Item().PaddingTop(10); + + column.Item().Row(x => + { + x.RelativeItem().Column(c => + { + c.Item().AlignLeft().Text("CONDIÇÕES DE USO").FontSize(10).FontFamily(_fontFamily) + .Bold(); + }); + }); + + column.Item().Border(0.5f).Row(x => + { + x.RelativeItem().Table(t => + { + t.ColumnsDefinition(cd => + { + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + }); + + + t.Cell().RowSpan(8).ColumnSpan(12).Column(tx => + { + tx.Item().Border(0.5f).PaddingLeft(2).Text(Regex.Replace(_procEventoNFe.evento.infEvento.detEvento.xCondUso.Replace("\r", string.Empty).Replace("\n", ""), @"\s+", " ")).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + }); + + column.Item().PaddingTop(10); + + column.Item().Row(x => + { + x.RelativeItem().Column(c => + { + c.Item().AlignLeft().Text(_procEventoNFe.evento.infEvento.detEvento.descEvento).FontSize(10).FontFamily(_fontFamily) + .Bold(); + }); + }); + + column.Item().Border(0.5f).Row(x => + { + x.RelativeItem().Table(t => + { + t.ColumnsDefinition(cd => + { + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + cd.RelativeColumn(); + }); + + + t.Cell().RowSpan(8).ColumnSpan(12).Column(tx => + { + var texto = new StringBuilder(); + + if (_procEventoNFe.evento.infEvento.detEvento.xJust != null) + { + texto.Append(Regex.Replace( + _procEventoNFe.evento.infEvento.detEvento.xJust.Replace("\r", string.Empty) + .Replace("\n", ""), @"\s+", " ")); + + texto.Append("\n"); + } + + if (_procEventoNFe.evento.infEvento.detEvento.xCorrecao != null) + { + texto.Append(Regex.Replace( + _procEventoNFe.evento.infEvento.detEvento.xCorrecao.Replace("\r", string.Empty) + .Replace("\n", ""), @"\s+", " ")); + } + + tx.Item().Border(0.5f).PaddingLeft(2).Text(texto).FontSize(9).FontFamily(_fontFamily).Bold(); + }); + }); + }); + }); + } + + private void Rodape(IContainer container) + { + container.Column(column => + { + column.Item().Border(0.5f).Row(x => + { + x.RelativeItem().Column(c => + { + c.Item().AlignLeft().Text($"Data e hora da impressão {DateTime.UtcNow:G}").FontSize(6).FontFamily(_fontFamily); + }); + }); + }); + } + + public DocumentMetadata GetMetadata() => DocumentMetadata.Default; + + private void CarregarXmlNfe(string xmlNfe) + { + try + { + _nfeProc = FuncoesXml.XmlStringParaClasse(xmlNfe); + _nfe = _nfeProc.NFe; + } + catch (Exception) + { + try + { + NFe.Classes.NFe nfe = FuncoesXml.XmlStringParaClasse(xmlNfe); + _nfe = nfe; + } + catch (Exception) + { + throw new ArgumentException( + "Ei! Verifique se seu xml da NF-e ou NF-e Proc não está correto, pois identificamos uma falha ao tentar carregar ele."); + } + } + } + + private void CarregarXmlCce(string xmlCce) + { + try + { + _procEventoNFe = FuncoesXml.XmlStringParaClasse(xmlCce); + } + catch (Exception) + { + throw new ArgumentException( + "Ei! Verifique se seu xml da Carta Correção não está correto, pois identificamos uma falha ao tentar carregar ele."); + } + } +} \ No newline at end of file diff --git a/NFe.Danfe.QuestPdf/ImpressaoNfce/DanfeNfceDocument.cs b/NFe.Danfe.QuestPdf/ImpressaoNfce/DanfeNfceDocument.cs new file mode 100644 index 000000000..d4f24c973 --- /dev/null +++ b/NFe.Danfe.QuestPdf/ImpressaoNfce/DanfeNfceDocument.cs @@ -0,0 +1,564 @@ +using System.Text; +using DFe.Classes.Flags; +using DFe.Utils; +using NFe.Classes; +using NFe.Classes.Informacoes.Destinatario; +using NFe.Classes.Informacoes.Pagamento; +using NFe.Utils; +using NFe.Utils.InformacoesSuplementares; +using QuestPDF.Fluent; +using QuestPDF.Helpers; +using QuestPDF.Infrastructure; +using SkiaSharp; +using SkiaSharp.QrCode.Image; + +namespace NFe.Danfe.QuestPdf.ImpressaoNfce; + +public class DanfeNfceDocument : IDocument +{ + private readonly byte[]? _logo; + private nfeProc? _nfeProc; + private NFe.Classes.NFe? _nfe; + private float _tamanhoImpressao = 300f; + private float _tamanhoFontePadrao; + + public DanfeNfceDocument(string xml, byte[]? logo) + { + _logo = logo; + CarregarXml(xml); + } + + public void TamanhoImpressao(TamanhoImpressao tamanhoImpressao) + { + switch (tamanhoImpressao) + { + case ImpressaoNfce.TamanhoImpressao.Impressao80: + _tamanhoImpressao = 8f; + _tamanhoFontePadrao = 8f; + break; + case ImpressaoNfce.TamanhoImpressao.Impressao72: + _tamanhoImpressao = 7.2f; + _tamanhoFontePadrao = 8f; + break; + case ImpressaoNfce.TamanhoImpressao.Impressao50: + _tamanhoImpressao = 5f; + _tamanhoFontePadrao = 5f; + break; + default: + throw new ArgumentOutOfRangeException(nameof(tamanhoImpressao), tamanhoImpressao, null); + } + } + + public DocumentMetadata GetMetadata() => DocumentMetadata.Default; + + public void Compose(IDocumentContainer container) + { + container + .Page(page => + { + page.MarginLeft(0.4f, Unit.Centimetre); + page.MarginRight(0.8f, Unit.Centimetre); + page.MarginTop(0); + page.MarginBottom(0); + page.ContinuousSize(_tamanhoImpressao, Unit.Centimetre); + + page.Header().Element(Cabecalho); + + page.Content().Element(Conteudo); + + page.Footer().Element(Rodape); + }); + } + + private void Cabecalho(IContainer container) + { + container.Row(row => + { + row.RelativeItem().Column(column => + { + if (_logo != null) + { + column.Item().AlignCenter().MaxWidth(60).MaxHeight(60).Image(_logo); + } + column.Item().AlignCenter().Text(_nfe.infNFe.emit.xFant).FontSize(_tamanhoFontePadrao + 2).SemiBold(); + column.Item().AlignCenter().Text(_nfe.infNFe.emit.xNome).FontSize(_tamanhoFontePadrao + 2); + column.Item().Row(r => + { + r.RelativeItem().AlignLeft().Column(c => + { + c.Item().Text($"CNPJ: {_nfe.infNFe.emit.CNPJ}").FontSize(_tamanhoFontePadrao); + }); + + r.RelativeItem().AlignRight().Column(c => + { + c.Item().Text($"IE: {_nfe.infNFe.emit.IE}").FontSize(_tamanhoFontePadrao); + }); + }); + column.Item().Row(r => + { + r.RelativeItem().AlignLeft().Column(c => + { + c.Item().Text($"Endereço: {EnderecoEmitente()}").FontSize(_tamanhoFontePadrao).Italic(); + }); + }); + + if (_nfe.infNFe.ide.tpAmb == TipoAmbiente.Homologacao) + { + column.Item().LineHorizontal(1); + column.Item().Row(r => + { + r.RelativeItem().AlignCenter().Column(c => + { + c.Item().AlignCenter().Text("EMITIDO EM AMBIENTE DE HOMOLOGAÇÃO SEM VALOR FISCAL").FontSize(_tamanhoFontePadrao).SemiBold().Underline(); + }); + }); + column.Item().LineHorizontal(1); + } + + column.Item().LineHorizontal(1); + column.Item().Row(r => + { + r.RelativeItem().AlignCenter().Column(c => + { + c.Item().AlignCenter().Text("DANFE NFC-e").FontSize(_tamanhoFontePadrao).ExtraBlack(); + c.Item().AlignCenter().Text("Documento Auxiliar Da Nota Fiscal de Consumidor Eletrônica").FontSize(_tamanhoFontePadrao - 1); + c.Item().AlignCenter().Text("Não permite aproveitamento de crédito do ICMS").FontSize(_tamanhoFontePadrao - 1); + }); + }); + column.Item().LineHorizontal(1); + }); + }); + } + + private void Conteudo(IContainer container) + { + container.Table(t => + { + t.ColumnsDefinition(c => + { + c.RelativeColumn(); + c.RelativeColumn(); + c.RelativeColumn(); + c.RelativeColumn(); + c.RelativeColumn(); + c.RelativeColumn(); + c.RelativeColumn(); + c.RelativeColumn(); + c.RelativeColumn(); + c.RelativeColumn(); + c.RelativeColumn(); + c.RelativeColumn(); + }); + + t.Header(h => + { + h.Cell().Element(CellStyle).Text("Num.").ExtraBlack(); + h.Cell().ColumnSpan(11).Element(CellStyle).Text("Descrição").ExtraBlack(); + h.Cell().RowSpan(1); + h.Cell().ColumnSpan(2).Element(CellStyle).AlignRight().Text("Qtde Un ").ExtraBlack(); + h.Cell().ColumnSpan(1).Element(CellStyle).AlignCenter().Text("X").ExtraBlack(); + h.Cell().ColumnSpan(4).Element(CellStyle).AlignRight().Text("Valor Unitário").ExtraBlack(); + h.Cell().ColumnSpan(1).Element(CellStyle).AlignCenter().Text("=").ExtraBlack(); + h.Cell().ColumnSpan(3).Element(CellStyle).AlignRight().AlignRight().Text("Valor Total").ExtraBlack(); + h.Cell().RowSpan(1); + + IContainer CellStyle(IContainer container) + { + return container.DefaultTextStyle(x => + x.SemiBold() + .FontSize(_tamanhoFontePadrao - 0.5f) + ) + .BorderColor(Colors.Black); + } + }); + + t.Cell().ColumnSpan(12).LineHorizontal(1).LineColor(Colors.Grey.Medium); + + foreach (var det in _nfe.infNFe.det) + { + t.Cell().Text(det.nItem.ToString("D3")).FontSize(_tamanhoFontePadrao - 0.5f); + t.Cell().ColumnSpan(11).Text(det.prod.xProd).FontSize(_tamanhoFontePadrao - 0.5f); + t.Cell().RowSpan(1); + t.Cell().ColumnSpan(2).AlignRight().Text(det.prod.qCom.ToString("N3")).FontSize(_tamanhoFontePadrao - 0.5f); + t.Cell().ColumnSpan(1).AlignCenter(); + t.Cell().ColumnSpan(4).AlignRight().Text(det.prod.vUnCom.ToString("N2")).FontSize(_tamanhoFontePadrao - 0.5f); + t.Cell().ColumnSpan(1).AlignCenter(); + t.Cell().ColumnSpan(3).AlignRight().Text(det.prod.vProd.ToString("N2")).FontSize(_tamanhoFontePadrao - 0.5f); + t.Cell().ColumnSpan(12).RowSpan(1).Element(CellStyle); + } + + t.Footer(footer => + { + footer.Cell().LineHorizontal(1); + footer.Cell().LineHorizontal(1); + footer.Cell().LineHorizontal(1); + footer.Cell().LineHorizontal(1); + footer.Cell().LineHorizontal(1); + footer.Cell().LineHorizontal(1); + footer.Cell().LineHorizontal(1); + footer.Cell().LineHorizontal(1); + footer.Cell().LineHorizontal(1); + footer.Cell().LineHorizontal(1); + footer.Cell().LineHorizontal(1); + footer.Cell().LineHorizontal(1); + }); + static IContainer CellStyle(IContainer container) + { + return container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(0); + } + }); + } + + private void Rodape(IContainer container) + { + container.Row(row => + { + row.RelativeItem().Column(column => + { + column.Item().Row(r => + { + r.RelativeItem().AlignLeft().Column(c => + { + c.Item().Text("Quantidade de itens").FontSize(_tamanhoFontePadrao); + }); + + r.RelativeItem().AlignRight().Column(c => + { + c.Item().Text($"{_nfe.infNFe.det.Count}").FontSize(_tamanhoFontePadrao); + }); + }); + + column.Item().Row(r => + { + r.RelativeItem().AlignLeft().Column(c => + { + c.Item().Text("Total Desconto").FontSize(_tamanhoFontePadrao); + }); + + r.RelativeItem().AlignRight().Column(c => + { + c.Item().Text($"R$ {_nfe.infNFe.total.ICMSTot.vDesc:N2}").FontSize(_tamanhoFontePadrao); + }); + }); + + column.Item().Row(r => + { + r.RelativeItem().AlignLeft().Column(c => + { + c.Item().Text("Total Outros").FontSize(_tamanhoFontePadrao); + }); + + r.RelativeItem().AlignRight().Column(c => + { + c.Item().Text($"R$ {_nfe.infNFe.total.ICMSTot.vOutro:N2}").FontSize(_tamanhoFontePadrao); + }); + }); + + column.Item().Row(r => + { + r.RelativeItem().AlignLeft().Column(c => + { + c.Item().Text("Total Cupom").FontSize(_tamanhoFontePadrao).ExtraBlack(); + }); + + r.RelativeItem().AlignRight().Column(c => + { + c.Item().Text($"R$ {_nfe.infNFe.total.ICMSTot.vNF:N2}").FontSize(_tamanhoFontePadrao).ExtraBlack(); + }); + }); + + column.Item().Row(r => + { + r.RelativeItem().AlignLeft().Column(c => + { + c.Item().Text("Tributos Totais Incidentes (Lei Federal 12.741/2012)").FontSize(_tamanhoFontePadrao); + }); + + r.RelativeItem().AlignRight().Column(c => + { + c.Item().Text($"R$ {_nfe.infNFe.total.ICMSTot.vTotTrib:N2}").FontSize(_tamanhoFontePadrao); + }); + }); + + column.Item().LineHorizontal(1); + + column.Item().Row(r => + { + + r.RelativeItem().AlignLeft().Column(c => + { + c.Item().Text("Forma Pagamento").FontSize(_tamanhoFontePadrao); + }); + + r.RelativeItem().AlignRight().Column(c => + { + c.Item().Text("Valor Pago").FontSize(_tamanhoFontePadrao); + }); + }); + + foreach (pag pag in _nfe.infNFe.pag) + { + if (pag.detPag != null) + { + foreach (var detPag in pag.detPag) + { + column.Item().Row(r => + { + + r.RelativeItem().AlignLeft().Column(c => + { + c.Item().Text(ObtemDescricao(detPag.tPag)).FontSize(_tamanhoFontePadrao); + }); + + r.RelativeItem().AlignRight().Column(c => + { + c.Item().Text($"{detPag.vPag:N2}").FontSize(_tamanhoFontePadrao); + }); + }); + } + } + } + + column.Item().LineHorizontal(1); + + column.Item().Row(r => + { + r.RelativeItem().AlignCenter().Column(c => + { + c.Item().Text("Consulte pela chave de acesso em:").FontSize(_tamanhoFontePadrao); + }); + }); + + column.Item().Row(r => + { + r.RelativeItem().AlignCenter().Column(c => + { + c.Item().Text(string.IsNullOrEmpty(_nfe.infNFeSupl.urlChave) ? _nfe.infNFeSupl.ObterUrlConsulta(_nfe, VersaoQrCode.QrCodeVersao2) : _nfe.infNFeSupl.urlChave).FontSize(_tamanhoFontePadrao); + }); + }); + + column.Item().LineHorizontal(1); + + column.Item().Row(r => + { + r.RelativeItem().AlignCenter().Column(c => + { + c.Item().AlignCenter().Text(_nfe.infNFe.Id.Substring(3)).FontSize(_tamanhoFontePadrao); + }); + }); + + column.Item().LineHorizontal(1); + + column.Item().Row(r => + { + r.RelativeItem().AlignCenter().Column(c => + { + c.Item().AlignCenter().Text(MontaMensagemConsumidor()).FontSize(_tamanhoFontePadrao).ExtraBlack(); + }); + }); + + column.Item().LineHorizontal(1); + + column.Item().Row(r => + { + r.RelativeItem().Table(t => + { + t.ColumnsDefinition(c => + { + c.RelativeColumn(2); + c.RelativeColumn(3); + }); + + t.Cell().AlignCenter().Image(ImagemQrCode()); + t.Cell().AlignLeft().Column(c => + { + c.Item().Text($"Série: {_nfe.infNFe.ide.serie:D3}").FontSize(_tamanhoFontePadrao); + c.Item().Text($"Número: {_nfe.infNFe.ide.nNF:D9}").FontSize(_tamanhoFontePadrao); + c.Item().Text($"Emissão: {_nfeProc.NFe.infNFe.ide.dhEmi:G}").FontSize(_tamanhoFontePadrao); + c.Item().Text($"Protocolo: {_nfeProc.protNFe.infProt.nProt}").FontSize(_tamanhoFontePadrao); + c.Item().Text($"Autorização: {_nfeProc.protNFe.infProt.dhRecbto:G}").FontSize(_tamanhoFontePadrao); + }); + }); + }); + + column.Item().LineHorizontal(1); + + column.Item().Row(r => + { + r.RelativeItem().AlignCenter().Column(c => + { + c.Item().AlignCenter().Text("Nota Fiscal de Consumidor Eletrônica").FontSize(_tamanhoFontePadrao).ExtraBlack(); + }); + }); + + column.Item().LineHorizontal(1); + }); + }); + } + + private byte[] ImagemQrCode() + { + using var memoryStream = new MemoryStream(); + var qrCode = new QrCode(ObtemUrlQrCode(), new Vector2Slim(256, 256), SKEncodedImageFormat.Png); + qrCode.GenerateImage(memoryStream); + + var qrCodeBytes = memoryStream.ToArray(); + + return qrCodeBytes; + } + + private string ObtemUrlQrCode() + { + var urlQrCode = _nfe.infNFeSupl.qrCode; + + return urlQrCode; + } + + private string MontaMensagemConsumidor() + { + var dest = _nfe.infNFe.dest; + + var mensagem = new StringBuilder("CONSUMIDOR "); + + if (dest == null || string.IsNullOrEmpty(dest.CPF) && string.IsNullOrEmpty(dest.CNPJ)) + { + mensagem.Append("NÃO IDENTIFICADO"); + return mensagem.ToString(); + } + + if (!string.IsNullOrEmpty(dest.idEstrangeiro)) + { + mensagem.Append("Id "); + mensagem.Append(dest.idEstrangeiro); + } + + + if (!string.IsNullOrEmpty(dest.CPF)) + { + mensagem.Append("CPF "); + mensagem.Append(dest.CPF); + } + + if (!string.IsNullOrEmpty(dest.CNPJ)) + { + mensagem.Append("CNPJ "); + mensagem.Append(dest.CNPJ); + } + + if (!string.IsNullOrEmpty(dest.xNome)) + { + mensagem.Append(" "); + mensagem.Append(dest.xNome); + } + + enderDest enderecoDest = dest.enderDest; + + if (enderecoDest == null) return mensagem.ToString().Replace(", ,", ", "); + + string rua = string.Empty; + if (!string.IsNullOrEmpty(enderecoDest.xLgr)) + rua = enderecoDest.xLgr; + + string numero = "S/N"; + if (!string.IsNullOrEmpty(enderecoDest.nro)) + numero = enderecoDest.nro; + + var bairro = string.Empty; + if (!string.IsNullOrEmpty(enderecoDest.xBairro)) + bairro = enderecoDest.xBairro; + + var cidade = string.Empty; + if (!string.IsNullOrEmpty(enderecoDest.xMun)) + bairro = enderecoDest.xMun; + + var siglaUf = string.Empty; + if (!string.IsNullOrEmpty(enderecoDest.UF)) + bairro = enderecoDest.UF; + + if (string.IsNullOrEmpty(rua)) return mensagem.ToString(); + mensagem.Append(" - "); + mensagem.Append(rua); + mensagem.Append(", "); + mensagem.Append(numero); + mensagem.Append(", "); + mensagem.Append(bairro); + mensagem.Append(", "); + mensagem.Append(cidade); + mensagem.Append(" - "); + mensagem.Append(siglaUf); + + return mensagem.ToString().Replace(", ,", ", "); + } + + private string EnderecoEmitente() + { + var enderEmit = _nfe.infNFe.emit.enderEmit; + var foneEmit = string.Empty; + + if (enderEmit.fone != null) + { + StringBuilder fone = new StringBuilder(" - FONE: "); + fone.Append(enderEmit.fone); + foneEmit = fone.ToString(); + } + + var enderecoEmitenteBuilder = new StringBuilder(); + enderecoEmitenteBuilder.Append(enderEmit.xLgr); + enderecoEmitenteBuilder.Append(" "); + + if (string.IsNullOrEmpty(enderEmit.nro)) + { + enderecoEmitenteBuilder.Append("S/N, "); + } + + if (!string.IsNullOrEmpty(enderEmit.nro)) + { + enderecoEmitenteBuilder.Append(enderEmit.nro); + enderecoEmitenteBuilder.Append(", "); + } + + enderecoEmitenteBuilder.Append(enderEmit.xBairro); + enderecoEmitenteBuilder.Append(", "); + enderecoEmitenteBuilder.Append(enderEmit.xMun); + enderecoEmitenteBuilder.Append(", "); + enderecoEmitenteBuilder.Append(enderEmit.UF); + enderecoEmitenteBuilder.Append(foneEmit); + + return enderecoEmitenteBuilder.ToString(); + } + + private void CarregarXml(string xml) + { + try + { + _nfeProc = FuncoesXml.XmlStringParaClasse(xml); + _nfe = _nfeProc.NFe; + } + catch (Exception) + { + try + { + NFe.Classes.NFe nfe = FuncoesXml.XmlStringParaClasse(xml); + _nfe = nfe; + } + catch (Exception) + { + throw new ArgumentException( + "Ei! Verifique se seu xml está correto, pois identificamos uma falha ao tentar carregar ele."); + } + } + } + + private string ObtemDescricao(FormaPagamento? formaPagamento) + { + var existeEnum = Enum + .GetValues(typeof(FormaPagamento)) + .Cast() + .Any(p => formaPagamento.HasValue && p.Equals(formaPagamento)); + + if (existeEnum) + return formaPagamento.Descricao(); + else + throw new ArgumentException("Forma pagamento inválida"); + } +} \ No newline at end of file diff --git a/NFe.Danfe.QuestPdf/ImpressaoNfce/TamanhoImpressao.cs b/NFe.Danfe.QuestPdf/ImpressaoNfce/TamanhoImpressao.cs new file mode 100644 index 000000000..0585aca52 --- /dev/null +++ b/NFe.Danfe.QuestPdf/ImpressaoNfce/TamanhoImpressao.cs @@ -0,0 +1,15 @@ +using System.ComponentModel; + +namespace NFe.Danfe.QuestPdf.ImpressaoNfce; + +public enum TamanhoImpressao +{ + [Description("Impressão 80mm")] + Impressao80 = 1, + + [Description("Impressão 72mm")] + Impressao72 = 2, + + [Description("Impressão 50mm")] + Impressao50 = 3 +} \ No newline at end of file diff --git a/NFe.Danfe.QuestPdf/Models/EmpresaModel.cs b/NFe.Danfe.QuestPdf/Models/EmpresaModel.cs new file mode 100644 index 000000000..f4d954c55 --- /dev/null +++ b/NFe.Danfe.QuestPdf/Models/EmpresaModel.cs @@ -0,0 +1,7 @@ +namespace NFe.Danfe.QuestPdf.Models; + +public class EmpresaModel +{ + public string RazaoSocial { get; set; } + public string Cnpj { get; set; } +} \ No newline at end of file diff --git a/NFe.Danfe.QuestPdf/Models/NfceInutilizacaoModel.cs b/NFe.Danfe.QuestPdf/Models/NfceInutilizacaoModel.cs new file mode 100644 index 000000000..53f0e81c6 --- /dev/null +++ b/NFe.Danfe.QuestPdf/Models/NfceInutilizacaoModel.cs @@ -0,0 +1,10 @@ +namespace NFe.Danfe.QuestPdf.Models; + +public class NfceInutilizacaoModel +{ + public DateTime InutilizacaoEm { get; set; } + public string DescricaoModelo { get; set; } + public int Serie { get; set; } + public int NumeroInicial { get; set; } + public int NumeroFinal { get; set; } +} \ No newline at end of file diff --git a/NFe.Danfe.QuestPdf/Models/NfceResumidaModel.cs b/NFe.Danfe.QuestPdf/Models/NfceResumidaModel.cs new file mode 100644 index 000000000..0004d773e --- /dev/null +++ b/NFe.Danfe.QuestPdf/Models/NfceResumidaModel.cs @@ -0,0 +1,11 @@ +namespace NFe.Danfe.QuestPdf.Models; + +public class NfceResumidaModel +{ + public DateTime AutorizacaoEm { get; set; } + public string Situacao { get; set; } + public int Serie { get; set; } + public int NumeroFiscal { get; set; } + public string Chave { get; set; } + public decimal ValorTotal { get; set; } +} \ No newline at end of file diff --git a/NFe.Danfe.QuestPdf/Models/NfeInutilizadaModel.cs b/NFe.Danfe.QuestPdf/Models/NfeInutilizadaModel.cs new file mode 100644 index 000000000..e26fc3f1c --- /dev/null +++ b/NFe.Danfe.QuestPdf/Models/NfeInutilizadaModel.cs @@ -0,0 +1,10 @@ +namespace NFe.Danfe.QuestPdf.Models; + +public class NfeInutilizadaModel +{ + public DateTime InutilizacaoEm { get; set; } + public string DescricaoModelo { get; set; } + public int Serie { get; set; } + public int NumeroInicial { get; set; } + public int NumeroFinal { get; set; } +} \ No newline at end of file diff --git a/NFe.Danfe.QuestPdf/Models/NfeResumidaModel.cs b/NFe.Danfe.QuestPdf/Models/NfeResumidaModel.cs new file mode 100644 index 000000000..ad9cf37ed --- /dev/null +++ b/NFe.Danfe.QuestPdf/Models/NfeResumidaModel.cs @@ -0,0 +1,11 @@ +namespace NFe.Danfe.QuestPdf.Models; + +public class NfeResumidaModel +{ + public DateTime AutorizacaoEm { get; set; } + public string Situacao { get; set; } + public int Serie { get; set; } + public int NumeroFiscal { get; set; } + public string Chave { get; set; } + public decimal ValorTotal { get; set; } +} \ No newline at end of file diff --git a/NFe.Danfe.QuestPdf/Models/PeriodoModel.cs b/NFe.Danfe.QuestPdf/Models/PeriodoModel.cs new file mode 100644 index 000000000..e25c0a719 --- /dev/null +++ b/NFe.Danfe.QuestPdf/Models/PeriodoModel.cs @@ -0,0 +1,7 @@ +namespace NFe.Danfe.QuestPdf.Models; + +public class PeriodoModel +{ + public DateOnly DataInicial { get; set; } + public DateOnly DataFinal { get; set; } +} \ No newline at end of file diff --git a/NFe.Danfe.QuestPdf/Models/RelatorioFiscalEmissoesNfceModel.cs b/NFe.Danfe.QuestPdf/Models/RelatorioFiscalEmissoesNfceModel.cs new file mode 100644 index 000000000..a5793affb --- /dev/null +++ b/NFe.Danfe.QuestPdf/Models/RelatorioFiscalEmissoesNfceModel.cs @@ -0,0 +1,20 @@ +namespace NFe.Danfe.QuestPdf.Models; + +public class RelatorioFiscalEmissoesNfceModel +{ + public EmpresaModel EmpresaModel { get; set; } + public PeriodoModel PeriodoModel { get; set; } + public IEnumerable NfceResumidaModels { get; set; } + public IEnumerable NfceInutilizacaoModels { get; set; } + + public decimal QuantidadeNfceAutorizada => NfceResumidaModels.Count(n => n.Situacao == "Autorizada"); + public decimal ValorTotalNfceAutorizada => NfceResumidaModels.Where(n => n.Situacao == "Autorizada") + .Sum(n => n.ValorTotal); + public decimal QuantidadeNfceCancelada => NfceResumidaModels.Count(n => n.Situacao == "Cancelada"); + public decimal ValorTotalNfceCancelada => NfceResumidaModels.Where(n => n.Situacao == "Cancelada") + .Sum(n => n.ValorTotal); + public decimal QuantidadeNfceDenegada => NfceResumidaModels.Count(n => n.Situacao == "Denegada"); + + public decimal ValorTotalNfceDenegada => NfceResumidaModels.Where(n => n.Situacao == "Denegada") + .Sum(n => n.ValorTotal); +} \ No newline at end of file diff --git a/NFe.Danfe.QuestPdf/Models/RelatorioFiscalEmissoesNfeModel.cs b/NFe.Danfe.QuestPdf/Models/RelatorioFiscalEmissoesNfeModel.cs new file mode 100644 index 000000000..e9f4f2f5f --- /dev/null +++ b/NFe.Danfe.QuestPdf/Models/RelatorioFiscalEmissoesNfeModel.cs @@ -0,0 +1,20 @@ +namespace NFe.Danfe.QuestPdf.Models; + +public class RelatorioFiscalEmissoesNfeModel +{ + public EmpresaModel EmpresaModel { get; set; } + public PeriodoModel PeriodoModel { get; set; } + public IEnumerable NfeResumidaModels { get; set; } + public IEnumerable NfeInutilizacaoModels { get; set; } + + public decimal QuantidadeNfeAutorizada => NfeResumidaModels.Count(n => n.Situacao == "Autorizada"); + public decimal ValorTotalNfeAutorizada => NfeResumidaModels.Where(n => n.Situacao == "Autorizada") + .Sum(n => n.ValorTotal); + public decimal QuantidadeNfeCancelada => NfeResumidaModels.Count(n => n.Situacao == "Cancelada"); + public decimal ValorTotalNfeCancelada => NfeResumidaModels.Where(n => n.Situacao == "Cancelada") + .Sum(n => n.ValorTotal); + public decimal QuantidadeNfeDenegada => NfeResumidaModels.Count(n => n.Situacao == "Denegada"); + + public decimal ValorTotalNfeDenegada => NfeResumidaModels.Where(n => n.Situacao == "Denegada") + .Sum(n => n.ValorTotal); +} \ No newline at end of file diff --git a/NFe.Danfe.QuestPdf/NFe.Danfe.QuestPdf.csproj b/NFe.Danfe.QuestPdf/NFe.Danfe.QuestPdf.csproj new file mode 100644 index 000000000..26a09d3fa --- /dev/null +++ b/NFe.Danfe.QuestPdf/NFe.Danfe.QuestPdf.csproj @@ -0,0 +1,28 @@ + + + + net8.0 + enable + enable + + + + + + + + + Always + + + + + + + + + + + + + diff --git a/NFe.Danfe.QuestPdf/ParaContadores/RelatorioFiscalEmissoesNfceDocument.cs b/NFe.Danfe.QuestPdf/ParaContadores/RelatorioFiscalEmissoesNfceDocument.cs new file mode 100644 index 000000000..a864f2b85 --- /dev/null +++ b/NFe.Danfe.QuestPdf/ParaContadores/RelatorioFiscalEmissoesNfceDocument.cs @@ -0,0 +1,285 @@ +using NFe.Danfe.QuestPdf.Models; +using QuestPDF.Fluent; +using QuestPDF.Helpers; +using QuestPDF.Infrastructure; + +namespace NFe.Danfe.QuestPdf.ParaContadores; + +public class RelatorioFiscalEmissoesNfceDocument : IDocument +{ + private static string Fonte = "Arial"; + private RelatorioFiscalEmissoesNfceModel Model; + + public RelatorioFiscalEmissoesNfceDocument(RelatorioFiscalEmissoesNfceModel model) + { + Model = model; + } + + public DocumentMetadata GetMetadata() => DocumentMetadata.Default; + + public void Compose(IDocumentContainer container) + { + container + .Page(page => + { + page.Size(PageSizes.A4); + page.Margin(20); + + page.Header().Element(Cabecalho); + + + page.Content().Element(Conteudo); + + + page.Footer().AlignCenter().Text(x => + { + x.CurrentPageNumber(); + x.Span(" / "); + x.TotalPages(); + }); + }); + } + + private void Cabecalho(IContainer container) + { + var titleStyle = TextStyle.Default.FontSize(11).FontFamily(Fonte); + + container.Row(row => + { + row.RelativeItem().Column(column => + { + column.Item().AlignRight().Text("Relatório fiscal de emissões na NFC-e").FontSize(12).FontFamily(Fonte).SemiBold(); + column.Item().AlignRight().Text($"Impresso em: {GetMetadata().CreationDate}").FontSize(10).FontFamily(Fonte).Italic().SemiBold(); + column.Item().AlignLeft().Text($"Razão Social: {Model.EmpresaModel.RazaoSocial}").Style(titleStyle); + column.Item().AlignLeft().Text($"CNPJ: {Model.EmpresaModel.Cnpj}").Style(titleStyle); + column.Item().AlignLeft().Text($"Período: {Model.PeriodoModel.DataInicial:d} até {Model.PeriodoModel.DataFinal:d}"); + }); + }); + } + + private void Conteudo(IContainer container) + { + void TituloInicioTabela(ColumnDescriptor column, string titulo) + { + column.Item() + .PaddingTop(15) + .AlignCenter() + .Text(titulo) + .FontSize(9) + .Italic() + .SemiBold() + .FontFamily(Fonte); + } + + + container + .PaddingVertical(10) + .Column(column => + { + TituloInicioTabela(column, "Documento Fiscal Modelo 65 - NFCE"); + + column.Item().Element(TabelaNFCeResumida); + + TituloInicioTabela(column, "Inutilização feitas no periodo"); + + column.Item().Element(TabelaInutilizacoes); + + column.Item() + .PaddingTop(15) + .AlignCenter(); + + ResumoTotais(column); + }); + } + + private void TabelaNFCeResumida(IContainer container) + { + container.Table(table => + { + table.ColumnsDefinition(columns => + { + columns.ConstantColumn(80); + columns.ConstantColumn(50); + columns.ConstantColumn(30); + columns.ConstantColumn(55); + columns.RelativeColumn(); + columns.ConstantColumn(55); + }); + + + table.Header(header => + { + header.Cell().Element(CellStyle).Text("Data Autorização"); + header.Cell().Element(CellStyle).Text("Situação"); + header.Cell().Element(CellStyle).Text("Série"); + header.Cell().Element(CellStyle).Text("Número"); + header.Cell().Element(CellStyle).Text("Chave"); + header.Cell().Element(CellStyle).AlignRight().Text("Valo Total"); + + + static IContainer CellStyle(IContainer container) + { + return container.DefaultTextStyle(x => + x.SemiBold() + .FontSize(9) + .FontFamily(Fonte) + ) + .PaddingVertical(5) + .BorderBottom(1) + .BorderColor(Colors.Black); + } + }); + + foreach (var item in Model.NfceResumidaModels) + { + table.Cell().Element(CellStyle).Text(item.AutorizacaoEm.ToString("g")).Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text(item.Situacao).Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text($"{item.Serie:D3}").Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text($"{item.NumeroFiscal:D9}").Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text($"{item.Chave}").Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).AlignRight().Text($"{item.ValorTotal:C}").Style(EstiloTextoColunas()); + + static IContainer CellStyle(IContainer container) + { + return container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(0); + } + + static TextStyle EstiloTextoColunas() + { + return TextStyle.Default.FontSize(8.5f).FontFamily(Fonte); + } + } + + table.Footer(footer => + { + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + }); + }); + } + + private void TabelaInutilizacoes(IContainer container) + { + container.Table(table => + { + table.ColumnsDefinition(columns => + { + columns.RelativeColumn(); + columns.RelativeColumn(); + columns.RelativeColumn(); + columns.RelativeColumn(); + columns.RelativeColumn(); + }); + + table.Header(header => + { + header.Cell().Element(CellStyle).Text("Data Inutilização"); + header.Cell().Element(CellStyle).Text("Modelo"); + header.Cell().Element(CellStyle).Text("Série"); + header.Cell().Element(CellStyle).Text("Número Inicial"); + header.Cell().Element(CellStyle).Text("Número Final"); + + + static IContainer CellStyle(IContainer container) + { + return container.DefaultTextStyle(x => x.SemiBold().FontSize(9).FontFamily(Fonte)) + .PaddingVertical(5) + .BorderBottom(1) + .BorderColor(Colors.Black); + } + }); + + foreach (var item in Model.NfceInutilizacaoModels) + { + table.Cell().Element(CellStyle).Text(item.InutilizacaoEm.ToString("g")).Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text(item.DescricaoModelo).Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text($"{item.Serie:D3}").Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text($"{item.NumeroInicial:D9}").Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text($"{item.NumeroFinal:D9}").Style(EstiloTextoColunas()); + + static IContainer CellStyle(IContainer container) + { + return container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(0); + } + + static TextStyle EstiloTextoColunas() + { + return TextStyle.Default.FontSize(8.5f).FontFamily(Fonte); + } + } + + table.Footer(footer => + { + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + }); + }); + } + + private void ResumoTotais(ColumnDescriptor column) + { + column.Item().Row(row => + { + row.RelativeItem(7) + .Border(0.3f) + .Text("Resumo das saidas").FontSize(10).FontFamily(Fonte).SemiBold().Italic(); + + row.RelativeItem(3) + .Border(0.3f) + .Text(text => + { + text.AlignCenter(); + text.Span("Cupons").FontSize(10).FontFamily(Fonte).SemiBold().Italic(); + }); + }); + + column.Item().Row(LinhaTotal("Resumo notas autorizadas" + , Model.QuantidadeNfceAutorizada + , Model.ValorTotalNfceAutorizada)); + + column.Item().Row(LinhaTotal("Resumo notas canceladas" + , Model.QuantidadeNfceCancelada + , Model.ValorTotalNfceCancelada)); + + column.Item().Row(LinhaTotal("Resumo notas denegadas" + , Model.QuantidadeNfceDenegada + , Model.ValorTotalNfceDenegada)); + + + } + + private Action LinhaTotal(string descricaoLinha, decimal quantidadeNfce, decimal valorTotal) + { + return row => + { + row.RelativeItem(7) + .Border(0.3f) + .Text(descricaoLinha).FontSize(10).FontFamily(Fonte).Italic(); + + row.RelativeItem(3) + .Border(0.3f) + .Row(resumoAutoriadasRow => + { + resumoAutoriadasRow.RelativeItem(5) + .Border(0.3f) + .Text(quantidadeNfce).FontSize(10) + .FontFamily(Fonte).Italic(); + + resumoAutoriadasRow.RelativeItem(5) + .Border(0.3f) + .Text(text => + { + text.AlignRight(); + text.Span(valorTotal.ToString("N")).FontSize(10).FontFamily(Fonte).Italic(); + }); + }); + }; + } +} \ No newline at end of file diff --git a/NFe.Danfe.QuestPdf/ParaContadores/RelatorioFiscalEmissoesNfeDocument.cs b/NFe.Danfe.QuestPdf/ParaContadores/RelatorioFiscalEmissoesNfeDocument.cs new file mode 100644 index 000000000..5fea82b34 --- /dev/null +++ b/NFe.Danfe.QuestPdf/ParaContadores/RelatorioFiscalEmissoesNfeDocument.cs @@ -0,0 +1,285 @@ +using NFe.Danfe.QuestPdf.Models; +using QuestPDF.Fluent; +using QuestPDF.Helpers; +using QuestPDF.Infrastructure; + +namespace NFe.Danfe.QuestPdf.ParaContadores; + +public class RelatorioFiscalEmissoesNfeDocument : IDocument +{ + private static string Fonte = "Arial"; + private RelatorioFiscalEmissoesNfeModel Model; + + public RelatorioFiscalEmissoesNfeDocument(RelatorioFiscalEmissoesNfeModel model) + { + Model = model; + } + + public DocumentMetadata GetMetadata() => DocumentMetadata.Default; + + public void Compose(IDocumentContainer container) + { + container + .Page(page => + { + page.Size(PageSizes.A4); + page.Margin(20); + + page.Header().Element(Cabecalho); + + + page.Content().Element(Conteudo); + + + page.Footer().AlignCenter().Text(x => + { + x.CurrentPageNumber(); + x.Span(" / "); + x.TotalPages(); + }); + }); + } + + private void Cabecalho(IContainer container) + { + var titleStyle = TextStyle.Default.FontSize(11).FontFamily(Fonte); + + container.Row(row => + { + row.RelativeItem().Column(column => + { + column.Item().AlignRight().Text("Relatório fiscal de emissões na NF-e").FontSize(12).FontFamily(Fonte).SemiBold(); + column.Item().AlignRight().Text($"Impresso em: {GetMetadata().CreationDate}").FontSize(10).FontFamily(Fonte).Italic().SemiBold(); + column.Item().AlignLeft().Text($"Razão Social: {Model.EmpresaModel.RazaoSocial}").Style(titleStyle); + column.Item().AlignLeft().Text($"CNPJ: {Model.EmpresaModel.Cnpj}").Style(titleStyle); + column.Item().AlignLeft().Text($"Período: {Model.PeriodoModel.DataInicial:d} até {Model.PeriodoModel.DataFinal:d}"); + }); + }); + } + + private void Conteudo(IContainer container) + { + void TituloInicioTabela(ColumnDescriptor column, string titulo) + { + column.Item() + .PaddingTop(15) + .AlignCenter() + .Text(titulo) + .FontSize(9) + .Italic() + .SemiBold() + .FontFamily(Fonte); + } + + + container + .PaddingVertical(10) + .Column(column => + { + TituloInicioTabela(column, "Documento Fiscal Modelo 55 - NFE"); + + column.Item().Element(TabelaNFeResumida); + + TituloInicioTabela(column, "Inutilização feitas no periodo"); + + column.Item().Element(TabelaInutilizacoes); + + column.Item() + .PaddingTop(15) + .AlignCenter(); + + ResumoTotais(column); + }); + } + + private void TabelaNFeResumida(IContainer container) + { + container.Table(table => + { + table.ColumnsDefinition(columns => + { + columns.ConstantColumn(80); + columns.ConstantColumn(50); + columns.ConstantColumn(30); + columns.ConstantColumn(55); + columns.RelativeColumn(); + columns.ConstantColumn(55); + }); + + + table.Header(header => + { + header.Cell().Element(CellStyle).Text("Data Autorização"); + header.Cell().Element(CellStyle).Text("Situação"); + header.Cell().Element(CellStyle).Text("Série"); + header.Cell().Element(CellStyle).Text("Número"); + header.Cell().Element(CellStyle).Text("Chave"); + header.Cell().Element(CellStyle).AlignRight().Text("Valo Total"); + + + static IContainer CellStyle(IContainer container) + { + return container.DefaultTextStyle(x => + x.SemiBold() + .FontSize(9) + .FontFamily(Fonte) + ) + .PaddingVertical(5) + .BorderBottom(1) + .BorderColor(Colors.Black); + } + }); + + foreach (var item in Model.NfeResumidaModels) + { + table.Cell().Element(CellStyle).Text(item.AutorizacaoEm.ToString("g")).Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text(item.Situacao).Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text($"{item.Serie:D3}").Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text($"{item.NumeroFiscal:D9}").Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text($"{item.Chave}").Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).AlignRight().Text($"{item.ValorTotal:C}").Style(EstiloTextoColunas()); + + static IContainer CellStyle(IContainer container) + { + return container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(0); + } + + static TextStyle EstiloTextoColunas() + { + return TextStyle.Default.FontSize(8.5f).FontFamily(Fonte); + } + } + + table.Footer(footer => + { + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + }); + }); + } + + private void TabelaInutilizacoes(IContainer container) + { + container.Table(table => + { + table.ColumnsDefinition(columns => + { + columns.RelativeColumn(); + columns.RelativeColumn(); + columns.RelativeColumn(); + columns.RelativeColumn(); + columns.RelativeColumn(); + }); + + table.Header(header => + { + header.Cell().Element(CellStyle).Text("Data Inutilização"); + header.Cell().Element(CellStyle).Text("Modelo"); + header.Cell().Element(CellStyle).Text("Série"); + header.Cell().Element(CellStyle).Text("Número Inicial"); + header.Cell().Element(CellStyle).Text("Número Final"); + + + static IContainer CellStyle(IContainer container) + { + return container.DefaultTextStyle(x => x.SemiBold().FontSize(9).FontFamily(Fonte)) + .PaddingVertical(5) + .BorderBottom(1) + .BorderColor(Colors.Black); + } + }); + + foreach (var item in Model.NfeInutilizacaoModels) + { + table.Cell().Element(CellStyle).Text(item.InutilizacaoEm.ToString("g")).Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text(item.DescricaoModelo).Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text($"{item.Serie:D3}").Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text($"{item.NumeroInicial:D9}").Style(EstiloTextoColunas()); + table.Cell().Element(CellStyle).Text($"{item.NumeroFinal:D9}").Style(EstiloTextoColunas()); + + static IContainer CellStyle(IContainer container) + { + return container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(0); + } + + static TextStyle EstiloTextoColunas() + { + return TextStyle.Default.FontSize(8.5f).FontFamily(Fonte); + } + } + + table.Footer(footer => + { + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + footer.Cell().BorderBottom(1); + }); + }); + } + + private void ResumoTotais(ColumnDescriptor column) + { + column.Item().Row(row => + { + row.RelativeItem(7) + .Border(0.3f) + .Text("Resumo das saidas").FontSize(10).FontFamily(Fonte).SemiBold().Italic(); + + row.RelativeItem(3) + .Border(0.3f) + .Text(text => + { + text.AlignCenter(); + text.Span("Notas Fiscais").FontSize(10).FontFamily(Fonte).SemiBold().Italic(); + }); + }); + + column.Item().Row(LinhaTotal("Resumo notas autorizadas" + , Model.QuantidadeNfeAutorizada + , Model.ValorTotalNfeAutorizada)); + + column.Item().Row(LinhaTotal("Resumo notas canceladas" + , Model.QuantidadeNfeCancelada + , Model.ValorTotalNfeCancelada)); + + column.Item().Row(LinhaTotal("Resumo notas denegadas" + , Model.QuantidadeNfeDenegada + , Model.ValorTotalNfeDenegada)); + + + } + + private Action LinhaTotal(string descricaoLinha, decimal quantidadeNfe, decimal valorTotal) + { + return row => + { + row.RelativeItem(7) + .Border(0.3f) + .Text(descricaoLinha).FontSize(10).FontFamily(Fonte).Italic(); + + row.RelativeItem(3) + .Border(0.3f) + .Row(resumoAutoriadasRow => + { + resumoAutoriadasRow.RelativeItem(5) + .Border(0.3f) + .Text(quantidadeNfe).FontSize(10) + .FontFamily(Fonte).Italic(); + + resumoAutoriadasRow.RelativeItem(5) + .Border(0.3f) + .Text(text => + { + text.AlignRight(); + text.Span(valorTotal.ToString("N")).FontSize(10).FontFamily(Fonte).Italic(); + }); + }); + }; + } +} \ No newline at end of file