te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>java - ZUGFeRD Mustang Validator Gives Font Reference Error - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - ZUGFeRD Mustang Validator Gives Font Reference Error - Stack Overflow

programmeradmin3浏览0评论

We are working on implementing e-invoicing in Germany using ZUGFeRD and PDF/A. When validating our very simple PDF with the Mustag Validator (/) we end up getting this error message:

<?xml version="1.0" encoding="UTF-8"?>

<validation filename="Beleg_245210299_20241201_out.pdf" datetime="2025-02-14 13:16:52">
  <pdf>ValidationResult [flavour=3u, totalAssertions=695, assertions=[TestAssertion [ruleId=RuleId [specification=ISO 19005-3:2012, clause=6.2.2, testNumber=2], status=failed, message=A content stream that references other objects, such as images and fonts that are necessary to fully render or process the stream, shall have an explicitly associated Resources dictionary as described in ISO 32000-1:2008, 7.8.3, location=Location [level=CosDocument, context=root/document[0]/pages[0](9 0 obj PDPage)/contentStream[0](14 0 obj PDContentStream)/operators[0]/xObject[0]/contentStream[0](17 0 obj PDContentStream)], locationContext=null, errorMessage=A content stream refers to resource(s) F1 not defined in an explicitly associated Resources dictionary]], isCompliant=false]
    <info>
      <signature>unknown</signature>
      <duration unit="ms">652</duration>
    </info>
    <summary status="invalid"/>
  </pdf>  
  <xml>
    <info>
      <version>2</version>
      <profile>urn:cen.eu:en16931:2017</profile>
      <validator version="2.16.2"/>
      <rules>
        <fired>177</fired>
        <failed>0</failed>
      </rules>
      <duration unit="ms">1270</duration>
    </info>
    <summary status="valid"/>
  </xml>
  <summary status="invalid"/>
</validation>

However, if we were to delete the header from our PDF where it says "Blatt: 1 von 1", the validator error goes away. Therefore it must have something to do with how the header is referencing the fonts, right? At least that's what I'm thinking.

Our source PDF looks like this:
Source PDF

Download it from here:

Our target PDF including the ZUGFeRD XML can be downloaded from here:

Can you tell me why Mustag throws the error A content stream that references other objects, such as images and fonts that are necessary to fully render or process the stream, shall have an explicitly associated Resources dictionary as described in ISO 32000-1:2008?

The spec (.pdf page 8) is not really helpful here.

We embedded this example XML into the PDF/A file using PDFBox to create a "valid" ZUGFeRD file.

.xml

However, when running the Mustang validator using this command

$ java  -jar Mustang-CLI-2.16.2.jar --action validate --no-notice --source Beleg_245210299_20241201_out.pdf

we end up getting the error message above.

To attach the XML, we use the following code, where data contains the UTF-8 bytes of the XML to attach:

    private void attachFile(String filename, String relationship, String description, String subType, byte[] data) throws IOException {
        this.fileAttached = true;

        PDComplexFileSpecification fs = new PDComplexFileSpecification();
        fs.setFile(filename);

        COSDictionary dict = fs.getCOSObject();
        dict.setName("AFRelationship", relationship);
        dict.setString("UF", filename);
        dict.setString("Desc", description);

        ByteArrayInputStream bais = new ByteArrayInputStream(data);
        PDEmbeddedFile ef = new PDEmbeddedFile(pdf, bais);
        ef.setSubtype(subType);
        ef.setSize(data.length);
        ef.setCreationDate(Calendar.getInstance());
        ef.setModDate(Calendar.getInstance());

        fs.setEmbeddedFile(ef);
        dict = fs.getCOSObject();

        COSDictionary efDict = (COSDictionary) dict.getDictionaryObject(COSName.EF);
        COSBase lowerLevelFile = efDict.getItem(COSName.F);
        efDict.setItem(COSName.UF, lowerLevelFile);

        PDDocumentNameDictionary names = new PDDocumentNameDictionary(pdf.getDocumentCatalog());
        PDEmbeddedFilesNameTreeNode efTree = names.getEmbeddedFiles();
        if (efTree == null) {
            efTree = new PDEmbeddedFilesNameTreeNode();
        }

        Map<String, PDComplexFileSpecification> namesMap = new HashMap<>();
        Map<String, PDComplexFileSpecification> oldNamesMap = efTree.getNames();
        if (oldNamesMap != null) {
            namesMap.putAll(oldNamesMap);
        }

        namesMap.put(filename, fs);
        efTree.setNames(namesMap);
        names.setEmbeddedFiles(efTree);
        pdf.getDocumentCatalog().setNames(names);

        COSBase afEntry = pdf.getDocumentCatalog().getCOSObject().getItem("AF");
        COSArray cosArray;
        if (afEntry == null) {
            cosArray = new COSArray();
            cosArray.add(fs);
            pdf.getDocumentCatalog().getCOSObject().setItem("AF", cosArray);
        } else if (afEntry instanceof COSArray) {
            cosArray = (COSArray) afEntry;
            cosArray.add(fs);
            pdf.getDocumentCatalog().getCOSObject().setItem("AF", cosArray);
        } else {
            if (!(afEntry instanceof COSObject) || !(((COSObject) afEntry).getObject() instanceof COSArray)) {
                throw new IOException("Unexpected object type for PDFDocument/Catalog/COSDictionary/Item(AF)");
            }
            cosArray = (COSArray) ((COSObject) afEntry).getObject();
            cosArray.add(fs);
        }
    }

We call the method above like this:

        this.attachFile(filename, "Alternative",
                "Invoice metadata conforming to ZUGFeRD standard (.php?idcat=231&lang=4)",
                "text/xml", this.xmlProvider.getData());

We are working on implementing e-invoicing in Germany using ZUGFeRD and PDF/A. When validating our very simple PDF with the Mustag Validator (https://www.mustangproject./commandline/) we end up getting this error message:

<?xml version="1.0" encoding="UTF-8"?>

<validation filename="Beleg_245210299_20241201_out.pdf" datetime="2025-02-14 13:16:52">
  <pdf>ValidationResult [flavour=3u, totalAssertions=695, assertions=[TestAssertion [ruleId=RuleId [specification=ISO 19005-3:2012, clause=6.2.2, testNumber=2], status=failed, message=A content stream that references other objects, such as images and fonts that are necessary to fully render or process the stream, shall have an explicitly associated Resources dictionary as described in ISO 32000-1:2008, 7.8.3, location=Location [level=CosDocument, context=root/document[0]/pages[0](9 0 obj PDPage)/contentStream[0](14 0 obj PDContentStream)/operators[0]/xObject[0]/contentStream[0](17 0 obj PDContentStream)], locationContext=null, errorMessage=A content stream refers to resource(s) F1 not defined in an explicitly associated Resources dictionary]], isCompliant=false]
    <info>
      <signature>unknown</signature>
      <duration unit="ms">652</duration>
    </info>
    <summary status="invalid"/>
  </pdf>  
  <xml>
    <info>
      <version>2</version>
      <profile>urn:cen.eu:en16931:2017</profile>
      <validator version="2.16.2"/>
      <rules>
        <fired>177</fired>
        <failed>0</failed>
      </rules>
      <duration unit="ms">1270</duration>
    </info>
    <summary status="valid"/>
  </xml>
  <summary status="invalid"/>
</validation>

However, if we were to delete the header from our PDF where it says "Blatt: 1 von 1", the validator error goes away. Therefore it must have something to do with how the header is referencing the fonts, right? At least that's what I'm thinking.

Our source PDF looks like this:
Source PDF

Download it from here: https://drive.google/file/d/1oeLoXZYnTYijc4WbiBsbacnVkiyx2ixO/view?usp=sharing

Our target PDF including the ZUGFeRD XML can be downloaded from here: https://drive.google/file/d/1J-tyiKhBrffZXRoD6wK908azo8JoN3k4/view?usp=sharing

Can you tell me why Mustag throws the error A content stream that references other objects, such as images and fonts that are necessary to fully render or process the stream, shall have an explicitly associated Resources dictionary as described in ISO 32000-1:2008?

The spec (https://pdfa./wp-content/uploads/2017/07/TechNote0010.pdf page 8) is not really helpful here.

We embedded this example XML into the PDF/A file using PDFBox to create a "valid" ZUGFeRD file.

https://www.mustangproject./files/ZUGFeRD-invoice.xml

However, when running the Mustang validator using this command

$ java  -jar Mustang-CLI-2.16.2.jar --action validate --no-notice --source Beleg_245210299_20241201_out.pdf

we end up getting the error message above.

To attach the XML, we use the following code, where data contains the UTF-8 bytes of the XML to attach:

    private void attachFile(String filename, String relationship, String description, String subType, byte[] data) throws IOException {
        this.fileAttached = true;

        PDComplexFileSpecification fs = new PDComplexFileSpecification();
        fs.setFile(filename);

        COSDictionary dict = fs.getCOSObject();
        dict.setName("AFRelationship", relationship);
        dict.setString("UF", filename);
        dict.setString("Desc", description);

        ByteArrayInputStream bais = new ByteArrayInputStream(data);
        PDEmbeddedFile ef = new PDEmbeddedFile(pdf, bais);
        ef.setSubtype(subType);
        ef.setSize(data.length);
        ef.setCreationDate(Calendar.getInstance());
        ef.setModDate(Calendar.getInstance());

        fs.setEmbeddedFile(ef);
        dict = fs.getCOSObject();

        COSDictionary efDict = (COSDictionary) dict.getDictionaryObject(COSName.EF);
        COSBase lowerLevelFile = efDict.getItem(COSName.F);
        efDict.setItem(COSName.UF, lowerLevelFile);

        PDDocumentNameDictionary names = new PDDocumentNameDictionary(pdf.getDocumentCatalog());
        PDEmbeddedFilesNameTreeNode efTree = names.getEmbeddedFiles();
        if (efTree == null) {
            efTree = new PDEmbeddedFilesNameTreeNode();
        }

        Map<String, PDComplexFileSpecification> namesMap = new HashMap<>();
        Map<String, PDComplexFileSpecification> oldNamesMap = efTree.getNames();
        if (oldNamesMap != null) {
            namesMap.putAll(oldNamesMap);
        }

        namesMap.put(filename, fs);
        efTree.setNames(namesMap);
        names.setEmbeddedFiles(efTree);
        pdf.getDocumentCatalog().setNames(names);

        COSBase afEntry = pdf.getDocumentCatalog().getCOSObject().getItem("AF");
        COSArray cosArray;
        if (afEntry == null) {
            cosArray = new COSArray();
            cosArray.add(fs);
            pdf.getDocumentCatalog().getCOSObject().setItem("AF", cosArray);
        } else if (afEntry instanceof COSArray) {
            cosArray = (COSArray) afEntry;
            cosArray.add(fs);
            pdf.getDocumentCatalog().getCOSObject().setItem("AF", cosArray);
        } else {
            if (!(afEntry instanceof COSObject) || !(((COSObject) afEntry).getObject() instanceof COSArray)) {
                throw new IOException("Unexpected object type for PDFDocument/Catalog/COSDictionary/Item(AF)");
            }
            cosArray = (COSArray) ((COSObject) afEntry).getObject();
            cosArray.add(fs);
        }
    }

We call the method above like this:

        this.attachFile(filename, "Alternative",
                "Invoice metadata conforming to ZUGFeRD standard (http://www.ferd-net.de/front_content.php?idcat=231&lang=4)",
                "text/xml", this.xmlProvider.getData());
Share Improve this question edited Feb 17 at 13:26 Tilman Hausherr 18.9k8 gold badges65 silver badges105 bronze badges asked Feb 17 at 11:36 TheSmounTheSmoun 31 silver badge3 bronze badges New contributor TheSmoun is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
Add a comment  | 

2 Answers 2

Reset to default 3

The Page dictionary looks like this:

8 0 obj
<<
  /Type /Page
  /Parent 3 0 R
  /Resources 4 0 R
  /Contents 9 0 R
  /MediaBox [ 0 0 595.299988 841.900024 ]
  /CropBox [ 0 0 595.299988 841.900024 ]
  /Rotate 0
>>
endobj

Its Resources dictionary is:

4 0 obj
<<
  /ProcSet [ /PDF /Text ]
  /Font <<
    /F1 11 0 R
  >>
  /XObject <<
    /fx0 10 0 R
  >>
>>
endobj

Notice that it uses a Form XObject 'fx0' which is object 10:

10 0 obj
<<
  /Type /XObject
  /Subtype /Form
  /FormType 1
  /BBox [ 0 0 595.299988 841.900024 ]
  /Length 235
>>
stream
0 0 0 RG
0 0 0 rg
BT
1 0 0 1 483.844 806.366 Tm
/F1
 7 Tf

Now that content stream (the Form XObject content stream) uses the font /F1, but the Form XObject dictionary does not contain a Resources dictionary, so how do we know which font to use ?

In general PDF it is permissible to 'inherit' the Resource definition from the 'enclosing context', in this case the Page dictionary, but this can lead to strange results if, for example, the same Form is called from different pages, and /F1 is defined as a different Resource on each page.

So for PDF/A it is a requirement that any stream dictionary must include a Resources dictionary for any Resources which it uses.

Note that the 'spec' you are referencing is just a tech note clarifying certain areas of the actual specification which are unclear. The actual specification is an ISO document, and costs money :-(

However, page 8 of the tech note is the relevant important point 'Explicitly defined named resources (A002)'. The important point is

As the intention of both ISO 19005-2 and 19005-3 is to forbid the practice of defining resources implicitly, it would be better to clarify this explicitly.

The tech note goes on to say that the spec should be read as if this were the case.

TL;DR your Form XObject dictionary needs a Resources dictionary which contains a definition of the Font /F1.

This code fixes your file, and files that have the same structure.

PDDocument doc = Loader.loadPDF(new File("Beleg_245210299_20241201_out.pdf"));
for (PDPage page : doc.getPages())
{
    PDResources resources = page.getResources();
    for (COSName name : resources.getXObjectNames())
    {
        PDXObject xObject = resources.getXObject(name);
        if (xObject instanceof PDFormXObject)
        {
            PDFormXObject form = (PDFormXObject) xObject;
            if (form.getResources() != null)
            {
                continue;
            }
            form.setResources(resources);
        }
    }
}
doc.save(new File("Beleg_245210299_20241201_out-modified.pdf"));
doc.close();
发布评论

评论列表(0)

  1. 暂无评论