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/Zeus NFe.sln b/Zeus NFe.sln
index 382ae9e0e..e9efbf580 100644
--- a/Zeus NFe.sln
+++ b/Zeus NFe.sln
@@ -119,6 +119,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NFe.Danfe.Html", "NFe.Danfe
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NFe.Danfe.App.Teste.Html", "NFe.Danfe.App.Teste.Html\NFe.Danfe.App.Teste.Html.csproj", "{18429710-19E2-480F-8B3A-39FB6FC7AF19}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NFe.Danfe.PdfClown", "NFe.Danfe.PdfClown\NFe.Danfe.PdfClown.csproj", "{2A235861-B8FC-463A-A774-0C179529881A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -463,6 +465,14 @@ Global
{18429710-19E2-480F-8B3A-39FB6FC7AF19}.Release|Any CPU.Build.0 = Release|Any CPU
{18429710-19E2-480F-8B3A-39FB6FC7AF19}.Release|x86.ActiveCfg = Release|Any CPU
{18429710-19E2-480F-8B3A-39FB6FC7AF19}.Release|x86.Build.0 = Release|Any CPU
+ {2A235861-B8FC-463A-A774-0C179529881A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2A235861-B8FC-463A-A774-0C179529881A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2A235861-B8FC-463A-A774-0C179529881A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2A235861-B8FC-463A-A774-0C179529881A}.Debug|x86.Build.0 = Debug|Any CPU
+ {2A235861-B8FC-463A-A774-0C179529881A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2A235861-B8FC-463A-A774-0C179529881A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2A235861-B8FC-463A-A774-0C179529881A}.Release|x86.ActiveCfg = Release|Any CPU
+ {2A235861-B8FC-463A-A774-0C179529881A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -518,6 +528,7 @@ Global
{7F95E157-C567-451F-A250-9645BE3F9497} = {003B008B-F388-4FB5-A081-12C0FBA84B57}
{3CDB91BD-EEF4-4808-83AC-E45B1FE7570C} = {003B008B-F388-4FB5-A081-12C0FBA84B57}
{18429710-19E2-480F-8B3A-39FB6FC7AF19} = {3AC1A3D4-91D0-471A-AF9C-5EF8442F3F27}
+ {2A235861-B8FC-463A-A774-0C179529881A} = {003B008B-F388-4FB5-A081-12C0FBA84B57}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C8C4D9F7-EF86-49A2-83AF-13FA5D4E8DBB}