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 000000000..7ff88f228 Binary files /dev/null and b/NFe.Danfe.QuestPdf/Fontes/Arial.ttf differ 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