In questo post descriverò come utilizzare un componente commerciale (e assolutamente non free) per creare pdf.
Sottolineo che la mia è una recensione assolutamente indipendente, solo con l'intento di mostrare una possibile soluzione.
Segnalalo anche che per creare documenti pdf da codice Xamarin esistono anche alternative free, ma dovendo offrire questa funzionalità cross-platform, e non esistendo una librareria che offre queste caratteristiche, potrebbe essere preferibile usare un componente a pagamento: questo in particolare permette di avere un codice univoco per tutte le piattaforme e quindi risulta più facile da manuntenere.
Ovviamente è noto che esistono anche altri componenti commerciali: descrivo questo solo perchè io l'ho provato, e lo ha trovato soddifacente.
Eppoi il blog è mio..... e quindi scrivo di che voglio... no ?!
Il componente in parola è XFinium pdf (in linkografica il sito di riferimento). Per poche centinaia di euro (tale è il costo della licenza più economica per 1 dev) è possibile ottenere il componente in versione standard, che offre tutte le funzionalità richieste per la maggior parte degli utilizzi.
Preparatevi: la documentazione a corredo è realmente sconcertante. Pochi avrebbero saputo fare peggio: in contrasto i progetti sample in allegato sono curati e in gran numero. Quindi sono una buona base di partenza per qualsiasi cosa Vi occorre fare.
Il prodotto presenta un mare di opzioni, che all'inizio spiazzano e confondono, ma una volta capito il meccanismo poi dopo non sono così difficili da gestire.
E come detto l'help è "diversamente utilizzabile". Noi abbiamo usato il tutto per creare un pdf di un ordine da cliente: da codice condiviso PCL abbiamo usato il pattern DependencyService. Ecco l'interfaccia: il valore restituito è il path del pdf stesso.
public interface IOrder2PdfFile { string Ordine2PdfFile(DateTime _DataDocumento, string _CondizioneDiPagamento, string _NomeCliente, ObservableCollection< GEST_Ordini_Righe_DettaglioOrdine> _ListaArticoliInOrdine, decimal _TotaleOrdine); }
Nel seguito l'implementazione in Android: per le altre piattaforme il codice è sostanzialmente uguale. In pratica si crea un file pdf con delle intestazioni e quindi delle righe prodotto utilizzando una tabella.
public class Order2PdfFile : IOrder2PdfFile { public string Ordine2PdfFile(DateTime _DataDocumento, string _CondizioneDiPagamento, string _NomeCliente, ObservableCollection< GEST_Ordini_Righe_DettaglioOrdine> _ListaArticoliInOrdine, decimal _TotaleOrdine) { var output = Run(_DateTime _DataDocumento, string _CondizioneDiPagamento, string _NomeCliente, ObservableCollection< GEST_Ordini_Righe_DettaglioOrdine> _ListaArticoliInOrdine, decimal _TotaleOrdine); string pdfPath = string.Empty; for (int i = 0; i < output.Length; i++) { pdfPath = Path.Combine(Forms.Context.ExternalCacheDir.ToString() , output[i].FileName); Stream pdfStream = File.Open(pdfPath, FileMode.Create); output[i].Document.Save(pdfStream); pdfStream.Flush(); pdfStream.Close(); } Java.IO.File pdffile = new Java.IO.File(pdfPath); pdffile.SetReadable(true, false); return pdfPath; } private static void AggiungiRiga(PdfFlowDocument _Documento,string _Testo) { PdfFormattedContent _NuovaRiga = new PdfFormattedContent(); _NuovaRiga.Paragraphs.Add(_Testo); PdfFlowContent NuovaRigaPdfFlowContent = new PdfFlowTextContent(_NuovaRiga); _Documento.AddContent(NuovaRigaPdfFlowContent); } private static OutputInfo[] Run(DateTime _DataDocumento, string _CondizioneDiPagamento, string _NomeCliente, ObservableCollection< GEST_Ordini_Righe_DettaglioOrdine> _ListaArticoliInOrdine, decimal _TotaleOrdine) { PdfFlowDocument document = new PdfFlowDocument(); document.PageCreated += Document_PageCreated; PdfStandardFont FontGrosso = new PdfStandardFont(); FontGrosso.Size = 27; PdfFormattedContent sellerInfo = new PdfFormattedContent(); sellerInfo.Paragraphs.Add(new PdfFormattedTextBlock("Ordine", FontGrosso)); PdfFlowTextContent sellerInfoText = new PdfFlowTextContent(sellerInfo); document.AddContent(sellerInfoText); AggiungiRiga(document, string.Empty); AggiungiRiga(document, "Data Documento: " + _DataDocumento.ToString("dd/MM/yyyy")); AggiungiRiga(document, "Cliente: " + _NomeCliente); AggiungiRiga(document, string.Empty); AggiungiRiga(document, "Condizione di Pagamento: " + _CondizioneDiPagamento); AggiungiRiga(document, string.Empty); PdfFlowContent invoiceItemsSection = BuildTblProdottiPerClasse(_ListaArticoli); document.AddContent(invoiceItemsSection); AggiungiRiga(document, string.Empty); PdfStandardFont FontGrassetto = new PdfStandardFont(); FontGrassetto.Bold = true; PdfFormattedContent TotaleInfo = new PdfFormattedContent(); TotaleInfo.Paragraphs.Add(new PdfFormattedTextBlock("Totale Ordine: € " + DecimalFormattato(_TotaleOrdine, false), FontGrassetto)); PdfFlowTextContent InfoTotale = new PdfFlowTextContent(TotaleInfo); document.AddContent(InfoTotale); OutputInfo[] output = new OutputInfo[] { new OutputInfo(document, "Ordine.pdf") }; return output; } private static void Document_PageCreated(object sender, PdfFlowPageCreatedEventArgs e) { e.PageDefaults.Margins.Left = 18; e.PageDefaults.Margins.Right = 18; } private static PdfFlowContent BuildTblProdottiPerClasse( ObservableCollection _Lista) { PdfFont fontPiccolo = new PdfStandardFont(); fontPiccolo.Size = 9; PdfFlowTableContent tblOrdine = new PdfFlowTableContent(8); tblOrdine.Border = new PdfPen(PdfRgbColor.Black, 0.5); tblOrdine.MinRowHeight = 25; tblOrdine.Columns[0].VerticalAlign = PdfGraphicAlign.Near; //Cod Art tblOrdine.Columns[0].Width = 45; tblOrdine.Columns[0].WidthIsRelativeToTable = false; tblOrdine.Columns[0].HorizontalAlign = PdfGraphicAlign.Near; tblOrdine.Columns[1].HorizontalAlign = PdfGraphicAlign.Near; // Descrizione tblOrdine.Columns[1].VerticalAlign = PdfGraphicAlign.Near; tblOrdine.Columns[1].Width = 200; tblOrdine.Columns[1].WidthIsRelativeToTable = false; tblOrdine.Columns[2].VerticalAlign = PdfGraphicAlign.Near; //Qta Ordinato tblOrdine.Columns[2].HorizontalAlign = PdfGraphicAlign.Far; tblOrdine.Columns[2].Width = 50; tblOrdine.Columns[2].WidthIsRelativeToTable = false; tblOrdine.Columns[4].HorizontalAlign = PdfGraphicAlign.Far; //Importo tblOrdine.Columns[4].VerticalAlign = PdfGraphicAlign.Near; tblOrdine.Columns[4].Width = 65; tblOrdine.Columns[4].WidthIsRelativeToTable = false; tblOrdine.Columns[5].HorizontalAlign = PdfGraphicAlign.Far; //Sc1 tblOrdine.Columns[5].VerticalAlign = PdfGraphicAlign.Near; tblOrdine.Columns[5].Width = 30; tblOrdine.Columns[5].WidthIsRelativeToTable = false; tblOrdine.Columns[6].HorizontalAlign = PdfGraphicAlign.Far; //Sc2 tblOrdine.Columns[6].VerticalAlign = PdfGraphicAlign.Near; tblOrdine.Columns[6].Width = 30; tblOrdine.Columns[6].WidthIsRelativeToTable = false; tblOrdine.Columns[7].HorizontalAlign = PdfGraphicAlign.Far; tblOrdine.Columns[7].VerticalAlign = PdfGraphicAlign.Near; //Totale tblOrdine.Columns[7].Width = 55; tblOrdine.Columns[7].WidthIsRelativeToTable = false; (tblOrdine.DefaultCell as PdfFlowTableStringCell).Font = fontPiccolo; PdfFlowContentMargins margine = new PdfFlowContentMargins(3); (tblOrdine.DefaultCell as PdfFlowTableStringCell).InnerMargins = margine; tblOrdine.Rows.AddRowWithCells("Lista Articoli"); tblOrdine.Rows[0].Cells[0].ColSpan = 8; tblOrdine.Rows[0].Cells[0].HorizontalAlign = PdfGraphicAlign.Center; tblOrdine.Rows[0].Cells[0].VerticalAlign = PdfGraphicAlign.Center; PdfFlowTableRow row = tblOrdine.Rows.AddRowWithCells("CodArt", "Descrizione", "Qta", "Euro", "Sc1", "Sc2", "Totale"); for (int i = 0; i < row.Cells.Count; i++) { (row.Cells[i] as PdfFlowTableStringCell).HorizontalAlign = PdfGraphicAlign.Center; (row.Cells[i] as PdfFlowTableStringCell).VerticalAlign = PdfGraphicAlign.Near; (row.Cells[i] as PdfFlowTableStringCell).Multiline = true; } foreach (var RigaInClasse in _Lista) { row = tblOrdine.Rows.AddRowWithCells(RigaInClasse.CodArt, RigaInClasse.Descrizione, RigaInClasse.Qta, "€ " + DecimalFormattato(RigaInClasse.ValUnit,false), RigaInClasse.Sc1, RigaInClasse.Sc2, "€ " + DecimalFormattato(RigaInClasse.Imponibile,false)); (row.Cells[1] as PdfFlowTableStringCell).Multiline = true; (row.Cells[1] as PdfFlowTableStringCell).Content = RigaInClasse.Descrizione.Replace("''","'"); } return tblOrdine; } private static string DecimalFormattato(decimal _Importo, bool _SenzaDecimali) { if (_SenzaDecimali) { return _Importo.ToString("N0", CultureInfo.GetCultureInfo("it-IT")); } string stringadaritornare = string.Empty; if (_Importo == 0) return "0"; else { if (_Importo < 1) { return _Importo.ToString("N2", CultureInfo.GetCultureInfo("it-IT")); } } return _Importo.ToString("N2", CultureInfo.GetCultureInfo("it-IT")); } } public class OutputInfo { public OutputInfo(PdfDocument document, string fileName) : this(document, fileName, null) { } public OutputInfo(PdfDocument document, string fileName, PdfSecurityHandler securityHandler) { this.document = document; this.fileName = fileName; this.securityHandler = securityHandler; } private PdfDocument document; public PdfDocument Document { get { return document; } set { document = value; } } private string fileName; public string FileName { get { return fileName; } set { fileName = value; } } private PdfSecurityHandler securityHandler; public PdfSecurityHandler SecurityHandler { get { return securityHandler; } set { securityHandler = value; } } }
Affinchè tutto funzioni occorre porre come reference del progetto la dll corretta e per la piattaforma. Il codice è sicuramente passibile di ogni tipo di miglioria, e ammettiamo che non è splendido ed è preso da un codice reale buttato già in fretta e furia per fornire la funzionalità, eppoi come spesso capita abbandonato lì. Però ha una caratteristica apprezzabile: funziona perfettamente !
Linkografia