Skip to content

embedInPdf produces invalid PDF/A-3 - Multiple validation errors #110

@pseuss

Description

@pseuss

To Reproduce

The embedInPdf function produces PDFs that fail ZUGFeRD/Factur-X validation. Even with a minimal blank PDF created with pdf-lib, the resulting PDF has numerous PDF/A-3 compliance issues.

Environment

Reproduction

Minimal test script:

import { zugferd } from "node-zugferd";
import { BASIC } from "node-zugferd/profile/basic";
import { writeFileSync } from "fs";
import { PDFDocument } from "pdf-lib";

async function testZugferd() {
  const invoicer = zugferd({ profile: BASIC });

  const data = {
    number: "TEST-2024-001",
    typeCode: "380",
    issueDate: new Date(),
    businessProcessType: "A1",
    transaction: {
      tradeAgreement: {
        buyerReference: "N/A",
        seller: {
          name: "Test Firma GmbH",
          postalAddress: {
            countryCode: "DE",
            postCode: "12345",
            line1: "Teststraße 1",
            city: "Berlin",
          },
          taxRegistration: { vatIdentifier: "DE123456789" },
        },
        buyer: {
          name: "Kunde AG",
          postalAddress: {
            countryCode: "DE",
            postCode: "54321",
            line1: "Kundenweg 2",
            city: "München",
          },
        },
      },
      tradeDelivery: {
        information: { deliveryDate: new Date() },
      },
      tradeSettlement: {
        currencyCode: "EUR",
        paymentInstruction: {
          typeCode: "58",
          transfers: [{ paymentAccountIdentifier: "DE89370400440532013000" }],
        },
        vatBreakdown: [
          {
            calculatedAmount: 19,
            typeCode: "VAT",
            basisAmount: 100,
            categoryCode: "S",
            rateApplicablePercent: 19,
          },
        ],
        paymentTerms: {
          dueDate: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000),
          description: "Zahlbar innerhalb von 14 Tagen",
        },
        monetarySummation: {
          lineTotalAmount: 100,
          chargeTotalAmount: 0,
          allowanceTotalAmount: 0,
          taxBasisTotalAmount: 100,
          taxTotal: { amount: 19, currencyCode: "EUR" },
          grandTotalAmount: 119,
          duePayableAmount: 119,
        },
      },
      line: [
        {
          identifier: "1",
          tradeProduct: { name: "Testprodukt" },
          tradeAgreement: { netTradePrice: { chargeAmount: 100 } },
          tradeDelivery: {
            billedQuantity: { amount: 1, unitMeasureCode: "H87" },
          },
          tradeSettlement: {
            tradeTax: {
              typeCode: "VAT",
              categoryCode: "S",
              rateApplicablePercent: 19,
            },
            monetarySummation: { lineTotalAmount: 100 },
          },
        },
      ],
    },
    includedNote: [{ content: "Testrechnung" }],
  };

  const invoice = invoicer.create(data);

  // Create blank PDF
  const blankPdf = await PDFDocument.create();
  blankPdf.addPage([595.28, 841.89]);
  const blankPdfBytes = await blankPdf.save();

  const zugferdPdf = await invoice.embedInPdf(blankPdfBytes, {
    metadata: {
      title: "Test Rechnung TEST-2024-001",
      author: "Test Firma GmbH",
      producer: "Invoice",
      creator: "Invoice",
      createDate: new Date(),
    },
  });

  writeFileSync("test-zugferd.pdf", zugferdPdf);
}

testZugferd();

Validation Errors

The generated PDF fails validation with the following errors:

PDF/A-3 Compliance Issues

  1. [VD-Valitool-H-018] - Document must conform to PDF/A-3 or PDF/A-4 standard
  2. EmbeddedFiles key error - File name dictionary must not contain EmbeddedFiles key (PDF/A-1 restriction)
  3. EF key error - File specification dictionary must not contain EF key (PDF/A-1 restriction)
  4. Xref-Streams error - Document uses Xref-Streams which are not allowed in PDF/A-1

XMP Metadata Issues

  1. [VD-Valitool-H-005] - Invalid XMP data
  2. Producer mismatch - Document Info "Producer" ≠ XMP "pdf:Producer"
  3. Author mismatch - Document Info "Author" ≠ XMP "dc:creator"
  4. Creator mismatch - Document Info "Creator" ≠ XMP "xmp:CreatorTool"
  5. Title mismatch - Document Info "Title" ≠ XMP "dc:title"
  6. Missing PDF/A identification - Metadata stream missing PDF/A Identification Extension Schema
  7. XMP serialization error - Metadata stream not conformant to XMP specification

ICC Profile Issues

  1. ICC Profile N entry - N entry in ICC profile dictionary missing or doesn't match component count

Factur-X/ZUGFeRD Specific Issues

  1. [VD-Valitool-H-007] - Embedded file has MIME type application/xml but text/xml is required
  2. [VD-Valitool-H-008] - XMP metadata missing required ZUGFeRD/Factur-X information
  3. [VD-Valitool-H-013] - Namespace format in XMP not standard-conformant
  4. [VD-Valitool-H-015] - Namespace prefix must be fx (since Factur-X/ZUGFeRD 2.1)
  5. [VD-Valitool-H-019] - DocumentType not found in XMP (should be INVOICE)
  6. [VD-Valitool-H-020] - DocumentType from PDF () doesn't match XML (INVOICE)
  7. [VD-Valitool-H-022] - ConformanceLevel from PDF () doesn't match XML (BASIC)
  8. [VD-Valitool-H-023] - Version value in PDF is invalid

Expected Behavior

The embedInPdf function should produce a valid PDF/A-3b document with:

  1. Correct PDF/A-3 structure (not PDF/A-1)
  2. Consistent Document Info Dictionary and XMP metadata
  3. Valid ICC profile with correct N entry
  4. Correct MIME type text/xml for the embedded XML
  5. Complete Factur-X XMP extension schema with:
    • fx:DocumentType = "INVOICE"
    • fx:DocumentFileName = "factur-x.xml"
    • fx:Version = "1.0"
    • fx:ConformanceLevel = "BASIC"

Additional Context

The XML generation (toXML()) works correctly and validates. Only the PDF embedding is broken.

The validator reports the PDF as "PDF/A-1B" format, suggesting the library is not correctly setting PDF/A-3 markers.

Current vs. Expected behavior

What version of node-zugferd are you using?

0.1.0 (also tested with 0.1.1-beta.1)

Provide environment information

-

Which area(s) are affected? (Select all that apply)

Other

Config (if applicable)

import { zugferd } from "node-zugferd";
import { BASIC } from "node-zugferd/profile/basic";

export const invoicer = zugferd({
  profile: BASIC
})

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions