Xojo Conferences
MBSOct2019CologneDE
XDCMarch2020NashvilleUS

« ZUGFeRD 2.0 and Factu… | Home | Tip of the day: Folde… »

ZUGFeRD 2.0 and Factur-X for Xojo


With the last DynaPDF update we got new support for ZUGFeRD 2.0 and Factur-X, two standards to deliver invoices as PDF files with embedded XML data. We supported ZUGFeRD 1.0 before and now upgraded code to support version 2 in addition.

 

So here is a little Xojo code snippet to convert an existing PDF into a ZUGFeRD 2.0 document. Before you start, please assemble the XML for the invoice and know whether it's Minimum, Basic, Comfort or Extended level. So the code here starts with a new DynaPDFMBS object and creates a new PDF environment. We set import flags to import all content and prepare for PDF/A. If needed, you could skip some content like annotations directly here.

 

dim pdf as new MyDynapdfMBS

dim f as FolderItem = SpecialFolder.Desktop.Child("Create PDF.pdf")

pdf.SetLicenseKey "Pro" // For this example you can use a Pro or Enterprise License

// now create PDF

call pdf.CreateNewPDF f

Dim flags As Integer

flags = Bitwise.BitOr(flags, pdf.kifImportAll)

flags = Bitwise.BitOr(flags, pdf.kifImportAsPage)

flags = Bitwise.BitOr(flags, pdf.kifPrepareForPDFA)

call pdf.SetImportFlags(flags)

 

Next we load an existing PDF from a container field. Of course you can also use functions to create new pages here or open an import PDF from a file on disk. The next line imports the whole PDF file we opened into our work PDF. If you repeat the open and import step, you can merge several documents here. If you need only one page, you can use DynaPDF.ImportPDFPage function.

 

// import PDF file

dim ImportFile as FolderItem = FindFile("invoice template.pdf")

call pdf.OpenImportFile(ImportFile)

call pdf.ImportPDFFile(1) 

 

Per PDF/A requirement, we add here the language setting and a structure tree:

 

// PDF/A requires a language set

Call pdf.SetLanguage("en-US")

// PDF/A requires a structure tree

Call pdf.CreateStructureTree

 

If the PDF contains colorspaces which are not backed by an ICC Profile, we get an OnReplaceICCProfile event call to provide a replacement profile to the PDF engine. So if there is a colorspace missing the profile, it will be using one of our replacement profiles for RGB, Gray or CMYK. Here is an example implementation of the event, which looks for some generic profiles to replace an existing one:

 

EventHandler Function OnReplaceICCProfile(Type as integer, ColorSpace as integer) As integer

// provide missing ICC Profiles to DynaPDF

// The ICC profiles which should normally be configured by the user.

Dim filename As String

Select Case type

Case Me.kictGray

filename = "Generic Gray Profile.icc"

Case Me.kictRGB

filename = "Generic RGB Profile.icc"

Case Me.kictCMYK

filename = "Generic CMYK Profile.icc"

Case Me.kictLab

// not yet needed, but maybe in future

filename = "Generic Lab Profile.icc"

Else

Break

Return -1 // new type we don't know?

End Select

Dim f As FolderItem = FindFile(filename)

If f = Nil Or Not f.Exists Then

// file missing?

Return -1

End If

Dim e As Integer = ReplaceICCProfile(ColorSpace, f)

If e < 0 Then

// failed

Break

End If

// pass along success or failure

Return e

End EventHandler

 

There is another event for missing fonts, where you can provide a replacement font. We simply replace missing fonts with Arial

 

EventHandler Function OnFontNotFound(PDFFontRef as integer, FontName as string, Style as integer, StdFontIndex as integer, IsSymbolFont as boolean) As integer

// Here you could use your own mapping table.

// In this example we replace the font simply with Arial

if (WeightFromStyle(Style) < 500) then

// Only the weights 500 and 700 of Arial are installed

// by default. If you have also light variants then it is

// not required to change the style.

Style = BitwiseAnd(Style, &h0F)

Style = BitwiseOr(Style, kfsRegular)

end if

return ReplaceFont(PDFFontRef, "Arial", Style, true)

End EventHandler

 

Next we attach the XML invoice to the PDF. You can pass attachments as container, file or text, but please use the corresponding function. Here we pass it as text from a field, specify UTF-8 encoding and the ZUGFeRD specific file name. The attachment is associated with the main catalog as an alternative version.

 

// now add xml

Dim file As FolderItem = FindFile("ZUGFeRD-invoice.xml")

dim n as integer = pdf.AttachFile(file, "ZUGFeRD Rechnung", false)

if not pdf.AssociateEmbFile(pdf.kadCatalog, -1, pdf.karAlternative, n) then

Break // error

end if

 

Now we do the conformance check. If you licensed the PDF/A converter from us (Add-on for DynaPDF Pro), you get the PDF fixed if needed. If the PDF was already PDF/A, this should return okay and tel you which output indent is recommended. We than add the ICC Profile for RGB or CMYK to indicate how the PDF likes to be viewed.

 

// make sure we conform

// for perfect usage, you need PDF/A extension for DynaPDF (extra purchase)

// here we pass Basic level. Please make sure XML and level here match!

Dim retval As Integer = pdf.CheckConformance(pdf.kctZUGFeRD2_Basic, pdf.kcoDefault)

Select case retval

case 1

call pdf.AddOutputIntent(RGBProfileFile) // RGB

case 2

call pdf.AddOutputIntent(CMYKProfileFile) // CMYK

case 3

call pdf.AddOutputIntent(GrayProfileFile) // Gray

end Select

 

Finally we close the PDF file and let the destructor free the PDF environment.

 

call pdf.CloseFile 

 

We hope you enjoy this blog post. Please check the example databases included with MBS Xojo Plugins 19.3.

14 06 19 - 19:27