Winnovative HTML to PDF Converter creates electronic invoices in the Factur-X and ZUGFeRD formats (including the XRechnung profile) and electronic orders in the Order-X format. An electronic invoice is a PDF/A-3 document that shows the invoice as a page a person reads and carries the same invoice as an XML file that accounting software reads automatically. Factur-X and ZUGFeRD are the French and the German name of the same format.
The invoice XML is loaded in an Winnovative.Pdf.NextElectronicInvoice object with ElectronicInvoiceFromXml(Byte), ElectronicInvoiceFromXml(String) or ElectronicInvoiceFromFile(String). The profile, the document type and the invoice number are taken from the XML and exposed by the Profile, DocumentType and InvoiceNumber properties. The invoice is then added to the generated document with PdfDocumentOptionsAddElectronicInvoice(ElectronicInvoice). The same method is available on PdfDocumentAddElectronicInvoice(ElectronicInvoice) for documents created from scratch and on PdfEditorAddElectronicInvoice(ElectronicInvoice) for existing PDF/A-3 documents. The produced document embeds the invoice XML and carries the invoice identification that the receiving software looks for.
Supported profiles
Factur-X and ZUGFeRD invoices in the ElectronicInvoiceProfileMinimum, ElectronicInvoiceProfileBasicWL, ElectronicInvoiceProfileBasic, ElectronicInvoiceProfileEN16931, ElectronicInvoiceProfileExtended and ElectronicInvoiceProfileXRechnung profiles
Order-X purchase orders, order changes and order responses in the ElectronicInvoiceProfileOrderXBasic, ElectronicInvoiceProfileOrderXComfort and ElectronicInvoiceProfileOrderXExtended profiles
All the invoice document types: commercial invoices, credit notes, corrected invoices, self-billed invoices and credit notes, prepayment and partial invoices, as listed by the Winnovative.Pdf.NextElectronicDocumentType enumeration
Invoices in the UBL syntax and ZUGFeRD 1.0 invoices are not supported. The XML is embedded as given.
Requirements
The document must be a PDF/A-3 document: PdfDocumentOptionsPdfStandard must be set to PdfStandardPdfA3b, PdfStandardPdfA3u, PdfStandardPdfA3a or PdfStandardPdfUa1PdfA3a. The PDF editor requires a source document that is already a PDF/A-3 document
The Relationship property describes how the XML relates to the visible page. The default is Alternative, meaning the XML and the page carry the same invoice. The MINIMUM and BASIC WL profiles do not carry the complete invoice, so for them the relationship is always Data
One electronic invoice can be added to a document. Other file attachments can be added to the same document with PdfDocumentOptionsAddFileAttachment(PdfFileAttachment)
Accessible invoices. With the PdfStandardPdfUa1PdfA3a standard the invoice page is also a PDF/UA-1 document, readable with screen readers and assistive technologies, as required for the invoices delivered to consumers under the European Accessibility Act. The HTML of the invoice should use headings, tables with header cells and alternate text for images.
Setting HtmlToPdfConverterPdfViewerPreferences.PageMode to ViewerPageModeUseAttachments opens the Attachments panel when the document is loaded, so the embedded invoice XML is visible to the reader.
using System;
using System.IO;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Winnovative_Next_AspNetDemo.Models;
using Winnovative_Next_AspNetDemo.Models.HTML_to_PDF;
// Use Winnovative Namespace
using Winnovative.Pdf.Next;
namespace Winnovative_Next_AspNetDemo.Controllers.HTML_to_PDF
{
public class Create_Electronic_Invoice_PDFController : Controller
{
private readonly IWebHostEnvironment m_hostingEnvironment;
public Create_Electronic_Invoice_PDFController(IWebHostEnvironment hostingEnvironment)
{
m_hostingEnvironment = hostingEnvironment;
}
public IActionResult Index()
{
var model = SetViewModel();
return View(model);
}
[HttpPost]
public async Task<ActionResult> ConvertHtmlToPdf(Create_Electronic_Invoice_PDF_ViewModel model)
{
if (!ModelState.IsValid)
{
var errorMessage = ModelStateHelper.GetModelErrors(ModelState);
throw new ValidationException(errorMessage);
}
// Set the license key received after purchase to use the library in licensed mode; leave it commented for demo mode
// Licensing.LicenseKey = "your-license-key";
// The invoice XML: the uploaded file when there is one, otherwise the selected sample
byte[] invoiceXml = await ReadInvoiceXml(model);
// Read the invoice. The profile and the document type (invoice, credit note, order...) are
// taken from the XML itself
ElectronicInvoice invoice = ElectronicInvoice.FromXml(invoiceXml);
// How the embedded XML relates to the visible page. MINIMUM and BASIC WL do not carry
// the whole invoice, so for them the relationship is always Data
invoice.Relationship = model.Relationship;
// Create a HTML to PDF converter object with default settings
HtmlToPdfConverter htmlToPdfConverter = new HtmlToPdfConverter();
// An electronic invoice needs a PDF/A-3 carrier. PDF/UA-1 + PDF/A-3a produces an
// invoice that is also accessible
htmlToPdfConverter.PdfDocumentOptions.PdfStandard = model.PdfStandard;
// Embed the invoice XML and write the metadata invoice software looks for
htmlToPdfConverter.PdfDocumentOptions.AddElectronicInvoice(invoice);
// Open the Attachments panel on document load, so the embedded XML is visible
htmlToPdfConverter.PdfViewerPreferences.PageMode = ViewerPageMode.UseAttachments;
byte[] outPdfBuffer = htmlToPdfConverter.ConvertHtml(model.HtmlString, model.BaseUrl);
// Send the PDF file to browser
FileResult fileResult = new FileContentResult(outPdfBuffer, "application/pdf");
fileResult.FileDownloadName = string.Format("{0}_{1}_{2}.pdf", invoice.DocumentType, invoice.InvoiceNumber, invoice.Profile);
return fileResult;
}
private async Task<byte[]> ReadInvoiceXml(Create_Electronic_Invoice_PDF_ViewModel model)
{
if (model.InvoiceXmlFile != null && model.InvoiceXmlFile.Length > 0)
{
using var ms = new MemoryStream();
await model.InvoiceXmlFile.CopyToAsync(ms);
return ms.ToArray();
}
// Only file names from the samples folder are accepted
string fileName = Path.GetFileName(model.SampleInvoice ?? string.Empty);
string filePath = Path.Combine(GetInvoiceFilesPath(), fileName);
if (fileName.Length == 0 || !System.IO.File.Exists(filePath))
throw new Exception("No invoice XML provided: upload a file or select a sample");
return await System.IO.File.ReadAllBytesAsync(filePath);
}
private Create_Electronic_Invoice_PDF_ViewModel SetViewModel()
{
var model = new Create_Electronic_Invoice_PDF_ViewModel();
var contentRootPath = Path.Combine(m_hostingEnvironment.ContentRootPath, "wwwroot");
HttpRequest request = ControllerContext.HttpContext.Request;
UriBuilder uriBuilder = new UriBuilder();
uriBuilder.Scheme = request.Scheme;
uriBuilder.Host = request.Host.Host;
if (request.Host.Port != null)
uriBuilder.Port = (int)request.Host.Port;
uriBuilder.Path = request.PathBase.ToString() + request.Path.ToString();
uriBuilder.Query = request.QueryString.ToString();
string currentPageUrl = uriBuilder.Uri.AbsoluteUri;
string rootUrl = currentPageUrl.Substring(
0, currentPageUrl.Length - "Create_Electronic_Invoice_PDF".Length);
model.HtmlString = System.IO.File.ReadAllText(Path.Combine(contentRootPath, "DemoAppFiles/Input/HTML_Files/Electronic_Invoice.html"));
model.BaseUrl = rootUrl + "DemoAppFiles/Input/HTML_Files/";
return model;
}
private string GetDemoFilesPath() => m_hostingEnvironment.ContentRootPath + "/wwwroot" + "/DemoAppFiles/Input/";
private string GetInvoiceFilesPath() => Path.Combine(GetDemoFilesPath(), "Invoice_Files");
}
}