Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set additional XMP RDF to the document #877

Open
Cluster2a opened this issue May 11, 2024 · 9 comments
Open

Set additional XMP RDF to the document #877

Cluster2a opened this issue May 11, 2024 · 9 comments
Labels
enhancement New feature or request help wanted Extra attention is needed pdf-engines

Comments

@Cluster2a
Copy link

Cluster2a commented May 11, 2024

Hey, is this possible via Gotenberg?
https://mpdf.github.io/what-else-can-i-do/pdf-a3-xmp-rdf.html

We would like to add invoicing data (ZUGFeRD) to our pdf files and I prefer not to use an additional library.

We are currently using Laravel + Twig + Chromium to create the invoice.

Thanks in advance

@Cluster2a Cluster2a reopened this May 12, 2024
@gulien
Copy link
Collaborator

gulien commented May 13, 2024

@Cluster2a Cluster2a changed the title Set additional XMP RDF to the document metadata Set additional XMP RDF to the document May 13, 2024
@Cluster2a
Copy link
Author

@gulien , I think the way to realize inline XML is a file embedding, which seems like it has nothing to do with the metadata. Sorry for the misleading title.

https://invoice-portal.de/wp-content/uploads/2021/12/ZUGFeRD-Example.pdf
Here you have an example. If you check the source code, you will find the XML:
image
It also works as a stream inline.

The XML within this document looks like this:
image

As this will be required soon in Germany (and the EU?), the only chance right now is to edit the created pdf with another tool. Would be cool, if we could pass this XML to gotenberg and embed it directly.

@gulien gulien added enhancement New feature or request pdf-engines labels May 13, 2024
@gulien
Copy link
Collaborator

gulien commented May 13, 2024

There is no native support via LibreOffice for the factur-x standard. I found an extension that is allegedly capable to generate compliant PDF, but I have not idea how to trigger it programmatically 🤔

@Cluster2a
Copy link
Author

Cluster2a commented May 14, 2024

We Are currently using chromium + html for the rendering.

Do you see any chance to make that happen?

