Le présent article explique aux développeurs comment créer des documents PDF lorsque l’information est structurée en plusieurs couches afin de permettre aux utilisateurs d’afficher ou de cacher des couches individuelles.
Ces techniques peuvent être utilisées pour concevoir des dessins, créer des oeuvres architecturales ou créer des documents multilingues où chaque langue est une couche unique du PDF.
Concepts de base
Les couches PDF sont appelées « Optional Content » ou Contenu Optionnel dans les spécifications du langage PDF. Cette fonctionnalité a été ajoutée à la version PDF-1.5 (Acrobat 6) du langage PDF et permet de cacher certaines parties d’un document ou d’en afficher d’autres.
Une couche est définie par un identifiant numérique et un intitulé. Seul l’intitulé est visible à l’utilisateur du document PDF. Les couches peuvent être structurées en un arbre ou une hiérarchie de couches. Voici un exemple d’un objet de type Contenu Optionnel dans un PDF :
9 0 obj <</Type/OCG/Name(Couche Bleue) >> endobj
Chaque page qui utilise cette couche ajoute à ses ressources une référence à cette couche, exemple :
6 0 obj <</Type /Page /Parent 3 0 R /MediaBox [0 0 612 792] /Contents [7 0 R ]
/Resources << /ProcSet [/PDF /Text] /Font <</F10 10 0 R >>
/Properties <</OC9 9 0 R >>
>> >> endobj
OC9 est l’identifiant de la couche et (Couche Bleue) est l’intitulé (Rappel : le texte en langage PDF est entouré de parenthèses). Pour indiquer que certains objets d’une page appartiennent à une couche spécifique, la syntaxe suivante est utilisée :
% commencer la couche bleue contenant le texte : Page 1
/OC /OC9 BDC
BT /F10 5.76 Tf 1 0 0 1 24 745.56 Tm 0.026 Tc -0.067 Tw (Page 1) Tj ET
% commencer une sous-couche contenant un rectangle rempli
/OC /OC11 BDC
n 1 g 96 326.96 35.76 -6.6 re f*
% fin de la sous-couche 1
EMC
% commencer une autre sous-couche contenant un autre rectangle
/OC /OC13 BDC
n 1 g 96 526.96 35.76 -6.6 re f*
% fin de la sous-couche 2
EMC
%fin de la couche bleue
EMC
Même si le code montre une couche principale et deux sous-couches, ceci n’est pas suffisant pour définir la hiérarchie de couches. La chaîne de caractères « Order » est utilisée pour déterminer l’arborescence des couches. Cette chaîne fait partie du catalogue principal du document. Dans le cas précédent, l’entrée du catalogue ressemblerait à :
/Order [ 9 0 R [11 0 R 13 0 R] ]
Les identifiants des couches sont nécessaires pour créer la chaîne « Order ». Cette chaîne peut par contre contenir des nœuds qui ne font pas référence à une couche mais définissent uniquement un groupe de sous-couches. Dans l’exemple précédent, nous pourrions avoir toutes les couches affichées sous une couche parent nommée (PDF multi-couches) en utilisant :
/Order [ (PDF multi-couches) 9 0 R [11 0 R 13 0 R] ]
Il est important que la chaîne « Order » corresponde bien à la hiérarchie des couches telles qu’elles apparaissent dans le contenu de la page. Dans le cas contraire, le lecteur PDF ne sera pas en mesure d’afficher ou de cacher les sous-couches en même temps que les couches parentes.
Note: Il existe une confusion dans les spécifications PDF relative à la définition des nœuds qui sont des parents d’autres couches. Pour définir (PDF multi-couches) comme étant le parent de la couche 9, il ne faut pas utiliser [(PDF multi-couches) [9 0 R] ] comme nous pourrions s’y attendre mais [(PDF multi-couches) 9 0 R] comme si les deux nœuds étaient au même niveau hiérarchique.
Le format PDF permet la création de groupes de couches qui sont mutuellement exclusifs où l’affichage d’une couche en cache automatiquement une autre. Ceux-ci sont appelés « radio-button groups » et définis en utilisant le mot clé « RBGroups » en PDF. Il est possible aussi de définir quelles couches sont visibles ou cachées par défaut au moment de l’ouverture du document, exemple :
/RBGroups [[11 0 R 13 0 R]] /ON[ 11 0 R] /OFF [13 0 R] indique que les couches 11 et 13 sont groupées et que la couche 11 est visible par défaut alors que la couche 13 est invisible.
Les développeurs ont besoin des définir les intitulés des couches uniquement sans se soucier des identifiants. Les identifiants des objets et des couches n’ont pas de signification pour le développeur externe et l’API proposée n’utilisera que les intitulés pour définir complètement la hiérarchie de couches.
Les couches peuvent être insérées dans le document PDF pendant que le document est imprimé en utilisant les appels « Escape » à l’imprimante. Les appels « Escape » sont des API de Windows qui sont utilisées spécifiquement pour envoyer des données particulières au pilote d’impression.
La première étape consiste à vérifier que l’imprimante répond aux fonctions de création de PDF en couches telles que définies par Amyuni. Les appels suivants sont nécessaires :
// vérifier que l’imprimante PDF peut faire des Layers
#define ESCAPE_SETLAYER 248
CHAR technology[4];
int escape = ESCAPE_SETLAYER;
ExtEscape( hDC, GETTECHNOLOGY, 0, NULL, sizeof(technology), (LPSTR)technology
);
// la technologie devrait être PDF
if ( lstrcmp(technology, "PDF") )
{
MessageBox( 0, "Ceci n’est pas une imprimante Amyuni PDF", "Erreur", MB_ICONERROR );
}
// aussi vérifier que l’imprimante répond à SETLAYER
if ( !ExtEscape( hDC, QUERYESCSUPPORT, sizeof(escape), (LPCSTR)&escape, 0,
NULL ) )
{
MessageBox( 0, " Ceci n’est pas une imprimante Amyuni PDF ", "Erreur", MB_ICONERROR );
}
GETTECHNOLOGY et QUERYESCSUPPORT sont définies par Windows GDI. ESCAPE_SETLAYER est un appel privé auquel répond l’imprimante Amyuni PDF version 4 et plus.
L’appel à SETLAYER prend un seul paramètre qui consiste en une chaîne de caractères en Unicode. Cet appel peut être contenu dans une fonction utilitaire telle que :
void SetLayer( HDC hDC, LPWSTR LayerInfo )
{
switch ( ExtEscape( hDC, ESCAPE_SETLAYER, (int)((wcslen(LayerInfo) + 1)
* sizeof(LayerInfo[0])), (LPCSTR)LayerInfo, 0, NULL ) )
{
default:
// une valeur positive indique un succès
case 0:
// pas d’erreur
return;
case -1:
// erreur, la couche a été fermée alors qu’aucune couche n’est ouverte
break;
case -2:
// erreur d’allocation de mémoire (peut aussi indiquer un paramètre invalide)
break;
}
}
L’appel au même « Escape » est nécessaire pour commencer une couche ou une sous-couche, pour terminer une couche ou définir la hiérarchie de couches.
L’ordre ou la hiérarchie de couches est envoyé à l’imprimante PDF en utilisant SETLAYER avant l’appel à StartDoc. En fait, l’appel peut se faire à n’importe quel moment à condition que ce soit fait en dehors d’un bloc de StartPage/EndPage mais avant l’appel à EndDoc.
L’avantage de définir la hiérarchie en début du document est que cela permet à l’imprimante de générer un en-tête PDF-1.5 qui correspond au support des couches PDF. Si la hiérarchie n’es pas connue d’avance, le développeur peut faire une appel à SetLayer( L"" ) avant l’appel à StartDoc uniquement dans le but de créer un en-tête PDF-1.5.
La chaîne « Order » doit contenir les intitulés de toutes les couches ainsi que leur hiérarchie, le tout au format Unicode. Ex. :
// définition de l’ordre de hiérarchie des couches
SetLayer( hDC, L"/Order[(Blue Layer)[(Blue Layer - 1)(Blue Layer - 2)](Red Layer)(Green Layer)]" );
Ceci produira une structure de couches telle que :

