Le supplément Adobe de juin 2008 étend PDF 1.7 au niveau Adobe 3. Une extension conserve le format PDF de base et déclare les propriétés supplémentaires que le lecteur peut rencontrer.

Cet exemple utilise les déclarations d'extension et un artefact Bates. Les numéros Bates sont des identifiants choisis par l'application pour indexer des documents, par exemple DOC-000001 et EXH-000019. Ils ne sont pas nécessairement les numéros des pages du fichier.

Exécuter l'exemple

php examples/adobe-extensions.php adobe-extensions.pdf

Le script complet est commenté étape par étape. Le résultat contient une page et deux identifiants affichés en pied de page. Ce tutoriel dépend uniquement du core.

Déclarer l'extension

$extensions = new DeveloperExtensions(new DeveloperExtensionEntry(
    new DeveloperPrefix(new NameType('ADBE')),
    new DeveloperExtension(new NameType('1.7'), new IntegerType(3)),
));
$catalog = new DocumentCatalog($pages, new Extensions($extensions));

DeveloperPrefix conserve le préfixe sensible à la casse. ADBE identifie Adobe. DeveloperExtension assemble /BaseVersion /1.7 et /ExtensionLevel 3. DeveloperExtensionEntry associe le préfixe à cette instance. Les déclarations d'extension et toutes leurs entrées restent directes, comme l'exige ISO 32000-1, §7.12.1 : aucun objet indirect n'intervient dans cette partie du catalogue. Enfin, Extensions place ce dictionnaire dans le catalogue du document.

La version de base déclarée décrit la spécification étendue. Elle n'est pas calculée à partir de l'en-tête du fichier. Le modèle permet plusieurs préfixes, avec des versions de base et des niveaux indépendants. La bibliothèque ne procède pas à une inscription automatique dans un registre de préfixes.

Construire l'artefact Bates

$numbers = new ArrayType(
    new LiteralString('DOC-000001'),
    new LiteralString('EXH-000019'),
);
$bates = new BatesArtifact($numbers);

L'objet produit /Type /Pagination, /Subtype /BatesN et /Contents. Contents accepte une chaîne pour un seul identifiant, ou un tableau de chaînes pour plusieurs identifiants. Leur ordre reste celui choisi par l'application. Les valeurs sont des objets PDF ; elles ne peuvent pas être remplacées par des entiers ou des dictionnaires via une entrée générique.

Relier les propriétés au dessin

new BeginMarkedContentWithProperties(new NameType('Artifact'), $bates),
// BT, Tf, Td et Tj dessinent ici le pied de page.
new EndMarkedContent(),

BDC ouvre la séquence marquée /Artifact avec ses propriétés ; EMC la ferme. Les opérateurs texte ordinaires placent et dessinent les identifiants. Les propriétés Bates décrivent ce contenu, mais ne créent aucun texte visible à elles seules. L'exemple fournit donc les numéros aux propriétés et au texte affiché.

ContentStreams contrôle l'équilibre des séquences marquées à l'écriture. Le contrôle indépendant de l'exemple vérifie aussi la déclaration /ADBE, la classification /BatesN, l'ordre des deux chaînes et leur présence visible.

Expériences et références

  • Remplacer le tableau de chaînes par une seule chaîne et modifier le texte affiché.
  • Ajouter un préfixe de développeur avec son propre niveau pour observer les deux déclarations.
  • Déplacer le pied de page en modifiant les coordonnées fournies à MoveTextPosition.

Le mécanisme de déclaration figure aux pages 23–25 du supplément et dans ISO 32000-1, §7.12. L'artefact Bates figure au tableau 10.17, page 100 du supplément conservé dans le core. Le guide du supplément détaille la couverture des autres extensions et les règles de provenance.

Voir le résultat PDF

Ce fichier a été généré avec le code de cette documentation. Toutes ses pages sont reproduites ci-dessous ; ouvrir le PDF pour utiliser ses liens et ses signets.

Résultat PDF adobe-extensions : page 1 sur 1
Page 1 sur 1
Utiliser le lecteur PDF du navigateur

Ce lecteur dépend du navigateur. Si le document ne s’affiche pas, utiliser « Ouvrir le PDF » ou « Télécharger ».

Code source complet

Code de roukmoute/pdf · Consulter la source au commit documenté ↗

Lire le script commenté : adobe-extensions.php
<?php

declare(strict_types=1);

