MISE À JOUR (14 mars 2017). — Ajout de l'option « Préserver les blocs chaînés », d'après une suggestion de Colin Flashman et Tim Gouder. Cochez cette case pour empêcher que CleanupPasteboard inclue les blocs chaînés dans l'opération de nettoyage.


Voici un petit script sans prétention suggéré par Rasmus Olsen il y a quelques jours au détour du Forum InDesign Scripting. L'idée de départ est de débarrasser votre document des éléments temporaires que vous avez disposés ça et là sur la table de montage (au-delà des limites imprimables de la page). L'interface dialoguée du script vous permet d'étendre ou de réduire la zone à nettoyer, en prenant par exemple en considération la marge de fond tournant (les fonds perdus) ou celle dite de ligne-bloc. (Voir l'animation en bas de page — désolé, j'ai laissé l'interface en anglais!)

La principale opération à exécuter étant d'une simplicité biblique, profitons-en pour examiner plus religieusement les objets impliqués dans la manœuvre, et leurs à-côtés.

Table de montage et planche

AVERTISSEMENT (15 mars 2017). — Cette section n'est pertinente que sous CS3/CS4, ce qui correspond à la date de publication de l'article original (2009). De profondes modifications du Scripting DOM ont été opérées depuis. En particulier, il n'est plus vrai que l'objet parent d'un PageItem de premier niveau soit, ou bien une page, ou bien une planche. Aujourd'hui, l'objet Spread représente toujours le parent ultime d'un élément paginé, peu importe qu'il se situe à l'intérieur ou à l'extérieur de l'aire de la page. Ainsi, les lignes qui suivent ne doivent être considérées que dans leur perspective historique.


Le point qu'il faut absolument saisir concernant la « table de montage » (pasteboard), c'est qu'un tel objet n'existe pas réellement aux yeux du DOM JavaScript d'InDesign. C'est une abstraction. Les objets Application et Document ont bien dans leurs bagages une propriété pasteboardPreferences, mais elle ne conduit qu'à un objet PasteboardPreference. Objet dans lequel on ne trouve, pour l'essentiel, qu'une propriété minimumSpaceAboveAndBelow contrôlant la zone intercalaire au-dessus et au-dessous des pages du document affiché. Pour info, l'API du SDK d'InDesign expose une class IPasteboard (« I » pour « Interface »), laquelle accède à la « géométrie de la table de montage ». Mais la table de montage n'est d'aucune façon un conteneur au sens naïf du terme, sa fonction essentielle étant plutôt de fixer les bases du système de coordonnées (Pasteboard coordinate space), comme l'illustre le guide du développeur:

Coordonnées des planches et des pages au sein de la table de montage

Ainsi, quand nous parlons de « nettoyer la table de montage », la véritable zone à laquelle nous faisons référence est l'espace extérieur des pages — plus ou moins visible selon vos préférences d'affichage — et cet espace est en réalité contrôlé par l'objet spread (« étendue, déploiement, éventail ») qui correspond aux planches dans la version francisée d'InDesign. Un objet Spread peut se concevoir comme un ensemble compact d'une ou plusieurs pages contiguës. Une double-page réside sur une planche de deux pages, mais les amateurs de dépliants en accordéon peuvent tout aussi bien créer des planches de 10 pages. Cela dit, une planche ne correspond pas seulement à la réunion géométrique de plusieurs pages, c'est aussi une sorte de méta-conteneur capable d'adresser tous les objets résidant à l'intérieur et à l'extérieur des pages. De ce fait, il peut régner une certaine confusion, lexicale ou conceptuelle, entre la planche (spread) et la table de montage (pasteboard).

« Une planche, explique-t-on dans la documentation d'InDesign, est une série de pages affichées ensemble, comme les deux pages visibles lorsque vous ouvrez un livre ou un magazine. La table de montage, zone extérieure à la page dans laquelle sont stockés les objets qui ne sont pas encore positionnés sur une page, est propre à une planche InDesign. Elle prévoit un espace réservé aux objets à fonds perdus, c’est-à-dire qui dépassent les limites d’une page. » Techniquement, il suffira de retenir qu'il n'y a pas d'objet Pasteboard et que l'objet Spread supervise tout le monde : l'agrégat de pages, les composants graphiques mis en page et les composants graphiques mis hors page dans cette portée de la table de montage. Supposons que nous avons créé un bloc sur la page active. Dupliquons-le, déplaçons la copie hors de la page et relevons les compteurs : mySpread.pageItems.length vaut 2, alors que myPage.pageItems.length en reste à 1 (deux blocs au total vus de la planche, mais un seul sur la page).

À ce stade, la grande question est de savoir comment collecter uniquement les objets outsiders (ceux que l'utilisateur perçoit comme inscrits sur la table de montage). La réponse découle de la hiérarchie des objets. Considérons un composant myPageItem situé hors de page. Il est alors caractérisé par le fait que son parent immédiat (myPageItem.parent) n'est pas une Page, mais un Spread (ou un MasterSpread, pour les gabarits). Par conséquent, lorsque vous souhaitez récupérer les objets hors-page du document actif, il suffit de filtrer les objets par la classe de leur parent (obj.parent.constructor) :

// Identification des éléments d'une planche sous CS4
// (Ne fonctionne plus dans les versions ultérieures).
 
var pasteboardItems = [],
    pItems = app.activeDocument.pageItems.everyItem().getElements(),
    i,p;
 
while( i=pItems.pop() )
    {
    p = i.parent.constructor;
    if ( p == Spread || p == MasterSpread )
        {
        pasteboardItems.push(i);
        }
    }
 
alert(pasteboardItems.length + " objects sur la table de montage");
 

Limites (“bounds”) et unités de mesure

Le script CleanupPasteboard a été étendu pour localiser sélectivement les objets situés au-delà du fond perdu, de la ligne-bloc, ou bien d'un périmètre quelconque défini par sa distance aux bords de page (« custom offset »). Ce dernier paramètre offre la possibilité de poser un périmètre de sécurité par rapport aux limites géométriques de la page. Il s'ensuit que nous ne pouvons pas réutiliser tel quel notre algorithme de détection des objets retranchés sur la table de montage. Il nous faut un calcul plus fin, basé sur la position des objets vis-à-vis du périmètre considéré et tenant compte des valeurs saisies par l'utilisateur dans l'interface dialoguée (famille measurementEditboxes) ainsi que des unités de mesure courantes (measurement units).

Bien qu'un objet Spread puisse se voir comme l'union géométrique d'une ou plusieurs page(s), le DOM n'offre pas de propriété bounds à ce niveau. Si vous avez besoin de connaître le périmètre total d'une planche, il est commode de prototyper une fonction d'appoint s'appuyant sur les limites (Page.bounds) des pages situées aux extrémités de la planche :

Spread.prototype.bounds =
MasterSpread.prototype.bounds =
function()
{ // renvoie les limites [top,left,bottom,right] de cette planche
var bFirst = this.pages.item(0).bounds; // perimetre de la premiere page
var bLast = this.pages.item(-1).bounds; // perimetre de la derniere page
return [ bFirst[0], bFirst[1], bLast[2], bLast[3] ];
}
 
// utilisation:
alert( app.activeWindow.activeSpread.bounds() );
 

Notre script s'inspire de cette approche (cf. extraBounds dans la méthode Document.prototype.cleanout), à ceci près que le paramètre offset est injecté dans le calcul afin d'obtenir directement le périmètre de travail.

Signalons aux scripters une particularité importante de la propriété bounds : les valeurs qu'elle retourne sont exprimées dans les unités de mesure en vigueur dans l'interface, soit verticalMeasurementUnits pour top/bottom et horizontalMeasurementUnits pour left/right. (Au niveau des composants de la famille PageItems, il en va de même des propriétés geometricBounds et visibleBounds.) Cependant, les contrôles MeasurementEditbox (ou MeasurementCombobox) sollicités dans vos boîtes de dialogue suivent une logique particulière : ils affichent les données saisies (editContents) dans l'unité spécifiée par leur propriété editUnits, mais ils interprètent toujours en points la valeur résultante (editValue). La question se pose souvent de savoir s'il vaut mieux travailler avec editContents (chaîne de caractère) ou avec editValue (nombre). Pour synchroniser les calculs impliquant des unités de mesure, on pourrait penser que editContents est plus souple du fait qu'il permet de s'aligner sur les unités préférentielles de l'utilisateur. Cependant, s'il est en effet souhaitable d'afficher par défaut les informations dans l'unité attendue par l'utilisateur, le traitement des données via editContents comporte certains risques, parce que cette chaîne est « localisée » (virgule décimale en français, point décimal en anglais) et peut introduire des difficultés de conversion. C'est pourquoi il me paraît préférable de travailler malgré tout avec editValue, sous réserve de traduire cette mesure numérique (en points) vers l'unité désirée.

Pour ce faire, il vous suffit de mettre en place un mécanisme générique de conversion à l'aide de la classe UnitValue fournie par le noyau JavaScript. Voici une approche possible :

var UNITS = (function()
    {
    var r={}, mu=MeasurementUnits;
    r[mu.AGATES]='agt';
    r[mu.CENTIMETERS]='cm';
    r[mu.CICEROS]='ci';
    r[mu.INCHES]='in';
    r[mu.INCHES_DECIMAL]='in';
    r[mu.MILLIMETERS]='mm';
    r[mu.PICAS]='pc';
    r[mu.POINTS]='pt';
    return(r);
    })();
 
// convertir myPointsValue (pts) vers myMeasurementUnits
var uv = UnitValue(myPointsValue, 'pt');
var convertedValue = uv.as(UNITS[myMeasurementUnits]);
 

Le script CleanupPasteboard illustre cette logique dans la méthode ViewPreference.prototype.toOffsets().

CleanupPasteboard Demo

CleanupPasteboard Demo