Pour grouper les couches dans des groupes exclusifs de type « radio-button », les entrées /RBGroups, /ON et /OFF doivent être ajoutées à l’appel ci-dessus. Le code suivant fera des couches rouge et verte un groupe de couches exclusives :
// définition de l’ordre et le groupement des couches
SetLayer( hDC, L"/Order[(Couche Bleue)[(Couche Bleue - 1)(Couche Bleue - 2)](Couche Rouge)(Couche Verte)]" \
L"/RBGroups[[(Couche Rouge)(Couche Verte)]]/OFF[(Couche Rouge)]/ON[(Couche Verte)]"
);
A l’intérieur d’un bloc d’appels StartPage/EndPage, l’appel à SetLayer avec un intitulé valide démarrera une nouvelle couche et placera toutes les instructions graphiques suivantes à l’intérieur de cette couche. L’appel à SetLayer avec une chaîne vide termine la dernière couche.
Si plusieurs couches sont imbriquées, plusieurs appels à SetLayer avec une chaîne vide sont nécessaires pour fermer toutes les couches. Si toutes les couches ne sont pas fermées avant l’appel à EndPage, le pilote d’imprimante va automatiquement fermer les couches restantes, exemple :
// démarrer la couche parent
SetLayer( hDC, L"Couche Bleue" );
SetTextColor( hDC, RGB(0, 0, 255) );
TextOut( hDC, 200, yPos, buf, lstrlen(buf) );
yPos += 200;
// démarrer la sous-couche bleue numéro 1
SetLayer( hDC, L"Couche Bleue - 1" );
SetTextColor( hDC, RGB(0, 0, 128) );
TextOut( hDC, 800, yPos, "Couche Bleue - 1", lstrlen("Couche Bleue - 1") );
yPos += 200;
SetLayer( hDC, L"" ); // fermer la sous-couche
SetLayer( hDC, L"" ); // fermer la couche parent
La séquence d’ouverture et de fermeture des couches parents et enfants doit être consistante avec le chaîne « Order » afin de permettre au lecteur PDF de bien afficher et cacher les couches parentes avec leurs sous-couches d’une manière consistante.
En espérant que toute cette discussion à propos des parents, des enfants et des couches n’aura pas fait oublier aux nouveaux papas et nouvelles mamans leur obligation de base relative à leur nouveau-né !
Le contenu de cet article a été fourni par Dany Amiouny, directeur de la technologie d’Amyuni Technologies.