Maybe after rendering, we could pass the pdf to pdfcou (https://pdfcpu.io/attach/attach_add.html) and adding the XML as an a attachment?

But currently, there is no API for this kind of stuff in the client.

Maybe helpful: pdfcpu/pdfcpu#239

@gulien
Copy link
Collaborator

gulien commented May 14, 2024

pdfcpu may be helpful, good catch! Could you try it and tell me if it works with your use case?

@Cluster2a
Copy link
Author

@gulien, the following PDF is a valid one (got this from https://www.zugferd-community.net/de/zf_fx_invoiceportal/invoice_creation.
ZFdemo_2p1_en16931_66434a8b1e5b3.pdf

The validation confirms that this is valid:
image

Adding the x-factor.xml via pdfcpu works like charm.
But on validation, I am getting a few errors:
image

Here is the detailed report:

<!-- ?xml version="1.0" encoding="UTF-8"? -->
<validation filename="IN-24-1000.pdf" datetime="2024-05-14 13:29:50">
	<pdf>
		<report>
			<buildinformation>
				<releasedetails id="core" version="1.22.2" builddate="2022-09-14T13:46:00+02:00"/>
				<releasedetails id="validation-model" version="1.22.2" builddate="2022-09-14T13:47:00+02:00"/>
			</buildinformation>
			<jobs>
				<job>
					<item size="38939">
						<name>/var/www/zugferd_community/zugferd_checker/uploads/IN-24-1000.pdf</name>
					</item>
					<validationreport profilename="PDF/A-3B validation profile" statement="PDF file is not compliant with Validation Profile requirements." iscompliant="false">
						<details passedrules="121" failedrules="3" passedchecks="5485" failedchecks="8">
							<rule specification="ISO 19005-3:2012" clause="6.8" testnumber="3" status="failed" passedchecks="0" failedchecks="2">
								<description>In order to enable identification of the relationship between the file specification dictionary and the content that is referring to it, a new (required) key has been defined and its presence (in the dictionary) is required.</description>
								<object>CosFileSpecification</object>
								<test>AFRelationship != null</test>
								<check status="failed">
									<context>root/EmbeddedFiles[0]</context>
									<errormessage>The file specification dictionary for an embedded file does not contain the AFRelationship key</errormessage>
								</check>
								<check status="failed">
									<context>root/indirectObjects[107](115 0)/directObject[0]</context>
									<errormessage>The file specification dictionary for an embedded file does not contain the AFRelationship key</errormessage>
								</check>
							</rule>
							<rule specification="ISO 19005-3:2012" clause="6.8" testnumber="4" status="failed" passedchecks="0" failedchecks="2">
								<description>The additional information provided for associated files as well as the usage requirements for associated files indicate the relationship between the embedded file and the PDF document or the part of the PDF document with which it is associated.</description>
								<object>CosFileSpecification</object>
								<test>isAssociatedFile == true</test>
								<check status="failed">
									<context>root/EmbeddedFiles[0]</context>
									<errormessage>The file specification dictionary for an embedded file is not associated with the PDF document or any of its parts</errormessage>
								</check>
								<check status="failed">
									<context>root/indirectObjects[107](115 0)/directObject[0]</context>
									<errormessage>The file specification dictionary for an embedded file is not associated with the PDF document or any of its parts</errormessage>
								</check>
							</rule>
							<rule specification="ISO 19005-3:2012" clause="6.8" testnumber="1" status="failed" passedchecks="0" failedchecks="4">
								<description>The MIME type of an embedded file, or a subset of a file, shall be specified using the Subtype key of the file specification dictionary. If the MIME type is not known, the "application/octet-stream" shall be used.</description>
								<object>EmbeddedFile</object>
								<test>Subtype != null && /^[-\w+\.]+\/[-\w+\.]+$/.test(Subtype)</test>
								<check status="failed">
									<context>root/EmbeddedFiles[0]/EF[0]</context>
									<errormessage>MIME type null of an embedded file is missing or invalid</errormessage>
								</check>
								<check status="failed">
									<context>root/EmbeddedFiles[0]/EF[1]</context>
									<errormessage>MIME type null of an embedded file is missing or invalid</errormessage>
								</check>
								<check status="failed">
									<context>root/indirectObjects[107](115 0)/directObject[0]/EF[0]</context>
									<errormessage>MIME type null of an embedded file is missing or invalid</errormessage>
								</check>
								<check status="failed">
									<context>root/indirectObjects[107](115 0)/directObject[0]/EF[1]</context>
									<errormessage>MIME type null of an embedded file is missing or invalid</errormessage>
								</check>
							</rule>
						</details>
					</validationreport>
					<duration start="1715686190761" finish="1715686192013">00:00:01.252</duration>
				</job>
			</jobs>
			<batchsummary totaljobs="1" failedtoparse="0" encrypted="0" outofmemory="0" veraexceptions="0">
				<validationreports compliant="0" noncompliant="1" failedjobs="0">1</validationreports>
				<featurereports failedjobs="0">0</featurereports>
				<repairreports failedjobs="0">0</repairreports>
				<duration start="1715686190624" finish="1715686192048">00:00:01.424</duration>
			</batchsummary>
		</report>
		<info>
			<signature>unknown</signature>
			<duration unit="ms">1842</duration>
		</info>
		<messages>
			<error type="11">XMP Metadata: ConformanceLevel not found</error>
			<error type="12">XMP Metadata: ConformanceLevel contains invalid value</error>
			<error type="13">XMP Metadata: DocumentType not found</error>
			<error type="14">XMP Metadata: DocumentType invalid</error>
			<error type="21">XMP Metadata: DocumentFileName not found</error>
			<error type="19">XMP Metadata: DocumentFileName contains invalid value</error>
			<error type="15">XMP Metadata: Version not found</error>
			<error type="16">XMP Metadata: Version contains invalid value</error>
		</messages>
		<summary status="invalid"/>
	</pdf>
	<xml>
		<info>
			<version>2</version>
			<profile>urn:cen.eu:en16931:2017</profile>
			<validator version="2.10.0"/>
			<rules>
				<fired>22</fired>
				<failed>17</failed>
			</rules>
			<duration unit="ms">6470</duration>
		</info>
		<messages>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:ExchangedDocumentContext[1]" criterion="ram:BusinessProcessSpecifiedDocumentContextParameter/ram:ID">Business process MUST be provided. [ID PEPPOL-EN16931-R001] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction[1]/ram:ApplicableHeaderTradeAgreement[1]/ram:SellerTradeParty[1]" criterion="ram:URIUniversalCommunication/ram:URIID">Seller electronic address MUST be provided [ID PEPPOL-EN16931-R020] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction[1]/ram:ApplicableHeaderTradeAgreement[1]/ram:BuyerTradeParty[1]" criterion="ram:URIUniversalCommunication/ram:URIID">Buyer electronic address MUST be provided [ID PEPPOL-EN16931-R010] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:ExchangedDocument[1]/ram:IncludedNote[1]/ram:Content[1]" criterion="false()">Document MUST not contain empty elements. [ID PEPPOL-EN16931-R008] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction[1]/ram:ApplicableHeaderTradeAgreement[1]/ram:SellerTradeParty[1]/ram:DefinedTradeContact[1]/ram:EmailURIUniversalCommunication[1]/ram:URIID[1]" criterion="false()">Document MUST not contain empty elements. [ID PEPPOL-EN16931-R008] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction[1]/ram:ApplicableHeaderTradeAgreement[1]/ram:BuyerTradeParty[1]/ram:DefinedTradeContact[1]/ram:PersonName[1]" criterion="false()">Document MUST not contain empty elements. [ID PEPPOL-EN16931-R008] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction[1]/ram:ApplicableHeaderTradeAgreement[1]/ram:BuyerTradeParty[1]/ram:DefinedTradeContact[1]/ram:EmailURIUniversalCommunication[1]/ram:URIID[1]" criterion="false()">Document MUST not contain empty elements. [ID PEPPOL-EN16931-R008] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction[1]/ram:ApplicableHeaderTradeAgreement[1]/ram:BuyerTradeParty[1]/ram:PostalTradeAddress[1]/ram:LineOne[1]" criterion="false()">Document MUST not contain empty elements. [ID PEPPOL-EN16931-R008] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction[1]/ram:ApplicableHeaderTradeSettlement[1]/ram:SpecifiedTradeSettlementPaymentMeans[1]/ram:PayeePartyCreditorFinancialAccount[1]/ram:IBANID[1]" criterion="false()">Document MUST not contain empty elements. [ID PEPPOL-EN16931-R008] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction[1]/ram:IncludedSupplyChainTradeLineItem[1]/ram:SpecifiedLineTradeAgreement[1]/ram:GrossPriceProductTradePrice[1]" criterion="not(ram:ChargeAmount) or xs:decimal(../ram:NetPriceProductTradePrice/ram:ChargeAmount) = xs:decimal(ram:ChargeAmount) - xs:decimal(ram:AppliedTradeAllowanceCharge/ram:ActualAmount)">Item net price MUST equal (Gross price - Allowance amount) when gross price is provided. [ID PEPPOL-EN16931-R046] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice" criterion="rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerReference[boolean(normalize-space(.))]">[BR-DE-15] Das Element "Buyer reference" (BT-10) muss ?bermittelt werden. [ID BR-DE-15] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:ExchangedDocumentContext[1]" criterion="ram:GuidelineSpecifiedDocumentContextParameter/ram:ID = $XR-CIUS-ID or ram:GuidelineSpecifiedDocumentContextParameter/ram:ID = $XR-EXTENSION-ID">[BR-DE-21] Das Element "Specification identifier" (BT-24) soll syntaktisch der Kennung des Standards XRechnung entsprechen. [ID BR-DE-21] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction[1]/ram:ApplicableHeaderTradeAgreement[1]/ram:SellerTradeParty[1]/ram:DefinedTradeContact[1]" criterion="ram:TelephoneUniversalCommunication/ram:CompleteNumber[boolean(normalize-space(.))]">[BR-DE-6] Das Element "Seller contact telephone number" (BT-42) muss ?bermittelt werden. [ID BR-DE-6] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction[1]/ram:ApplicableHeaderTradeAgreement[1]/ram:SellerTradeParty[1]/ram:DefinedTradeContact[1]" criterion="ram:EmailURIUniversalCommunication/ram:URIID[boolean(normalize-space(.))]">[BR-DE-7] Das Element "Seller contact email address" (BT-43) muss ?bermittelt werden. [ID BR-DE-7] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction[1]/ram:ApplicableHeaderTradeAgreement[1]/ram:SellerTradeParty[1]/ram:DefinedTradeContact[1]" criterion="matches(normalize-space(ram:TelephoneUniversalCommunication/ram:CompleteNumber), $XR-TELEPHONE-REGEX)">[BR-DE-27] In BT-42 sollen mindestens drei Ziffern enthalten sein. [ID BR-DE-27] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction[1]/ram:ApplicableHeaderTradeAgreement[1]/ram:SellerTradeParty[1]/ram:DefinedTradeContact[1]" criterion="matches(normalize-space(ram:EmailURIUniversalCommunication/ram:URIID), $XR-EMAIL-REGEX)">[BR-DE-28] In BT-43 soll genau ein @-Zeichen enthalten sein, welches nicht von einem Leerzeichen, einem Punkt, aber mindestens zwei Zeichen auf beiden Seiten flankiert werden soll. Ein Punkt sollte nicht am Anfang oder am Ende stehen. [ID BR-DE-28] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
			<notice type="27" location="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction[1]/ram:ApplicableHeaderTradeSettlement[1]/ram:SpecifiedTradeSettlementPaymentMeans[1]" criterion="not(ram:TypeCode = '58') or matches(normalize-space(replace(ram:PayeePartyCreditorFinancialAccount/ram:IBANID, '([ \n\r\t\s])', '')), '^[A-Z]2[0-9]2[a-zA-Z0-9]0 30$') and xs:integer(string-join(for $cp in string-to-codepoints(concat(substring(normalize-space(replace(ram:PayeePartyCreditorFinancialAccount/ram:IBANID, '([ \n\r\t\s])', '')),5),upper-case(substring(normalize-space(replace(ram:PayeePartyCreditorFinancialAccount/ram:IBANID, '([ \n\r\t\s])', '')),1,2)),substring(normalize-space(replace(ram:PayeePartyCreditorFinancialAccount/ram:IBANID, '([ \n\r\t\s])', '')),3,2))) return (if($cp > 64) then string($cp - 55) else string($cp - 48)),'')) mod 97 = 1">[BR-DE-19] "Payment account identifier" (BT-84) soll eine korrekte IBAN enthalten, wenn in "Payment means type code" (BT-81) mit dem Code 58 SEPA als Zahlungsmittel gefordert wird. [ID BR-DE-19] from /xslt/XR_30/XRechnung-CII-validation.xslt)</notice>
		</messages>
		<summary status="valid"/>
	</xml>
	<messages/>
	<summary status="invalid"/>
</validation>

I probably made the mistake, that I created a PDF/A-3b-PDF, so adding an attachment makes it invalid.
Unfortunately I am not that deep into PDFs.

@gulien
Copy link
Collaborator

gulien commented May 14, 2024

Maybe the solution is to add the XML before converting the PDF to PDF/A?

@Cluster2a
Copy link
Author

Cluster2a commented May 14, 2024

I tried that, but the result is the same:
image

I used https://www.pdfforge.org/online/de/pdf-in-pdfa for conversion.

I am not sure how to add thos tags that are missing.

edit: I jsut saw that there are 0 failed rules add 0 failed checks... wondering why this is still invalid.
Maybe of an unknown signature?

eidt: I double checked the working PDF and my PDF (pdfcpu + converrted to PDF/A):
image

image

As you can see, the first image contains the Attributes that are shown as the missing ones.
Is there a way to get this into the pdf?

@gulien
Copy link
Collaborator

gulien commented May 14, 2024

No idea 😅

@gulien gulien added the help wanted Extra attention is needed label May 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed pdf-engines
Projects
None yet
Development

No branches or pull requests

2 participants