bytes, new FlateDecode()); $map = (new CharacterMapFactory($program))->create($fontFile, new UnicodeText(implode('', $lines))); $encoder = new TextEncoder($map); $descriptor = (new FontDescriptorFactory($program))->create($fontFile, new FontDescriptorFlags(4), new NumericType(80)); $font = new Type0Font(new CIDFontType2($descriptor, new CIDToGIDMap($map), new CIDWidths($map)), new ToUnicodeCMap($map)); $resources = new ResourceDictionary(new Font(new DictionaryType(new Entry(new NameType('F1'), new IndirectReference($font))))); $operators = []; foreach ($lines as $index => $line) { $identifier = new MarkedContentIdentifier(new IntegerType($index)); $operators[] = $identifier->begin(new NameType($index === 0 ? 'H1' : 'P')); $operators[] = new Op\BeginTextObject(); $operators[] = new Op\SetTextFontAndSize(new NameType('F1'), new IntegerType($index === 0 ? 19 : 11)); $operators[] = new Op\MoveTextPosition(new IntegerType(40), new IntegerType(770 - 48 * $index)); $operators[] = new Op\ShowText($encoder->encode(new UnicodeText($line))); $operators[] = new Op\EndTextObject(); $operators[] = new Op\EndMarkedContent(); } $page = new PageObject(new Rectangle(new IntegerType(0), new IntegerType(0), new IntegerType(595), new IntegerType(842)), $resources, new StreamObject(new ContentStreams(...$operators)), new Entry(new NameType('StructParents'), new IntegerType(0))); $pages = (new PageTreeNode())->addKid($page); $root = new StructureTreeRoot($pages, namespaces: new StructureNamespaces($pdf20, $pdf17, $report), inclusionRules: new StructureInclusionRules(StructureInclusionCorrections::Resolved336And440)); $document = new StructureElement(new StructureRole(new NameType('Document'), $pdf20)); $container = new MarkedContentContainer($page, new IntegerType(0)); $types = [new StructureRole(new NameType('H1'), $pdf20), new StructureRole(new NameType('ReportParagraph'), $report), new StructureRole(new NameType('Reference'), $pdf17), new StructureRole(new NameType('P'), $pdf20), new StructureRole(new NameType('P'), $pdf20)]; $elements = []; foreach ($types as $index => $type) { $element = new StructureElement($type, $page); $element->addKid(new PageMarkedContent($container, new MarkedContentIdentifier(new IntegerType($index)))); $elements[] = $element; } $toc = new StructureElement(new StructureRole(new NameType('TOC'), $pdf17)); $item = new StructureElement(new StructureRole(new NameType('TOCI'), $pdf17)); $elements[2]->addReference($elements[0]); $item->addKid($elements[2]); $toc->addKid($item); $document->addKid($elements[0])->addKid($elements[1])->addKid($toc)->addKid($elements[3])->addKid($elements[4]); $root->addKid($document); // These are explicit author assertions for this small, manually reviewed sample. // The audit below never inserts a declaration or turns a successful check into // certification. Font program/metric and semantic reviews remain separate evidence. $level = WtPdfConformanceLevel::Reuse; $metadata = new MetadataStream(new XmpPacket( new XmpLanguageAlternative(new XmpName('dc', new MetadataUri('http://purl.org/dc/elements/1.1/'), 'title'), new XmlText('PDF 2.0: explicit structure namespaces'), new XmpLocalizedText('fr', new XmlText('PDF 2.0 : espaces de noms de structure explicites'))), new Declarations(new Declaration(new MetadataUri('https://pdfa.org/declarations#iso32005')), new Declaration(new MetadataUri($level->declarationIdentifier()))), )); $catalog = new DocumentCatalog($pages, ...[...$root->catalogEntries(new MarkInformation(new BooleanType(true))), new MetadataEntry($metadata), new Entry(new NameType('Lang'), new TextString('en-US'))]); $header = new FileHeader('2.0'); $audit = (new WtPdfAudit())->inspect($catalog, new EffectivePdfVersion($header, $catalog), $level); $audit->assertNoDetectedFailures(); // stdout stays binary PDF; the optional --audit-json writes a separate review report. if (in_array('--audit-json', $argv ?? [], true)) { fwrite(STDERR, json_encode(['detectedFailures' => count($audit->failures()), 'pendingReviews' => array_map(static fn ($finding): array => ['clause' => $finding->clause, 'page' => $finding->pdfPage, 'status' => $finding->status->name, 'message' => $finding->message], $audit->pending())], JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT) . PHP_EOL); } $format = in_array('--xref-stream', $argv ?? [], true) ? CrossReferenceFormat::Stream : (in_array('--hybrid', $argv ?? [], true) ? CrossReferenceFormat::Hybrid : CrossReferenceFormat::Table); $file = new FileStructure($header, new FileBody($catalog), new IndirectReference($catalog), crossReferenceFormat: $format); if (isset($argv[1]) && !str_starts_with($argv[1], '--')) { $file->writeTo($argv[1]); } else { echo $file->toPdf(); }