use PDF\Document\Artifacts\BatesArtifact;
use PDF\Document\Extensions\DeveloperExtension;
use PDF\Document\Extensions\DeveloperExtensionEntry;
use PDF\Document\Extensions\DeveloperExtensions;
use PDF\Document\Extensions\DeveloperPrefix;
use PDF\Document\Fonts\StandardFont;
use PDF\Document\Fonts\Type1Font;
use PDF\Document\ResourceDictionary;
use PDF\Document\Resources\Font;
use PDF\FileStructure;
use PDF\Syntax\ContentStreamsResources\ContentStreams;
use PDF\Syntax\ContentStreamsResources\Operators\BeginMarkedContentWithProperties;
use PDF\Syntax\ContentStreamsResources\Operators\BeginTextObject;
use PDF\Syntax\ContentStreamsResources\Operators\EndMarkedContent;
use PDF\Syntax\ContentStreamsResources\Operators\EndTextObject;
use PDF\Syntax\ContentStreamsResources\Operators\MoveTextPosition;
use PDF\Syntax\ContentStreamsResources\Operators\SetTextFontAndSize;
use PDF\Syntax\ContentStreamsResources\Operators\ShowText;
use PDF\Syntax\DataStructure\Rectangle;
use PDF\Syntax\DocumentStructure\CatalogDictionary\Extensions;
use PDF\Syntax\DocumentStructure\DocumentCatalog;
use PDF\Syntax\DocumentStructure\PageTree\PageObject;
use PDF\Syntax\DocumentStructure\PageTree\PageTreeNode;
use PDF\Syntax\FileStructure\FileBody;
use PDF\Syntax\FileStructure\FileHeader;
use PDF\Syntax\Object\ArrayType;
use PDF\Syntax\Object\DictionaryObject\Entry;
use PDF\Syntax\Object\DictionaryType;
use PDF\Syntax\Object\IndirectReference;
use PDF\Syntax\Object\IntegerType;
use PDF\Syntax\Object\NameType;
use PDF\Syntax\Object\NumericType;
use PDF\Syntax\Object\StreamObject;
use PDF\Syntax\Object\StringObject\LiteralString;

require dirname(__DIR__) . '/vendor/autoload.php';

// Tutorial 13: docs/tutorials/13-adobe-extensions.md.
// The catalog declares the Adobe level used; the page stream then realizes
// one of its extensions, Bates numbering (supplement, p.100).
$extensions = new DeveloperExtensions(new DeveloperExtensionEntry(
    new DeveloperPrefix(new NameType('ADBE')),
    new DeveloperExtension(new NameType('1.7'), new IntegerType(3)),
));

$font = new Type1Font(StandardFont::Helvetica);
$resources = new ResourceDictionary(new Font(new DictionaryType(
    new Entry(new NameType('F1'), new IndirectReference($font)),
)));

// Bates properties describe numbers already chosen by the application.
// They draw nothing. BDC/EMC classify the drawn text as a pagination
// artifact, distinct from document content.
$numbers = new ArrayType(new LiteralString('DOC-000001'), new LiteralString('EXH-000019'));
$bates = new BatesArtifact($numbers);
$contents = new StreamObject(new ContentStreams(
    new BeginTextObject(),
    new SetTextFontAndSize(new NameType('F1'), new NumericType(24)),
    new MoveTextPosition(new NumericType(64), new NumericType(716)),
    new ShowText(new LiteralString('PDFToolkit - extensions Adobe')),
    new SetTextFontAndSize(new NameType('F1'), new NumericType(13)),
    new MoveTextPosition(new NumericType(0), new NumericType(-46)),
    new ShowText(new LiteralString('PDF 1.7, Adobe ExtensionLevel 3')),
    new MoveTextPosition(new NumericType(0), new NumericType(-30)),
    new ShowText(new LiteralString('Le catalogue contient /Extensions /ADBE.')),
    new MoveTextPosition(new NumericType(0), new NumericType(-26)),
    new ShowText(new LiteralString('Chaque numero Bates reste un objet chaine explicite.')),
    new MoveTextPosition(new NumericType(0), new NumericType(-26)),
    new ShowText(new LiteralString('Leur liste est fournie par /Contents dans un artefact.')),
    new EndTextObject(),
    // The same position can carry several document indices.
    new BeginMarkedContentWithProperties(new NameType('Artifact'), $bates),
    new BeginTextObject(),
    new SetTextFontAndSize(new NameType('F1'), new NumericType(11)),
    new MoveTextPosition(new NumericType(64), new NumericType(48)),
    new ShowText(new LiteralString('DOC-000001  |  EXH-000019')),
    new EndTextObject(),
    new EndMarkedContent(),
));
$page = new PageObject(
    new Rectangle(new NumericType(0), new NumericType(0), new NumericType(612), new NumericType(792)),
    $resources,
    $contents,
);
$pages = (new PageTreeNode())->addKid($page);
$catalog = new DocumentCatalog($pages, new Extensions($extensions));
$file = new FileStructure(new FileHeader('1.7'), new FileBody($catalog), new IndirectReference($catalog));
$path = $argv[1] ?? __DIR__ . '/adobe-extensions.pdf';
$file->writeTo($path);
echo 'PDF written to ' . $path . PHP_EOL;

Source de ce guide au commit documenté ↗