Ajoute le plugin Acrobat Pro d'imposition (acrobat-plugin/)

Suite d'imposition professionnelle en Folder-Level Scripts JS (7 modules +
verification de licence Vision), avec un plugin natif hybride optionnel
(WinINet + Registre) pour deplacer la verification de licence en code
compile. Installeur install.bat sur le meme modele que celui du projet
desktop (git portable, detection automatique des dossiers Acrobat).
This commit is contained in:
Jules
2026-06-21 22:16:24 +02:00
parent 202834a794
commit de76c1776c
14 changed files with 2856 additions and 0 deletions

View File

@@ -0,0 +1,231 @@
// Imposition-00-Core.js
// ---------------------------------------------------------------------------
// Suite d'imposition professionnelle - Folder-Level Script pour Adobe Acrobat Pro
// Module 0 : Noyau (namespace, utilitaires, menu racine)
//
// Ce fichier doit etre charge en premier (prefixe "00") car il definit le
// namespace global CDImp (Copydev Imposition) utilise par tous les autres
// modules. Acrobat charge les scripts du dossier JavaScripts par ordre
// alphabetique, donc le prefixe numerique garantit l'ordre d'initialisation.
//
// Emplacement attendu sous Windows :
// %APPDATA%\Adobe\Acrobat\<version>\JavaScripts\ (utilisateur courant)
// ou <InstallDir>\Acrobat\Javascripts\ (tous les utilisateurs)
// ---------------------------------------------------------------------------
// Namespace global partage par tous les modules. "var" au niveau racine d'un
// folder-level script devient une variable globale persistante pour la
// session Acrobat (et non une variable "global" partagee entre documents,
// qui necessiterait l'objet `global`).
if (typeof CDImp === "undefined") {
var CDImp = {};
}
// -----------------------------------------------------------------------
// Constantes
// -----------------------------------------------------------------------
CDImp.PT_PAR_MM = 72 / 25.4; // 1 mm en points PDF (~2.8346456693)
// -----------------------------------------------------------------------
// Utilitaires generaux
// -----------------------------------------------------------------------
CDImp.Util = {
/**
* Convertit des millimetres en points PDF.
*/
mmToPt: function (mm) {
return mm * CDImp.PT_PAR_MM;
},
/**
* Convertit des points PDF en millimetres.
*/
ptToMm: function (pt) {
return pt / CDImp.PT_PAR_MM;
},
/**
* Execute une fonction avec les privileges eleves necessaires aux
* operations sur les fichiers / documents (creation, sauvegarde,
* fermeture, ajout de menus...). Restaure toujours les privileges
* standards via app.endPriv(), meme en cas d'erreur.
*
* @param {Function} fn fonction a executer en contexte privilegie
* @returns la valeur retournee par fn, ou undefined en cas d'erreur
*/
avecPrivileges: function (fn) {
var resultat;
try {
app.beginPriv();
resultat = fn();
} catch (e) {
CDImp.Util.erreur("Erreur privilegiee : " + e.toString());
} finally {
app.endPriv();
}
return resultat;
},
/**
* Affiche une boite d'alerte d'erreur standardisee.
*/
erreur: function (message) {
app.alert({
cMsg: message,
nIcon: 0, // erreur
nType: 0 // OK
});
},
/**
* Affiche une boite d'information standardisee.
*/
info: function (message) {
app.alert({
cMsg: message,
nIcon: 3, // information
nType: 0 // OK
});
},
/**
* Retourne le document actif passe en parametre, ou a defaut le premier
* document ouvert dans Acrobat. Leve une erreur explicite si aucun
* document n'est ouvert.
*/
getDocActif: function (docPotentiel) {
var doc = docPotentiel;
if (!doc) {
if (app.activeDocs && app.activeDocs.length > 0) {
doc = app.activeDocs[0];
}
}
if (!doc) {
throw new Error("Aucun document PDF ouvert.");
}
return doc;
},
/**
* Demande un nombre a l'utilisateur via app.response, avec valeur par
* defaut et validation. Retourne null si l'utilisateur annule.
*/
demanderNombre: function (question, titre, defaut) {
var saisie = app.response({ cQuestion: question, cTitle: titre || "Imposition Professionnelle", cDefault: String(defaut) });
if (saisie === null) {
return null;
}
var n = parseFloat(saisie);
return isNaN(n) ? defaut : n;
},
/**
* Place le contenu integral de la page source nSrcPage (1-based) du
* document srcDoc a l'interieur du rectangle rectDest (en points PDF,
* [llx, lly, urx, ury]) de la page nDestPage (0-based) du document
* destDoc, avec mise a l'echelle automatique si rectDest a une taille
* differente de la page source.
*
* Technique : on cree un template a partir de la page source, on force
* temporairement la CropBox de la page de destination a rectDest (ce qui
* determine la zone et donc l'echelle d'affichage du contenu "spawn"),
* on spawn le template, puis on restaure la CropBox d'origine de la
* page de destination. C'est la technique standard utilisee en
* JavaScript pur pour Acrobat (sans plugin natif) ; a valider dans une
* vraie session Acrobat Pro (voir notes dans chaque module appelant).
*
* @param {Doc} srcDoc
* @param {Number|null} nSrcPage numero 1-based, ou null/0 pour ne rien placer
* @param {Doc} destDoc
* @param {Number} nDestPage index 0-based
* @param {Array} rectDest [llx, lly, urx, ury] en points
*/
placerPageDansRect: function (srcDoc, nSrcPage, destDoc, nDestPage, rectDest) {
if (!nSrcPage) {
return;
}
CDImp.Util.avecPrivileges(function () {
var nomTpl = "CDImp_tpl_" + nSrcPage + "_" + nDestPage + "_" + (new Date()).getTime();
var tpl = srcDoc.createTemplate({ cName: nomTpl, nPage: nSrcPage - 1 });
var cropOrigineDest = destDoc.getPageBox({ nPage: nDestPage, cBox: "Crop" });
try {
destDoc.setPageBoxes({ nPage: nDestPage, cBox: "Crop", rBox: rectDest });
tpl.spawn({ cDocName: destDoc, nPage: nDestPage, bRename: false, bOverlay: true });
} finally {
destDoc.setPageBoxes({ nPage: nDestPage, cBox: "Crop", rBox: cropOrigineDest });
tpl.remove();
}
});
},
/**
* Extrait une sous-region rectSource (en points PDF, [llx, lly, urx, ury]
* dans le repere de la page source) de la page nSrcPage (1-based) de
* srcDoc, et la place a l'echelle 1:1 sur la page nDestPage (0-based) de
* destDoc (qui doit deja avoir la taille de rectSource).
*
* Technique symetrique de placerPageDansRect : ici on recadre
* temporairement la CropBox de la page SOURCE (pour n'exposer que la
* region voulue), on cree le template, on restaure la CropBox source,
* puis on spawn sur la page de destination. Utilise pour le decoupage
* de pages (Module 4) et la delivretisation (Module 2).
*
* @param {Doc} srcDoc
* @param {Number} nSrcPage numero 1-based
* @param {Array} rectSource [llx, lly, urx, ury] dans le repere de la page source
* @param {Doc} destDoc
* @param {Number} nDestPage index 0-based
*/
extraireRegionPage: function (srcDoc, nSrcPage, rectSource, destDoc, nDestPage) {
CDImp.Util.avecPrivileges(function () {
var cropOrigineSrc = srcDoc.getPageBox({ nPage: nSrcPage - 1, cBox: "Crop" });
try {
srcDoc.setPageBoxes({ nPage: nSrcPage - 1, cBox: "Crop", rBox: rectSource });
var nomTpl = "CDImp_ext_" + nSrcPage + "_" + nDestPage + "_" + (new Date()).getTime();
var tpl = srcDoc.createTemplate({ cName: nomTpl, nPage: nSrcPage - 1 });
try {
tpl.spawn({ cDocName: destDoc, nPage: nDestPage, bRename: false, bOverlay: true });
} finally {
tpl.remove();
}
} finally {
srcDoc.setPageBoxes({ nPage: nSrcPage - 1, cBox: "Crop", rBox: cropOrigineSrc });
}
});
}
};
// -----------------------------------------------------------------------
// Formats de feuille standards (largeur x hauteur, en mm, portrait)
// -----------------------------------------------------------------------
CDImp.Util.FORMATS_MM = {
A3: [297, 420],
A4: [210, 297],
A5: [148, 210],
SRA3: [320, 450],
SRA4: [225, 320],
LETTER: [215.9, 279.4]
};
// -----------------------------------------------------------------------
// Menu racine "Imposition Pro"
// -----------------------------------------------------------------------
// Cree un menu de premier niveau avec un seul point d'entree qui ouvre le
// panneau de controle principal (boite de dialogue maitresse, voir Module 1).
// La creation/modification de menus est une operation privilegiee.
// -----------------------------------------------------------------------
CDImp.Util.avecPrivileges(function () {
try {
app.addMenuItem({
cName: "CDImpRacine",
cParent: "Edit", // accroche dans le menu Edition (peut etre adapte)
cExec: "CDImp.UI.ouvrirPanneauPrincipal(this);",
cEnable: "event.rc = (CDImp.UI != undefined);",
nPos: -1
});
} catch (e) {
// Si le menu existe deja (rechargement de script), on ignore
// silencieusement l'erreur de doublon.
}
});

View File

@@ -0,0 +1,342 @@
// Imposition-00b-Licence.js
// ---------------------------------------------------------------------------
// Suite d'imposition professionnelle - Folder-Level Script pour Adobe Acrobat Pro
// Module 0b : Verification de licence (serveur Vision)
//
// Depend de Imposition-00-Core.js (doit etre charge avant). Doit etre charge
// AVANT Imposition-01-UI.js (le prefixe "00b" garantit cet ordre).
//
// Reutilise le serveur de licence Vision deja en place pour le projet
// "Copydev Imposing Tool" (impose) : meme API license.php, meme logique de
// cache hors-ligne avec duree de tolerance, meme principe de plafond
// d'appareils (max_devices) gere cote serveur. Seul le champ "product"
// change pour distinguer ce plugin Acrobat des autres produits Copydev.
//
// AVERTISSEMENTS TECHNIQUES - a tester dans Acrobat Pro reel :
// - Net.HTTP.request() est l'API documentee pour les requetes HTTP depuis
// un script Acrobat privilegie, mais sa disponibilite depend des
// reglages de securite reseau d'Acrobat (Preferences > Securite
// (avancee) > Gestionnaire de confiance > acces internet pour les
// scripts non-PDF). A activer/whitelister si necessaire.
// - La lecture du flux de reponse (Stream.read()) est implementee selon le
// schema le plus communement documente, mais n'a pas pu etre executee
// dans cet environnement de developpement (pas d'Acrobat installe) :
// ajuster _lireFluxReponse() si le parsing JSON echoue en conditions
// reelles.
// - L'identifiant machine (machine_id) repose sur ActiveXObject (COM),
// disponible uniquement sous Windows pour des scripts de dossier
// (folder-level) de confiance. En cas d'indisponibilite, on retombe sur
// un identifiant genere une fois et persiste localement (moins robuste
// contre la copie de licence, mais fonctionnel).
// ---------------------------------------------------------------------------
if (typeof CDImp === "undefined") {
throw new Error("Imposition-00b-Licence.js : CDImp.Core doit etre charge avant ce fichier.");
}
CDImp.Licence = CDImp.Licence || {};
CDImp.Licence.CONFIG = {
serveurUrl: "https://vision.copydev.fr/api/license.php",
produit: "imposing-acrobat",
dureeCacheHeuresParDefaut: 24 // utilisee si le serveur ne renvoie pas offline_timeout_hours
};
// -----------------------------------------------------------------------
// Identifiants machine
// -----------------------------------------------------------------------
/**
* Nom de la machine, via WSH (Windows uniquement, scripts de dossier
* privilegies). Retombe sur "Inconnu" si indisponible.
*/
CDImp.Licence._obtenirHostname = function () {
try {
var reseau = new ActiveXObject("WScript.Network");
return String(reseau.ComputerName);
} catch (e) {
return "Inconnu";
}
};
/**
* Identifiant machine stable, persiste dans les preferences globales
* d'Acrobat (genere une seule fois). Pas un identifiant materiel (UUID
* BIOS), mais suffisant pour le comptage d'appareils cote serveur.
*/
CDImp.Licence._obtenirMachineId = function () {
if (typeof global.cdImpMachineId !== "undefined" && global.cdImpMachineId) {
return global.cdImpMachineId;
}
var id = "";
for (var i = 0; i < 32; i++) {
id += Math.floor(Math.random() * 16).toString(16);
}
CDImp.Util.avecPrivileges(function () {
global.cdImpMachineId = id;
global.setPersistent("cdImpMachineId", true);
});
return id;
};
// -----------------------------------------------------------------------
// Requete HTTP synchrone vers le serveur de licence
// -----------------------------------------------------------------------
/**
* Convertit le flux de reponse Net.HTTP en chaine de caracteres.
*/
CDImp.Licence._lireFluxReponse = function (msgStream) {
try {
var nTaille = msgStream.length;
var aOctets = msgStream.read(nTaille);
var texte = "";
for (var i = 0; i < aOctets.length; i++) {
texte += String.fromCharCode(aOctets[i]);
}
return texte;
} catch (e) {
return "";
}
};
/**
* Effectue une requete GET synchrone et retourne { erreur, texte }.
*/
CDImp.Licence._requeteHTTPSync = function (url) {
var resultat = { erreur: null, texte: null };
try {
Net.HTTP.request({
cVerb: "GET",
cURL: url,
oHandler: {
response: function (msgStream, status) {
if (status && status >= 400) {
resultat.erreur = "HTTP " + status;
} else {
resultat.texte = CDImp.Licence._lireFluxReponse(msgStream);
}
},
error: function (msg, err) {
resultat.erreur = (err || msg || "Erreur reseau inconnue").toString();
}
},
bAsync: false
});
} catch (e) {
resultat.erreur = e.toString();
}
return resultat;
};
// -----------------------------------------------------------------------
// Saisie / stockage de la cle de licence
// -----------------------------------------------------------------------
CDImp.Licence._demanderCle = function () {
var cle = app.response({
cQuestion: "Cle de licence Copydev Imposing (Acrobat) :",
cTitle: "Activation de la licence",
cDefault: ""
});
return (cle && cle.length > 0) ? cle : null;
};
/**
* Recopie la cle dans le Registre Windows (HKCU\Software\Copydev\
* ImposingAcrobat\CleLicence), seul emplacement que le plugin natif
* (voir /native/PluginMain.cpp) sait lire : global.setPersistent() stocke
* la valeur dans le mecanisme de preferences interne d'Acrobat, pas dans
* le Registre, donc cette ecriture explicite est necessaire pour que le
* plugin natif et le JS partagent la meme cle. Echec silencieux si
* ActiveXObject est indisponible (plugin natif alors simplement inutilisable,
* le JS continue de fonctionner seul).
*/
CDImp.Licence._ecrireCleRegistre = function (cle) {
try {
var shell = new ActiveXObject("WScript.Shell");
shell.RegWrite("HKCU\\Software\\Copydev\\ImposingAcrobat\\CleLicence", cle, "REG_SZ");
} catch (e) {
// pas grave : le plugin natif (s'il est installe) ne pourra pas
// verifier la licence de son cote, le JS reste fonctionnel seul.
}
};
CDImp.Licence._obtenirCle = function () {
if (typeof global.cdImpLicenceCle !== "undefined" && global.cdImpLicenceCle) {
return global.cdImpLicenceCle;
}
var cle = CDImp.Licence._demanderCle();
if (!cle) {
return null;
}
CDImp.Util.avecPrivileges(function () {
global.cdImpLicenceCle = cle;
global.setPersistent("cdImpLicenceCle", true);
});
CDImp.Licence._ecrireCleRegistre(cle);
return cle;
};
// -----------------------------------------------------------------------
// Verdict du plugin natif (voir /native/), lu en priorite s'il est present
// et frais. Permet de s'appuyer sur une verification effectuee en code
// compile (plus dur a lire/patcher qu'un .js) plutot que sur le seul check
// JS ci-dessous. Si le plugin natif n'est pas installe ou n'a pas encore
// ecrit de verdict, on retombe naturellement sur la verification JS pure
// (aucune regression).
// -----------------------------------------------------------------------
CDImp.Licence._lireVerdictNatif = function () {
try {
var shell = new ActiveXObject("WScript.Shell");
var ok = shell.RegRead("HKCU\\Software\\Copydev\\ImposingAcrobat\\LicenceOK");
var expirationStr = shell.RegRead("HKCU\\Software\\Copydev\\ImposingAcrobat\\LicenceValideJusqua");
var expiration = parseInt(expirationStr, 10);
var maintenant = Math.floor((new Date()).getTime() / 1000);
if (String(ok) === "1" && expiration > maintenant) {
return { valide: true, source: "plugin_natif" };
}
return { valide: false, source: "plugin_natif", raison: "Verdict du plugin natif absent ou expire." };
} catch (e) {
return null; // plugin natif non installe / registre absent : pas d'info
}
};
// -----------------------------------------------------------------------
// Verification (serveur, avec repli sur cache hors-ligne)
// -----------------------------------------------------------------------
/**
* Verifie la licence auprès du serveur Vision. En cas d'echec reseau,
* retombe sur le dernier resultat valide mis en cache, dans la limite de la
* duree de tolerance hors-ligne (offline_timeout_hours renvoye par le
* serveur, ou valeur par defaut).
*
* @returns {Object} { valide, source: "serveur"|"cache", donnees, raison }
*/
CDImp.Licence.verifier = function () {
var verdictNatif = CDImp.Licence._lireVerdictNatif();
if (verdictNatif && verdictNatif.valide) {
return verdictNatif;
}
// Verdict natif absent, expire ou plugin non installe : on continue
// avec la verification JS habituelle (HTTP + cache hors-ligne).
var cle = CDImp.Licence._obtenirCle();
if (!cle) {
return { valide: false, raison: "Aucune cle de licence saisie." };
}
var machineId = CDImp.Licence._obtenirMachineId();
var hostname = CDImp.Licence._obtenirHostname();
var url = CDImp.Licence.CONFIG.serveurUrl
+ "?key=" + encodeURIComponent(cle)
+ "&machine_id=" + encodeURIComponent(machineId)
+ "&hostname=" + encodeURIComponent(hostname)
+ "&product=" + encodeURIComponent(CDImp.Licence.CONFIG.produit);
var reponse = CDImp.Licence._requeteHTTPSync(url);
if (reponse.erreur || !reponse.texte) {
return CDImp.Licence._verifierCache(reponse.erreur);
}
var data;
try {
data = JSON.parse(reponse.texte);
} catch (e) {
return CDImp.Licence._verifierCache("Reponse serveur illisible.");
}
var valide = !!(data.active || data.valid);
CDImp.Util.avecPrivileges(function () {
global.cdImpLicenceCache = { valide: valide, donnees: data, dateVerif: (new Date()).getTime() };
global.setPersistent("cdImpLicenceCache", true);
});
return { valide: valide, donnees: data, source: "serveur" };
};
CDImp.Licence._verifierCache = function (raisonReseau) {
var cache = global.cdImpLicenceCache;
if (!cache) {
return { valide: false, raison: "Serveur de licence inaccessible (" + (raisonReseau || "?") + ") et aucun cache local." };
}
var heuresEcoulees = ((new Date()).getTime() - cache.dateVerif) / 3600000;
var tolerance = (cache.donnees && cache.donnees.offline_timeout_hours) || CDImp.Licence.CONFIG.dureeCacheHeuresParDefaut;
if (cache.valide && heuresEcoulees <= tolerance) {
return { valide: true, donnees: cache.donnees, source: "cache", heuresEcoulees: heuresEcoulees };
}
return {
valide: false,
raison: "Serveur de licence inaccessible (" + (raisonReseau || "?") + ") et cache hors-ligne expire ou invalide\n(hors-ligne depuis ~" + Math.round(heuresEcoulees) + "h, tolerance " + tolerance + "h)."
};
};
/**
* Verifie la licence et affiche une alerte bloquante si elle est invalide.
* A appeler avant toute action des modules d'imposition.
*
* @returns {Boolean} true si l'utilisation peut continuer
*/
CDImp.Licence.verifierEtBloquerSiInvalide = function () {
var resultat;
try {
resultat = CDImp.Licence.verifier();
} catch (e) {
CDImp.Util.erreur("Erreur lors de la verification de la licence : " + e.toString());
return false;
}
if (!resultat.valide) {
CDImp.Util.erreur("Licence invalide ou expiree :\n" + (resultat.raison || "")
+ "\n\nLa suite d'imposition professionnelle est desactivee.\nUtilisez \"Imposition Pro > Gerer la licence...\" pour saisir une cle valide.");
return false;
}
return true;
};
/**
* Boite de dialogue simple permettant de saisir/remplacer la cle de licence
* et de relancer immediatement une verification.
*/
CDImp.Licence.gererCle = function () {
var cleActuelle = global.cdImpLicenceCle || "";
var nouvelleCle = app.response({
cQuestion: "Cle de licence Copydev Imposing (Acrobat) :",
cTitle: "Gerer la licence",
cDefault: cleActuelle
});
if (nouvelleCle === null || nouvelleCle === cleActuelle) {
return;
}
CDImp.Util.avecPrivileges(function () {
global.cdImpLicenceCle = nouvelleCle;
global.setPersistent("cdImpLicenceCle", true);
global.cdImpLicenceCache = null; // force une nouvelle verification serveur
global.setPersistent("cdImpLicenceCache", true);
});
CDImp.Licence._ecrireCleRegistre(nouvelleCle);
CDImp.Util.info("Cle enregistree. Redemarrez Acrobat pour que le plugin natif\n(s'il est installe) la prenne en compte au prochain demarrage.");
var resultat = CDImp.Licence.verifier();
if (resultat.valide) {
CDImp.Util.info("Licence valide. Source : " + resultat.source + ".");
} else {
CDImp.Util.erreur("Licence invalide : " + (resultat.raison || ""));
}
};
// -----------------------------------------------------------------------
// Entree de menu "Gerer la licence..."
// -----------------------------------------------------------------------
CDImp.Util.avecPrivileges(function () {
try {
app.addMenuItem({
cName: "CDImpGererLicence",
cParent: "Edit",
cExec: "CDImp.Licence.gererCle();",
cEnable: "event.rc = (CDImp.Licence != undefined);",
nPos: -1
});
} catch (e) {
// menu deja present (rechargement de script) : ignore
}
});

View File

@@ -0,0 +1,154 @@
// Imposition-01-UI.js
// ---------------------------------------------------------------------------
// Suite d'imposition professionnelle - Folder-Level Script pour Adobe Acrobat Pro
// Module 1 : Panneau de controle principal (UI Dialog)
//
// Depend de Imposition-00-Core.js (doit etre charge avant).
//
// IMPORTANT : le format de description utilise par app.execDialog() est tres
// specifique et difficile a valider hors d'une session Acrobat reelle (cet
// environnement de developpement n'a pas Acrobat installe). La structure
// ci-dessous suit la convention documentee dans l'Annexe "Creating Dialogs"
// du Acrobat DC SDK JavaScript Reference. A tester et ajuster dans Acrobat
// Pro (les noms de "type" et d'evenements sont les points les plus fragiles).
// ---------------------------------------------------------------------------
if (typeof CDImp === "undefined") {
throw new Error("Imposition-01-UI.js : CDImp.Core doit etre charge avant ce fichier.");
}
CDImp.UI = CDImp.UI || {};
// Codes de retour utilises pour savoir quel bouton a ete actionne dans le
// panneau principal.
CDImp.UI.ACTIONS = {
LIVRET: 1,
NUP: 2,
DECOUPE: 3,
MARGES: 4,
REPERES: 5,
AUTO: 6
};
/**
* Construit l'objet de description du panneau principal pour app.execDialog.
* Les boutons referencent chacun un item_id ; le clic ferme le dialogue en
* renvoyant le code d'action correspondant via dialog.end().
*/
CDImp.UI._construireDialoguePrincipal = function () {
var A = CDImp.UI.ACTIONS;
return {
description: {
name: "Imposition Professionnelle",
elements: [
{
type: "view",
name: "Imposition Professionnelle",
elements: [
{
type: "panel",
align_children: "align_top",
elements: [
{ type: "static_text", name: "Choisissez un module d'imposition :" },
{
type: "view",
align_children: "align_row",
elements: [
{
type: "view",
align_children: "align_column",
elements: [
{ type: "button", item_id: "btnLivret", name: "1. Creer un livret" },
{ type: "button", item_id: "btnNUp", name: "2. N-Up / Step & Repeat" },
{ type: "button", item_id: "btnDecoupe", name: "3. Decoupe & reassemblage" }
]
},
{
type: "view",
align_children: "align_column",
elements: [
{ type: "button", item_id: "btnMarges", name: "4. Marges & cadrages" },
{ type: "button", item_id: "btnReperes", name: "5. Reperes, textes, numerotation" },
{ type: "button", item_id: "btnAuto", name: "6. Automatisation" }
]
}
]
},
{ type: "cancel", item_id: "btnFermer", name: "Fermer" }
]
}
]
}
]
},
// -- Gestionnaires d'evenements, un par item_id de bouton --------
btnLivret: { mouse_down: function (dialog) { dialog.end(A.LIVRET); } },
btnNUp: { mouse_down: function (dialog) { dialog.end(A.NUP); } },
btnDecoupe: { mouse_down: function (dialog) { dialog.end(A.DECOUPE); } },
btnMarges: { mouse_down: function (dialog) { dialog.end(A.MARGES); } },
btnReperes: { mouse_down: function (dialog) { dialog.end(A.REPERES); } },
btnAuto: { mouse_down: function (dialog) { dialog.end(A.AUTO); } }
};
};
/**
* Affiche un message indiquant qu'un module n'est pas encore implemente.
*/
CDImp.UI._moduleAVenir = function (nomModule) {
CDImp.Util.info("Module \"" + nomModule + "\" : en cours de developpement.\nDisponible dans une prochaine version de la suite d'imposition.");
};
/**
* Point d'entree principal : ouvre le panneau de controle, puis route vers
* le module choisi par l'utilisateur. Le document actif (celui depuis lequel
* le menu a ete invoque) est transmis aux modules afin qu'ils n'aient pas a
* deviner quel document cibler.
*
* @param {Doc} [docCourant] document actif, transmis via "this" par le menu
*/
CDImp.UI.ouvrirPanneauPrincipal = function (docCourant) {
var doc;
try {
doc = CDImp.Util.getDocActif(docCourant);
} catch (e) {
CDImp.Util.erreur("Veuillez ouvrir un document PDF avant de lancer l'imposition.");
return;
}
if (CDImp.Licence && !CDImp.Licence.verifierEtBloquerSiInvalide()) {
return;
}
var oDialogue = CDImp.UI._construireDialoguePrincipal();
var resultat;
try {
resultat = app.execDialog(oDialogue);
} catch (e) {
CDImp.Util.erreur("Impossible d'ouvrir le panneau d'imposition : " + e.toString());
return;
}
var routes = [
{ action: CDImp.UI.ACTIONS.LIVRET, module: "Livret", nom: "Creer un livret" },
{ action: CDImp.UI.ACTIONS.NUP, module: "NUp", nom: "N-Up / Step & Repeat" },
{ action: CDImp.UI.ACTIONS.DECOUPE, module: "Decoupe", nom: "Decoupe & reassemblage" },
{ action: CDImp.UI.ACTIONS.MARGES, module: "Marges", nom: "Marges & cadrages" },
{ action: CDImp.UI.ACTIONS.REPERES, module: "Reperes", nom: "Reperes, textes, numerotation" },
{ action: CDImp.UI.ACTIONS.AUTO, module: "Auto", nom: "Automatisation" }
];
for (var i = 0; i < routes.length; i++) {
if (routes[i].action === resultat) {
var mod = CDImp[routes[i].module];
if (mod && mod.lancer) {
mod.lancer(doc);
} else {
CDImp.UI._moduleAVenir(routes[i].nom);
}
return;
}
}
// Fermeture / Annulation : rien a faire.
};

View File

@@ -0,0 +1,318 @@
// Imposition-02-Livret.js
// ---------------------------------------------------------------------------
// Suite d'imposition professionnelle - Folder-Level Script pour Adobe Acrobat Pro
// Module 2 : Creer un livret / Delivretiser (signature de cahier, piqure a cheval)
//
// Depend de Imposition-00-Core.js (doit etre charge avant).
//
// ATTENTION TECHNIQUE - a lire avant utilisation en production :
// Le placement des pages source sur les planches composites repose sur
// CDImp.Util.placerPageDansRect()/extraireRegionPage() (voir Core.js), qui
// utilisent la technique "template + recadrage de CropBox" standard en
// JavaScript pur pour Acrobat (sans plugin natif). Cette technique n'a pas
// pu etre executee/verifiee dans une vraie session Acrobat depuis cet
// environnement de developpement (Acrobat n'y est pas installe) : a tester
// sur un PDF reel avant tout usage en production.
// ---------------------------------------------------------------------------
if (typeof CDImp === "undefined") {
throw new Error("Imposition-02-Livret.js : CDImp.Core doit etre charge avant ce fichier.");
}
CDImp.Livret = CDImp.Livret || {};
// -----------------------------------------------------------------------
// Calcul de l'ordre des pages (imposition en cahier unique, piqure a cheval)
// -----------------------------------------------------------------------
/**
* Calcule l'imbrication des pages pour un cahier unique (selle/pliage central).
* Fonction symetrique : utilisee aussi bien pour imposer (Page logique ->
* position sur planche) que pour delivretiser (position sur planche -> page
* logique), puisque l'imposition en cahier simple est sa propre inverse au
* niveau de la formule (seul le sens d'utilisation change).
*
* Pour N pages (multiple de 4), chaque feuille porte 4 pages : 2 au recto,
* 2 au verso. Exemple pour N=8 :
* Feuille 1 - recto : (8 | 1) verso : (2 | 7)
* Feuille 2 - recto : (6 | 3) verso : (4 | 5)
*
* @param {Number} nPages nombre total de pages (doit etre un multiple de 4)
* @returns {Array} liste de planches, dans l'ordre d'impression :
* [{ feuille: 1, face: "recto", gauche: 8, droite: 1 }, ...]
* "gauche"/"droite" sont des numeros de page 1-based du document source.
*/
CDImp.Livret.calculerOrdre = function (nPages) {
if (nPages % 4 !== 0) {
throw new Error("calculerOrdre : le nombre de pages (" + nPages + ") doit etre un multiple de 4.");
}
var nFeuilles = nPages / 4;
var planches = [];
for (var s = 0; s < nFeuilles; s++) {
planches.push({ feuille: s + 1, face: "recto", gauche: nPages - 2 * s, droite: 2 * s + 1 });
planches.push({ feuille: s + 1, face: "verso", gauche: 2 * s + 2, droite: nPages - 2 * s - 1 });
}
return planches;
};
// -----------------------------------------------------------------------
// Ajout de pages blanches pour atteindre un multiple de 4
// -----------------------------------------------------------------------
/**
* Insere nManque pages blanches dans doc, a la position demandee.
* Les pages blanches reprennent le format (largeur/hauteur) de la premiere
* page du document.
*
* @param {Doc} doc document a modifier (modification en place)
* @param {Number} nManque nombre de pages blanches a ajouter
* @param {String} position "debut" | "fin" | "apres_couverture"
*/
CDImp.Livret.ajouterPagesBlanches = function (doc, nManque, position) {
if (nManque <= 0) {
return;
}
var boxRef = doc.getPageBox({ nPage: 0, cBox: "Crop" });
var largeur = Math.abs(boxRef[2] - boxRef[0]);
var hauteur = Math.abs(boxRef[3] - boxRef[1]);
CDImp.Util.avecPrivileges(function () {
for (var k = 0; k < nManque; k++) {
var idxInsertion;
switch (position) {
case "debut":
idxInsertion = 0;
break;
case "apres_couverture":
idxInsertion = 1;
break;
case "fin":
default:
idxInsertion = doc.numPages; // toujours recalcule : ajoute en fin
break;
}
doc.newPage({ nPage: idxInsertion, width: largeur, height: hauteur });
}
});
};
/**
* Demande a l'utilisateur ou inserer les pages blanches manquantes, via un
* menu contextuel simple (app.popUpMenu, API stable et bien documentee,
* preferee ici a un second execDialog pour limiter les risques sur un choix
* aussi simple).
*
* @returns {String|null} "debut" | "fin" | "apres_couverture", ou null si
* l'utilisateur annule.
*/
CDImp.Livret._demanderPositionPagesBlanches = function (nManque) {
var choix = app.popUpMenu(
{ cName: "A la fin du document (" + nManque + " page(s))", cReturn: "fin" },
{ cName: "Au debut du document (" + nManque + " page(s))", cReturn: "debut" },
{ cName: "Juste apres la couverture (page 1)", cReturn: "apres_couverture" }
);
return choix || null;
};
// -----------------------------------------------------------------------
// Construction du document imposes (sens direct : pages -> livret)
// -----------------------------------------------------------------------
/**
* Construit le document de sortie : une page composite (format double, en
* paysage) par planche recto/verso, avec les deux pages source positionnees
* cote a cote.
*
* @param {Doc} srcDoc document source (deja complete a un multiple de 4 pages)
* @param {Array} planches resultat de CDImp.Livret.calculerOrdre()
* @param {Number} creepMm compensation de chasse en mm, appliquee
* progressivement vers l'interieur du cahier (0 = desactive)
* @returns {Doc} le nouveau document compose
*/
CDImp.Livret.construireDocument = function (srcDoc, planches, creepMm) {
var boxSrc = srcDoc.getPageBox({ nPage: 0, cBox: "Crop" });
var largeurSrc = Math.abs(boxSrc[2] - boxSrc[0]);
var hauteurSrc = Math.abs(boxSrc[3] - boxSrc[1]);
var largeurDest = largeurSrc * 2;
var hauteurDest = hauteurSrc;
var nFeuilles = planches.length / 2;
var destDoc;
CDImp.Util.avecPrivileges(function () {
destDoc = new Doc();
destDoc.newPage({ nPage: 0, width: largeurDest, height: hauteurDest });
for (var k = 1; k < planches.length; k++) {
destDoc.newPage({ nPage: k, width: largeurDest, height: hauteurDest });
}
});
for (var i = 0; i < planches.length; i++) {
var planche = planches[i];
// Decalage de chasse (creep) : croissant en s'eloignant du centre du
// cahier vers l'exterieur, applique symetriquement gauche/droite.
var decalageCreepPt = creepMm
? CDImp.Util.mmToPt(creepMm) * (nFeuilles - planche.feuille)
: 0;
CDImp.Util.placerPageDansRect(srcDoc, planche.gauche, destDoc, i,
[decalageCreepPt, 0, decalageCreepPt + largeurSrc, hauteurSrc]);
CDImp.Util.placerPageDansRect(srcDoc, planche.droite, destDoc, i,
[largeurSrc - decalageCreepPt, 0, largeurDest - decalageCreepPt, hauteurSrc]);
}
return destDoc;
};
// -----------------------------------------------------------------------
// Delivretisation (sens inverse : livret imposes -> ordre de lecture normal)
// -----------------------------------------------------------------------
/**
* Reconstitue l'ordre de lecture normal d'un PDF imposé en cahier piqûre a
* cheval : chaque page du document source est une planche double (recto ou
* verso) contenant deux pages logiques cote a cote, dans l'ordre produit par
* CDImp.Livret.construireDocument(). On reutilise calculerOrdre() pour
* retrouver, pour chaque planche, les numeros de page logique a replacer a
* gauche et a droite, puis on extrait chaque moitie vers sa position finale.
*
* Limite connue : ne compense pas une eventuelle chasse (creep) appliquee
* lors de l'imposition d'origine (les pages reconstituees seront legerement
* decalees si un creep non nul avait ete utilise).
*
* @param {Doc} doc document imposes (planches doubles), document actif si omis
*/
CDImp.Livret.delivretiser = function (doc) {
try {
doc = CDImp.Util.getDocActif(doc);
} catch (e) {
CDImp.Util.erreur(e.toString());
return;
}
var nPlanches = doc.numPages;
if (nPlanches % 2 !== 0) {
CDImp.Util.erreur("Delivretiser : le document doit contenir un nombre pair de planches\n(une planche recto + une planche verso par feuille).");
return;
}
var nPagesLogiques = nPlanches * 2;
var planches;
try {
planches = CDImp.Livret.calculerOrdre(nPagesLogiques);
} catch (e) {
CDImp.Util.erreur(e.toString());
return;
}
var boxRef = doc.getPageBox({ nPage: 0, cBox: "Crop" });
var largeurPlanche = Math.abs(boxRef[2] - boxRef[0]);
var hauteur = Math.abs(boxRef[3] - boxRef[1]);
var largeurDemiPage = largeurPlanche / 2;
var destDoc;
try {
CDImp.Util.avecPrivileges(function () {
destDoc = new Doc();
destDoc.newPage({ nPage: 0, width: largeurDemiPage, height: hauteur });
for (var k = 1; k < nPagesLogiques; k++) {
destDoc.newPage({ nPage: k, width: largeurDemiPage, height: hauteur });
}
});
for (var i = 0; i < planches.length; i++) {
var planche = planches[i];
var rectGauche = [0, 0, largeurDemiPage, hauteur];
var rectDroite = [largeurDemiPage, 0, largeurPlanche, hauteur];
CDImp.Util.extraireRegionPage(doc, i + 1, rectGauche, destDoc, planche.gauche - 1);
CDImp.Util.extraireRegionPage(doc, i + 1, rectDroite, destDoc, planche.droite - 1);
}
} catch (e) {
CDImp.Util.erreur("Erreur lors de la delivretisation : " + e.toString());
return;
}
if (CDImp.Auto && CDImp.Auto.enregistrerDerniereAction) {
CDImp.Auto.enregistrerDerniereAction("Livret.delivretiser", {});
}
CDImp.Util.info("Delivretisation terminee (" + nPagesLogiques + " pages reconstituees).\nPensez a l'enregistrer (Ctrl+S) sous un nouveau nom.");
};
// -----------------------------------------------------------------------
// Point d'entree du module (appele depuis le panneau principal, Module 1)
// -----------------------------------------------------------------------
CDImp.Livret.lancer = function (doc) {
try {
doc = CDImp.Util.getDocActif(doc);
} catch (e) {
CDImp.Util.erreur(e.toString());
return;
}
var sens = app.popUpMenu(
{ cName: "Creer un livret (imposition)", cReturn: "imposer" },
{ cName: "Delivretiser (revenir a l'ordre de lecture)", cReturn: "delivretiser" }
);
if (!sens) {
return; // annulation
}
if (sens === "delivretiser") {
CDImp.Livret.delivretiser(doc);
return;
}
var nPages = doc.numPages;
var reste = nPages % 4;
if (reste !== 0) {
var nManque = 4 - reste;
var position = CDImp.Livret._demanderPositionPagesBlanches(nManque);
if (!position) {
return; // annulation utilisateur
}
CDImp.Livret.ajouterPagesBlanches(doc, nManque, position);
nPages = doc.numPages;
}
var creepMm = CDImp.Util.demanderNombre(
"Compensation de chasse (creep) en mm par feuille (0 = aucune) :",
"Creer un livret",
0
);
if (creepMm === null) {
return; // annulation utilisateur
}
var planches = CDImp.Livret.calculerOrdre(nPages);
var destDoc;
try {
destDoc = CDImp.Livret.construireDocument(doc, planches, creepMm);
} catch (e) {
CDImp.Util.erreur("Erreur lors de la construction du livret : " + e.toString());
return;
}
if (CDImp.Auto && CDImp.Auto.enregistrerDerniereAction) {
CDImp.Auto.enregistrerDerniereAction("Livret.imposer", { creepMm: creepMm });
}
CDImp.Util.info("Livret cree avec succes (" + planches.length + " planches).\nPensez a l'enregistrer (Ctrl+S) sous un nouveau nom.");
};
/**
* Variante directe sans dialogues, utilisee par le Module 7 (Automatisation)
* pour rejouer une action enregistree sur un nouveau document. Ne gere pas
* l'ajout de pages blanches (le document doit deja etre un multiple de 4).
*/
CDImp.Livret.executerAvecParams = function (doc, params) {
doc = CDImp.Util.getDocActif(doc);
var nPages = doc.numPages;
if (nPages % 4 !== 0) {
CDImp.Util.erreur("Repeter l'action : le document (" + nPages + " pages) n'est pas un multiple de 4.\nUtilisez le module Livret manuellement pour ajouter les pages blanches.");
return;
}
var planches = CDImp.Livret.calculerOrdre(nPages);
var destDoc = CDImp.Livret.construireDocument(doc, planches, params.creepMm || 0);
CDImp.Util.info("Livret recree avec succes (" + planches.length + " planches).");
return destDoc;
};

View File

@@ -0,0 +1,197 @@
// Imposition-03-NUp.js
// ---------------------------------------------------------------------------
// Suite d'imposition professionnelle - Folder-Level Script pour Adobe Acrobat Pro
// Module 3 : N-Up & Step and Repeat (amalgame et multi-poses)
//
// Depend de Imposition-00-Core.js (doit etre charge avant).
//
// Repose sur CDImp.Util.placerPageDansRect() (technique template + CropBox,
// voir avertissement dans Imposition-00-Core.js) pour positionner ET mettre
// a l'echelle chaque page source dans sa cellule de grille. A tester dans
// Acrobat Pro reel avant usage en production.
// ---------------------------------------------------------------------------
if (typeof CDImp === "undefined") {
throw new Error("Imposition-03-NUp.js : CDImp.Core doit etre charge avant ce fichier.");
}
CDImp.NUp = CDImp.NUp || {};
/**
* Construit une grille de nLignes x nColonnes sur des feuilles au format
* (largeurFeuilleMm x hauteurFeuilleMm), en mode "n-up" (pages successives
* du document source) ou "repeat" (une seule page dupliquee sur la grille).
*
* @param {Doc} srcDoc document source
* @param {Object} p parametres :
* nLignes, nColonnes {Number}
* largeurFeuilleMm, hauteurFeuilleMm {Number}
* gouttiereMm {Number} espace entre les poses
* margeMm {Number} marge technique autour de la feuille
* mode {String} "nup" | "repeat"
* nPageRepeat {Number} page 1-based a dupliquer (mode "repeat" uniquement)
* @returns {Doc} le nouveau document compose
*/
CDImp.NUp.construireGrille = function (srcDoc, p) {
var feuilleW = CDImp.Util.mmToPt(p.largeurFeuilleMm);
var feuilleH = CDImp.Util.mmToPt(p.hauteurFeuilleMm);
var margePt = CDImp.Util.mmToPt(p.margeMm);
var goutPt = CDImp.Util.mmToPt(p.gouttiereMm);
var cellW = (feuilleW - 2 * margePt - (p.nColonnes - 1) * goutPt) / p.nColonnes;
var cellH = (feuilleH - 2 * margePt - (p.nLignes - 1) * goutPt) / p.nLignes;
if (cellW <= 0 || cellH <= 0) {
throw new Error("Grille N-Up : la feuille est trop petite pour " + p.nLignes + " x " + p.nColonnes + " avec ces marges/gouttieres.");
}
// Page de reference pour le ratio d'echelle (premiere page, ou page a
// repeter en mode Step & Repeat).
var nPageRef = (p.mode === "repeat") ? p.nPageRepeat : 1;
var boxRef = srcDoc.getPageBox({ nPage: nPageRef - 1, cBox: "Crop" });
var largeurSrc = Math.abs(boxRef[2] - boxRef[0]);
var hauteurSrc = Math.abs(boxRef[3] - boxRef[1]);
var echelle = Math.min(cellW / largeurSrc, cellH / hauteurSrc);
var largeurMise = largeurSrc * echelle;
var hauteurMise = hauteurSrc * echelle;
var nParFeuille = p.nLignes * p.nColonnes;
var nFeuilles = (p.mode === "repeat") ? 1 : Math.ceil(srcDoc.numPages / nParFeuille);
var destDoc;
CDImp.Util.avecPrivileges(function () {
destDoc = new Doc();
destDoc.newPage({ nPage: 0, width: feuilleW, height: feuilleH });
for (var k = 1; k < nFeuilles; k++) {
destDoc.newPage({ nPage: k, width: feuilleW, height: feuilleH });
}
});
for (var s = 0; s < nFeuilles; s++) {
for (var r = 0; r < p.nLignes; r++) {
for (var c = 0; c < p.nColonnes; c++) {
var idxGrille = r * p.nColonnes + c;
var nSrcPage;
if (p.mode === "repeat") {
nSrcPage = p.nPageRepeat;
} else {
var idxGlobal = s * nParFeuille + idxGrille;
if (idxGlobal >= srcDoc.numPages) {
continue; // cellule vide en fin de grille
}
nSrcPage = idxGlobal + 1;
}
var cellX = margePt + c * (cellW + goutPt);
// r=0 correspond a la rangee du haut visuellement.
var cellYBas = feuilleH - margePt - (r + 1) * cellH - r * goutPt;
var offsetX = cellX + (cellW - largeurMise) / 2;
var offsetY = cellYBas + (cellH - hauteurMise) / 2;
CDImp.Util.placerPageDansRect(srcDoc, nSrcPage, destDoc, s,
[offsetX, offsetY, offsetX + largeurMise, offsetY + hauteurMise]);
}
}
}
return destDoc;
};
/**
* Demande le format de feuille cible (standard ou personnalise), en mm.
* @returns {Object|null} { largeurMm, hauteurMm }, ou null si annule
*/
CDImp.NUp._demanderFormatFeuille = function () {
var choix = app.popUpMenu(
{ cName: "A4 (210 x 297 mm)", cReturn: "A4" },
{ cName: "A3 (297 x 420 mm)", cReturn: "A3" },
{ cName: "SRA3 (320 x 450 mm)", cReturn: "SRA3" },
{ cName: "SRA4 (225 x 320 mm)", cReturn: "SRA4" },
{ cName: "Letter (215.9 x 279.4 mm)", cReturn: "LETTER" },
{ cName: "Personnalise...", cReturn: "PERSO" }
);
if (!choix) {
return null;
}
if (choix !== "PERSO") {
var f = CDImp.Util.FORMATS_MM[choix];
return { largeurMm: f[0], hauteurMm: f[1] };
}
var largeurMm = CDImp.Util.demanderNombre("Largeur de la feuille (mm) :", "Format personnalise", 320);
if (largeurMm === null) { return null; }
var hauteurMm = CDImp.Util.demanderNombre("Hauteur de la feuille (mm) :", "Format personnalise", 450);
if (hauteurMm === null) { return null; }
return { largeurMm: largeurMm, hauteurMm: hauteurMm };
};
CDImp.NUp.lancer = function (doc) {
try {
doc = CDImp.Util.getDocActif(doc);
} catch (e) {
CDImp.Util.erreur(e.toString());
return;
}
var mode = app.popUpMenu(
{ cName: "Grille N-Up (pages successives du document)", cReturn: "nup" },
{ cName: "Repetition - Step & Repeat (dupliquer une page)", cReturn: "repeat" }
);
if (!mode) { return; }
var nLignes = CDImp.Util.demanderNombre("Nombre de lignes :", "N-Up / Step & Repeat", 2);
if (nLignes === null) { return; }
var nColonnes = CDImp.Util.demanderNombre("Nombre de colonnes :", "N-Up / Step & Repeat", 2);
if (nColonnes === null) { return; }
var formatFeuille = CDImp.NUp._demanderFormatFeuille();
if (!formatFeuille) { return; }
var gouttiereMm = CDImp.Util.demanderNombre("Gouttiere entre les poses (mm) :", "N-Up / Step & Repeat", 4);
if (gouttiereMm === null) { return; }
var margeMm = CDImp.Util.demanderNombre("Marge technique autour de la feuille (mm) :", "N-Up / Step & Repeat", 5);
if (margeMm === null) { return; }
var nPageRepeat = 1;
if (mode === "repeat") {
nPageRepeat = CDImp.Util.demanderNombre("Numero de la page a dupliquer :", "Step & Repeat", doc.pageNum + 1);
if (nPageRepeat === null) { return; }
if (nPageRepeat < 1 || nPageRepeat > doc.numPages) {
CDImp.Util.erreur("Page invalide : " + nPageRepeat);
return;
}
}
var params = {
nLignes: nLignes,
nColonnes: nColonnes,
largeurFeuilleMm: formatFeuille.largeurMm,
hauteurFeuilleMm: formatFeuille.hauteurMm,
gouttiereMm: gouttiereMm,
margeMm: margeMm,
mode: mode,
nPageRepeat: nPageRepeat
};
var destDoc;
try {
destDoc = CDImp.NUp.construireGrille(doc, params);
} catch (e) {
CDImp.Util.erreur("Erreur lors de la construction de la grille : " + e.toString());
return;
}
if (CDImp.Auto && CDImp.Auto.enregistrerDerniereAction) {
CDImp.Auto.enregistrerDerniereAction("NUp.executer", params);
}
CDImp.Util.info("Mise en page terminee (" + destDoc.numPages + " feuille(s)).\nPensez a l'enregistrer (Ctrl+S) sous un nouveau nom.");
};
/**
* Variante directe sans dialogues (Module 7 - Automatisation).
*/
CDImp.NUp.executerAvecParams = function (doc, params) {
doc = CDImp.Util.getDocActif(doc);
var destDoc = CDImp.NUp.construireGrille(doc, params);
CDImp.Util.info("Mise en page recreee avec succes (" + destDoc.numPages + " feuille(s)).");
return destDoc;
};

View File

@@ -0,0 +1,222 @@
// Imposition-04-Decoupe.js
// ---------------------------------------------------------------------------
// Suite d'imposition professionnelle - Folder-Level Script pour Adobe Acrobat Pro
// Module 4 : Decoupe et reassemblage de pages
//
// Depend de Imposition-00-Core.js (doit etre charge avant).
//
// Repose sur CDImp.Util.extraireRegionPage() / placerPageDansRect() (voir
// avertissement dans Imposition-00-Core.js). A tester dans Acrobat Pro reel
// avant usage en production.
// ---------------------------------------------------------------------------
if (typeof CDImp === "undefined") {
throw new Error("Imposition-04-Decoupe.js : CDImp.Core doit etre charge avant ce fichier.");
}
CDImp.Decoupe = CDImp.Decoupe || {};
// -----------------------------------------------------------------------
// Couper les pages en deux (Split)
// -----------------------------------------------------------------------
/**
* Coupe chaque page de srcDoc en deux moities independantes et retourne un
* nouveau document contenant 2x plus de pages.
*
* @param {Doc} srcDoc
* @param {String} axe "vertical" (gauche/droite) | "horizontal" (haut/bas)
* @returns {Doc} nouveau document
*/
CDImp.Decoupe.couperPages = function (srcDoc, axe) {
var destDoc;
CDImp.Util.avecPrivileges(function () {
destDoc = new Doc();
});
var nDestPage = 0;
for (var s = 0; s < srcDoc.numPages; s++) {
var box = srcDoc.getPageBox({ nPage: s, cBox: "Crop" });
var w = Math.abs(box[2] - box[0]);
var h = Math.abs(box[3] - box[1]);
var demis; // [{rect, largeur, hauteur}, ...] dans l'ordre de lecture
if (axe === "horizontal") {
demis = [
{ rect: [0, h / 2, w, h], largeur: w, hauteur: h / 2 }, // haut
{ rect: [0, 0, w, h / 2], largeur: w, hauteur: h / 2 } // bas
];
} else {
demis = [
{ rect: [0, 0, w / 2, h], largeur: w / 2, hauteur: h }, // gauche
{ rect: [w / 2, 0, w, h], largeur: w / 2, hauteur: h } // droite
];
}
for (var d = 0; d < demis.length; d++) {
CDImp.Util.avecPrivileges(function () {
destDoc.newPage({ nPage: nDestPage, width: demis[d].largeur, height: demis[d].hauteur });
});
CDImp.Util.extraireRegionPage(srcDoc, s + 1, demis[d].rect, destDoc, nDestPage);
nDestPage++;
}
}
return destDoc;
};
// -----------------------------------------------------------------------
// Inversion de l'ordre des pages
// -----------------------------------------------------------------------
/**
* Retourne un nouveau document avec les pages de srcDoc dans l'ordre inverse.
*/
CDImp.Decoupe.inverserOrdre = function (srcDoc) {
var n = srcDoc.numPages;
var destDoc;
CDImp.Util.avecPrivileges(function () {
destDoc = new Doc();
});
for (var i = 0; i < n; i++) {
var nSrcPage = n - i; // 1-based, en partant de la derniere page
var box = srcDoc.getPageBox({ nPage: nSrcPage - 1, cBox: "Crop" });
var w = Math.abs(box[2] - box[0]);
var h = Math.abs(box[3] - box[1]);
CDImp.Util.avecPrivileges(function () {
destDoc.newPage({ nPage: i, width: w, height: h });
});
CDImp.Util.placerPageDansRect(srcDoc, nSrcPage, destDoc, i, [0, 0, w, h]);
}
return destDoc;
};
// -----------------------------------------------------------------------
// Reassemblage par motif (mélange par blocs)
// -----------------------------------------------------------------------
/**
* Reordonne les pages de srcDoc par blocs, selon un motif comme "1,3,2,4"
* (1-based, a l'interieur de chaque bloc). La taille de bloc est la longueur
* du motif ; le nombre de pages doit en etre un multiple exact.
*
* @param {Doc} srcDoc
* @param {String} motifStr ex: "1,3,2,4"
* @returns {Doc} nouveau document reordonne
*/
CDImp.Decoupe.reassembler = function (srcDoc, motifStr) {
var motif = motifStr.split(",").map(function (s) { return parseInt(s.trim(), 10); });
var taille = motif.length;
if (taille === 0 || motif.some(function (m) { return isNaN(m) || m < 1 || m > taille; })) {
throw new Error("Motif invalide : \"" + motifStr + "\". Attendu une liste de positions 1-based, ex: 1,3,2,4");
}
if (srcDoc.numPages % taille !== 0) {
throw new Error("Le document (" + srcDoc.numPages + " pages) n'est pas un multiple de la taille de bloc (" + taille + ").");
}
var nBlocs = srcDoc.numPages / taille;
var destDoc;
CDImp.Util.avecPrivileges(function () {
destDoc = new Doc();
});
var nDestPage = 0;
for (var b = 0; b < nBlocs; b++) {
for (var k = 0; k < taille; k++) {
var nSrcPage = b * taille + motif[k]; // 1-based
var box = srcDoc.getPageBox({ nPage: nSrcPage - 1, cBox: "Crop" });
var w = Math.abs(box[2] - box[0]);
var h = Math.abs(box[3] - box[1]);
CDImp.Util.avecPrivileges(function () {
destDoc.newPage({ nPage: nDestPage, width: w, height: h });
});
CDImp.Util.placerPageDansRect(srcDoc, nSrcPage, destDoc, nDestPage, [0, 0, w, h]);
nDestPage++;
}
}
return destDoc;
};
// -----------------------------------------------------------------------
// Point d'entree du module
// -----------------------------------------------------------------------
CDImp.Decoupe.lancer = function (doc) {
try {
doc = CDImp.Util.getDocActif(doc);
} catch (e) {
CDImp.Util.erreur(e.toString());
return;
}
var action = app.popUpMenu(
{ cName: "Couper les pages en deux (Split)", cReturn: "split" },
{ cName: "Inverser l'ordre des pages", cReturn: "reverse" },
{ cName: "Reassembler par motif (Shuffle)", cReturn: "shuffle" }
);
if (!action) { return; }
var destDoc, params;
try {
switch (action) {
case "split":
var axe = app.popUpMenu(
{ cName: "Vertical (gauche / droite)", cReturn: "vertical" },
{ cName: "Horizontal (haut / bas)", cReturn: "horizontal" }
);
if (!axe) { return; }
params = { action: "split", axe: axe };
destDoc = CDImp.Decoupe.couperPages(doc, axe);
break;
case "reverse":
params = { action: "reverse" };
destDoc = CDImp.Decoupe.inverserOrdre(doc);
break;
case "shuffle":
var motifStr = app.response({
cQuestion: "Motif de reassemblage par bloc (positions 1-based separees par des virgules) :",
cTitle: "Reassembler par motif",
cDefault: "1,3,2,4"
});
if (motifStr === null) { return; }
params = { action: "shuffle", motif: motifStr };
destDoc = CDImp.Decoupe.reassembler(doc, motifStr);
break;
}
} catch (e) {
CDImp.Util.erreur("Erreur : " + e.toString());
return;
}
if (CDImp.Auto && CDImp.Auto.enregistrerDerniereAction) {
CDImp.Auto.enregistrerDerniereAction("Decoupe.executer", params);
}
CDImp.Util.info("Operation terminee (" + destDoc.numPages + " page(s)).\nPensez a l'enregistrer (Ctrl+S) sous un nouveau nom.");
};
/**
* Variante directe sans dialogues (Module 7 - Automatisation).
*/
CDImp.Decoupe.executerAvecParams = function (doc, params) {
doc = CDImp.Util.getDocActif(doc);
var destDoc;
switch (params.action) {
case "split":
destDoc = CDImp.Decoupe.couperPages(doc, params.axe);
break;
case "reverse":
destDoc = CDImp.Decoupe.inverserOrdre(doc);
break;
case "shuffle":
destDoc = CDImp.Decoupe.reassembler(doc, params.motif);
break;
default:
throw new Error("Action inconnue : " + params.action);
}
CDImp.Util.info("Operation recreee avec succes (" + destDoc.numPages + " page(s)).");
return destDoc;
};

View File

@@ -0,0 +1,233 @@
// Imposition-05-Marges.js
// ---------------------------------------------------------------------------
// Suite d'imposition professionnelle - Folder-Level Script pour Adobe Acrobat Pro
// Module 5 : Gestion des marges et cadrages (Page Boxes)
//
// Depend de Imposition-00-Core.js (doit etre charge avant).
//
// ajouterEspaceBlanc() et harmoniserBoites() modifient le document EN PLACE
// (simple edition de boites, operation legere et fiable). deplacerContenu()
// doit en revanche reconstruire les pages via la technique template+CropBox
// (voir avertissement dans Imposition-00-Core.js) car deplacer du contenu
// existant n'est pas possible par simple edition de boite.
// ---------------------------------------------------------------------------
if (typeof CDImp === "undefined") {
throw new Error("Imposition-05-Marges.js : CDImp.Core doit etre charge avant ce fichier.");
}
CDImp.Marges = CDImp.Marges || {};
/**
* Resout la liste d'indices de page (0-based) a traiter.
* @param {Doc} doc
* @param {String} portee "toutes" | "courante"
*/
CDImp.Marges._resoudrePortee = function (doc, portee) {
if (portee === "courante") {
return [doc.pageNum];
}
var indices = [];
for (var i = 0; i < doc.numPages; i++) {
indices.push(i);
}
return indices;
};
// -----------------------------------------------------------------------
// Ajouter de l'espace blanc autour du contenu
// -----------------------------------------------------------------------
/**
* Agrandit la MediaBox et la CropBox de chaque page concernee sans toucher
* au contenu existant, en ajoutant les marges donnees (en mm) sur chaque
* cote.
*
* @param {Doc} doc modifie en place
* @param {Object} margesMm { haut, bas, gauche, droite } en mm
* @param {String} portee "toutes" | "courante"
*/
CDImp.Marges.ajouterEspaceBlanc = function (doc, margesMm, portee) {
var hautPt = CDImp.Util.mmToPt(margesMm.haut);
var basPt = CDImp.Util.mmToPt(margesMm.bas);
var gauchePt = CDImp.Util.mmToPt(margesMm.gauche);
var droitePt = CDImp.Util.mmToPt(margesMm.droite);
var indices = CDImp.Marges._resoudrePortee(doc, portee);
CDImp.Util.avecPrivileges(function () {
for (var k = 0; k < indices.length; k++) {
var i = indices[k];
var crop = doc.getPageBox({ nPage: i, cBox: "Crop" });
var nouveauCrop = [crop[0] - gauchePt, crop[1] - basPt, crop[2] + droitePt, crop[3] + hautPt];
doc.setPageBoxes({ nPage: i, cBox: "Crop", rBox: nouveauCrop });
doc.setPageBoxes({ nPage: i, cBox: "Media", rBox: nouveauCrop });
}
});
};
// -----------------------------------------------------------------------
// Deplacer le contenu d'une page
// -----------------------------------------------------------------------
/**
* Construit un nouveau document ou le contenu de chaque page concernee est
* decale de (dxMm, dyMm) par rapport a sa position d'origine (les pages non
* concernees sont recopiees telles quelles).
*
* @param {Doc} srcDoc
* @param {Number} dxMm decalage horizontal en mm (positif = vers la droite)
* @param {Number} dyMm decalage vertical en mm (positif = vers le haut)
* @param {String} portee "toutes" | "courante"
* @returns {Doc} nouveau document
*/
CDImp.Marges.deplacerContenu = function (srcDoc, dxMm, dyMm, portee) {
var dxPt = CDImp.Util.mmToPt(dxMm);
var dyPt = CDImp.Util.mmToPt(dyMm);
var indicesConcernes = CDImp.Marges._resoudrePortee(srcDoc, portee);
var ensembleConcernes = {};
for (var k = 0; k < indicesConcernes.length; k++) {
ensembleConcernes[indicesConcernes[k]] = true;
}
var destDoc;
CDImp.Util.avecPrivileges(function () {
destDoc = new Doc();
});
for (var i = 0; i < srcDoc.numPages; i++) {
var box = srcDoc.getPageBox({ nPage: i, cBox: "Crop" });
var w = Math.abs(box[2] - box[0]);
var h = Math.abs(box[3] - box[1]);
CDImp.Util.avecPrivileges(function () {
destDoc.newPage({ nPage: i, width: w, height: h });
});
var decalX = ensembleConcernes[i] ? dxPt : 0;
var decalY = ensembleConcernes[i] ? dyPt : 0;
CDImp.Util.placerPageDansRect(srcDoc, i + 1, destDoc, i, [decalX, decalY, decalX + w, decalY + h]);
}
return destDoc;
};
// -----------------------------------------------------------------------
// Harmonisation des boites (Media/Crop/Bleed/Trim)
// -----------------------------------------------------------------------
/**
* Pour chaque page concernee, force les 4 boites (Media, Crop, Bleed, Trim)
* a coincider avec la valeur actuelle de la boite de reference cBoxRef.
*
* @param {Doc} doc modifie en place
* @param {String} cBoxRef "Media" | "Crop" | "Bleed" | "Trim"
* @param {String} portee "toutes" | "courante"
*/
CDImp.Marges.harmoniserBoites = function (doc, cBoxRef, portee) {
var indices = CDImp.Marges._resoudrePortee(doc, portee);
var toutesBoites = ["Media", "Crop", "Bleed", "Trim"];
CDImp.Util.avecPrivileges(function () {
for (var k = 0; k < indices.length; k++) {
var i = indices[k];
var rectRef = doc.getPageBox({ nPage: i, cBox: cBoxRef });
for (var b = 0; b < toutesBoites.length; b++) {
doc.setPageBoxes({ nPage: i, cBox: toutesBoites[b], rBox: rectRef });
}
}
});
};
// -----------------------------------------------------------------------
// Point d'entree du module
// -----------------------------------------------------------------------
CDImp.Marges.lancer = function (doc) {
try {
doc = CDImp.Util.getDocActif(doc);
} catch (e) {
CDImp.Util.erreur(e.toString());
return;
}
var action = app.popUpMenu(
{ cName: "Ajouter de l'espace blanc autour du contenu", cReturn: "espace" },
{ cName: "Deplacer le contenu (decalage)", cReturn: "deplacer" },
{ cName: "Harmoniser les boites (Media/Crop/Bleed/Trim)", cReturn: "harmoniser" }
);
if (!action) { return; }
var portee = app.popUpMenu(
{ cName: "Toutes les pages", cReturn: "toutes" },
{ cName: "Page courante uniquement", cReturn: "courante" }
);
if (!portee) { return; }
var params;
try {
switch (action) {
case "espace": {
var haut = CDImp.Util.demanderNombre("Marge en haut (mm) :", "Ajouter de l'espace blanc", 0);
if (haut === null) { return; }
var bas = CDImp.Util.demanderNombre("Marge en bas (mm) :", "Ajouter de l'espace blanc", 0);
if (bas === null) { return; }
var gauche = CDImp.Util.demanderNombre("Marge a gauche (mm) :", "Ajouter de l'espace blanc", 0);
if (gauche === null) { return; }
var droite = CDImp.Util.demanderNombre("Marge a droite (mm) :", "Ajouter de l'espace blanc", 0);
if (droite === null) { return; }
params = { action: "espace", marges: { haut: haut, bas: bas, gauche: gauche, droite: droite }, portee: portee };
CDImp.Marges.ajouterEspaceBlanc(doc, params.marges, portee);
break;
}
case "deplacer": {
var dx = CDImp.Util.demanderNombre("Decalage horizontal (mm, + = vers la droite) :", "Deplacer le contenu", 0);
if (dx === null) { return; }
var dy = CDImp.Util.demanderNombre("Decalage vertical (mm, + = vers le haut) :", "Deplacer le contenu", 0);
if (dy === null) { return; }
params = { action: "deplacer", dxMm: dx, dyMm: dy, portee: portee };
var destDoc = CDImp.Marges.deplacerContenu(doc, dx, dy, portee);
if (CDImp.Auto && CDImp.Auto.enregistrerDerniereAction) {
CDImp.Auto.enregistrerDerniereAction("Marges.executer", params);
}
CDImp.Util.info("Contenu deplace dans un nouveau document.\nPensez a l'enregistrer (Ctrl+S) sous un nouveau nom.");
return;
}
case "harmoniser": {
var refBoite = app.popUpMenu(
{ cName: "Aligner sur la TrimBox", cReturn: "Trim" },
{ cName: "Aligner sur la CropBox", cReturn: "Crop" },
{ cName: "Aligner sur la BleedBox", cReturn: "Bleed" },
{ cName: "Aligner sur la MediaBox", cReturn: "Media" }
);
if (!refBoite) { return; }
params = { action: "harmoniser", refBoite: refBoite, portee: portee };
CDImp.Marges.harmoniserBoites(doc, refBoite, portee);
break;
}
}
} catch (e) {
CDImp.Util.erreur("Erreur : " + e.toString());
return;
}
if (CDImp.Auto && CDImp.Auto.enregistrerDerniereAction) {
CDImp.Auto.enregistrerDerniereAction("Marges.executer", params);
}
CDImp.Util.info("Operation terminee avec succes.");
};
/**
* Variante directe sans dialogues (Module 7 - Automatisation).
*/
CDImp.Marges.executerAvecParams = function (doc, params) {
doc = CDImp.Util.getDocActif(doc);
switch (params.action) {
case "espace":
CDImp.Marges.ajouterEspaceBlanc(doc, params.marges, params.portee);
return doc;
case "deplacer":
return CDImp.Marges.deplacerContenu(doc, params.dxMm, params.dyMm, params.portee);
case "harmoniser":
CDImp.Marges.harmoniserBoites(doc, params.refBoite, params.portee);
return doc;
default:
throw new Error("Action inconnue : " + params.action);
}
};

View File

@@ -0,0 +1,242 @@
// Imposition-06-Reperes.js
// ---------------------------------------------------------------------------
// Suite d'imposition professionnelle - Folder-Level Script pour Adobe Acrobat Pro
// Module 6 : Reperes de coupe, textes et numerotation
//
// Depend de Imposition-00-Core.js (doit etre charge avant).
//
// Les traits de coupe utilisent des annotations de type "Line" (API stable
// et bien documentee : doc.addAnnot). La numerotation utilise
// doc.addWatermarkFromText, egalement standard. Ces deux API sont plus
// fiables que la technique template+CropBox utilisee dans les autres
// modules, mais les constantes exactes de app.constants.align doivent etre
// confirmees dans Acrobat Pro reel (non installe dans cet environnement de
// developpement).
// ---------------------------------------------------------------------------
if (typeof CDImp === "undefined") {
throw new Error("Imposition-06-Reperes.js : CDImp.Core doit etre charge avant ce fichier.");
}
CDImp.Reperes = CDImp.Reperes || {};
// -----------------------------------------------------------------------
// Traits de coupe (8 reperes aux 4 coins, bases sur la TrimBox)
// -----------------------------------------------------------------------
/**
* Ajoute 8 traits de coupe (2 par coin) autour de la zone de delimitation
* (TrimBox par defaut) de chaque page.
*
* @param {Doc} doc modifie en place
* @param {Object} opts
* decalageMm {Number} retrait entre le bord du format fini et le trait
* longueurMm {Number} longueur de chaque trait
* epaisseurPt {Number} epaisseur du trait en points
* cBoxRef {String} boite de reference, "Trim" par defaut
*/
CDImp.Reperes.ajouterTraitsCoupe = function (doc, opts) {
var decalage = CDImp.Util.mmToPt(opts.decalageMm);
var longueur = CDImp.Util.mmToPt(opts.longueurMm);
var epaisseur = opts.epaisseurPt || 0.25;
var cBoxRef = opts.cBoxRef || "Trim";
CDImp.Util.avecPrivileges(function () {
for (var i = 0; i < doc.numPages; i++) {
var box = doc.getPageBox({ nPage: i, cBox: cBoxRef });
var llx = Math.min(box[0], box[2]);
var lly = Math.min(box[1], box[3]);
var urx = Math.max(box[0], box[2]);
var ury = Math.max(box[1], box[3]);
var traits = [
// bas-gauche
[llx - decalage - longueur, lly, llx - decalage, lly],
[llx, lly - decalage - longueur, llx, lly - decalage],
// bas-droite
[urx + decalage, lly, urx + decalage + longueur, lly],
[urx, lly - decalage - longueur, urx, lly - decalage],
// haut-gauche
[llx - decalage - longueur, ury, llx - decalage, ury],
[llx, ury + decalage, llx, ury + decalage + longueur],
// haut-droite
[urx + decalage, ury, urx + decalage + longueur, ury],
[urx, ury + decalage, urx, ury + decalage + longueur]
];
for (var t = 0; t < traits.length; t++) {
var pts = traits[t];
var rect = [
Math.min(pts[0], pts[2]) - epaisseur, Math.min(pts[1], pts[3]) - epaisseur,
Math.max(pts[0], pts[2]) + epaisseur, Math.max(pts[1], pts[3]) + epaisseur
];
doc.addAnnot({
page: i,
type: "Line",
rect: rect,
linePoints: pts,
width: epaisseur,
color: color.black,
print: true,
name: "CDImp_repere_" + i + "_" + t
});
}
}
});
};
// -----------------------------------------------------------------------
// Numerotation sequentielle avancee
// -----------------------------------------------------------------------
/**
* Remplace les variables dynamiques dans un modele de texte.
* Variables : [Page], [Total], [NomDuFichier], [Date]
*/
CDImp.Reperes.remplacerVariables = function (modele, doc, numPage, total) {
var date = util.printd("dd/mm/yyyy", new Date());
return modele
.replace(/\[Page\]/g, String(numPage))
.replace(/\[Total\]/g, String(total))
.replace(/\[NomDuFichier\]/g, doc.documentFileName || "")
.replace(/\[Date\]/g, date);
};
/**
* Ajoute une numerotation / mention variable sur chaque page (sauf la
* couverture si demande).
*
* @param {Doc} doc modifie en place
* @param {Object} opts
* modele {String} ex: "Page [Page] / [Total]"
* police {String} ex: "Helvetica"
* taillePt {Number}
* positionXmm, positionYmm {Number} distance depuis le bord (selon coin)
* coin {String} "bas_droite" | "bas_gauche" | "haut_droite" | "haut_gauche"
* masquerPremierePage {Boolean}
*/
CDImp.Reperes.ajouterNumerotation = function (doc, opts) {
var total = doc.numPages;
var A = app.constants.align;
var coins = {
bas_droite: { h: A.right, v: A.bottom },
bas_gauche: { h: A.left, v: A.bottom },
haut_droite: { h: A.right, v: A.top },
haut_gauche: { h: A.left, v: A.top }
};
var coin = coins[opts.coin] || coins.bas_droite;
var offsetXPt = CDImp.Util.mmToPt(opts.positionXmm);
var offsetYPt = CDImp.Util.mmToPt(opts.positionYmm);
CDImp.Util.avecPrivileges(function () {
for (var i = 0; i < doc.numPages; i++) {
var numPage = i + 1;
if (opts.masquerPremierePage && numPage === 1) {
continue;
}
var texte = CDImp.Reperes.remplacerVariables(opts.modele, doc, numPage, total);
doc.addWatermarkFromText({
cText: texte,
cFont: opts.police || "Helvetica",
nFontSize: opts.taillePt || 9,
nHorizAlign: coin.h,
nVertAlign: coin.v,
nHorizValue: offsetXPt,
nVertValue: offsetYPt,
nRotation: 0,
nOpacity: 1.0,
bOnTop: true,
bOnscreen: true,
bPrintable: true,
nStart: i,
nEnd: i
});
}
});
};
// -----------------------------------------------------------------------
// Point d'entree du module
// -----------------------------------------------------------------------
CDImp.Reperes.lancer = function (doc) {
try {
doc = CDImp.Util.getDocActif(doc);
} catch (e) {
CDImp.Util.erreur(e.toString());
return;
}
var action = app.popUpMenu(
{ cName: "Ajouter les traits de coupe", cReturn: "traits" },
{ cName: "Ajouter une numerotation / mention variable", cReturn: "numero" }
);
if (!action) { return; }
var params;
try {
if (action === "traits") {
var decalageMm = CDImp.Util.demanderNombre("Retrait par rapport au format fini (mm) :", "Traits de coupe", 3);
if (decalageMm === null) { return; }
var longueurMm = CDImp.Util.demanderNombre("Longueur du trait (mm) :", "Traits de coupe", 5);
if (longueurMm === null) { return; }
params = { action: "traits", decalageMm: decalageMm, longueurMm: longueurMm, epaisseurPt: 0.25 };
CDImp.Reperes.ajouterTraitsCoupe(doc, params);
} else {
var modele = app.response({ cQuestion: "Modele de texte ([Page], [Total], [NomDuFichier], [Date]) :", cTitle: "Numerotation", cDefault: "[Page] / [Total]" });
if (modele === null) { return; }
var taillePt = CDImp.Util.demanderNombre("Taille de police (pt) :", "Numerotation", 9);
if (taillePt === null) { return; }
var coin = app.popUpMenu(
{ cName: "Bas droite", cReturn: "bas_droite" },
{ cName: "Bas gauche", cReturn: "bas_gauche" },
{ cName: "Haut droite", cReturn: "haut_droite" },
{ cName: "Haut gauche", cReturn: "haut_gauche" }
);
if (!coin) { return; }
var positionXmm = CDImp.Util.demanderNombre("Distance horizontale depuis le bord (mm) :", "Numerotation", 10);
if (positionXmm === null) { return; }
var positionYmm = CDImp.Util.demanderNombre("Distance verticale depuis le bord (mm) :", "Numerotation", 10);
if (positionYmm === null) { return; }
var masquerStr = app.popUpMenu(
{ cName: "Afficher sur toutes les pages", cReturn: "non" },
{ cName: "Masquer sur la premiere page (couverture)", cReturn: "oui" }
);
if (!masquerStr) { return; }
params = {
action: "numero",
modele: modele,
police: "Helvetica",
taillePt: taillePt,
coin: coin,
positionXmm: positionXmm,
positionYmm: positionYmm,
masquerPremierePage: masquerStr === "oui"
};
CDImp.Reperes.ajouterNumerotation(doc, params);
}
} catch (e) {
CDImp.Util.erreur("Erreur : " + e.toString());
return;
}
if (CDImp.Auto && CDImp.Auto.enregistrerDerniereAction) {
CDImp.Auto.enregistrerDerniereAction("Reperes.executer", params);
}
CDImp.Util.info("Operation terminee avec succes.");
};
/**
* Variante directe sans dialogues (Module 7 - Automatisation).
*/
CDImp.Reperes.executerAvecParams = function (doc, params) {
doc = CDImp.Util.getDocActif(doc);
if (params.action === "traits") {
CDImp.Reperes.ajouterTraitsCoupe(doc, params);
} else if (params.action === "numero") {
CDImp.Reperes.ajouterNumerotation(doc, params);
} else {
throw new Error("Action inconnue : " + params.action);
}
return doc;
};

View File

@@ -0,0 +1,189 @@
// Imposition-07-Auto.js
// ---------------------------------------------------------------------------
// Suite d'imposition professionnelle - Folder-Level Script pour Adobe Acrobat Pro
// Module 7 : Automatisation (sequences de commandes / presets)
//
// Depend de Imposition-00-Core.js (doit etre charge avant) ainsi que des
// modules 2 a 6 pour le rejeu d'actions (chacun expose une fonction
// "executerAvecParams(doc, params)" sans dialogue, appelee ici).
//
// Les presets sont stockes dans l'objet global persistant
// global.impositionSettings (survit aux fermetures d'Acrobat grace a
// global.setPersistent). La "derniere action" n'est gardee qu'en memoire
// pour la session courante.
// ---------------------------------------------------------------------------
if (typeof CDImp === "undefined") {
throw new Error("Imposition-07-Auto.js : CDImp.Core doit etre charge avant ce fichier.");
}
CDImp.Auto = CDImp.Auto || {};
// Initialise l'espace de stockage persistant des presets.
CDImp.Util.avecPrivileges(function () {
if (typeof global.impositionSettings === "undefined") {
global.impositionSettings = {};
}
global.setPersistent("impositionSettings", true);
});
// Memoire de la derniere action executee (module + parametres), pour le
// bouton "Repeter la derniere action". Reinitialisee a chaque session.
CDImp.Auto.derniereAction = null;
// Table de correspondance "cle d'action" -> fonction de rejeu sans dialogue.
// Chaque entree appelle la variante "executerAvecParams" du module concerne.
CDImp.Auto._dispatch = {
"Livret.imposer": function (doc, params) { return CDImp.Livret.executerAvecParams(doc, params); },
"Livret.delivretiser": function (doc) { return CDImp.Livret.delivretiser(doc); },
"NUp.executer": function (doc, params) { return CDImp.NUp.executerAvecParams(doc, params); },
"Decoupe.executer": function (doc, params) { return CDImp.Decoupe.executerAvecParams(doc, params); },
"Marges.executer": function (doc, params) { return CDImp.Marges.executerAvecParams(doc, params); },
"Reperes.executer": function (doc, params) { return CDImp.Reperes.executerAvecParams(doc, params); }
};
/**
* Appelee par chaque module a l'issue d'une execution reussie, pour
* permettre "Repeter la derniere action" et la sauvegarde en preset.
*
* @param {String} cle une des cles de CDImp.Auto._dispatch
* @param {Object} params parametres necessaires au rejeu
*/
CDImp.Auto.enregistrerDerniereAction = function (cle, params) {
if (!CDImp.Auto._dispatch[cle]) {
return; // cle inconnue : on ignore silencieusement (pas bloquant)
}
CDImp.Auto.derniereAction = { cle: cle, params: params || {} };
};
/**
* Rejoue la derniere action enregistree sur le document fourni (typiquement
* un nouveau document different de celui d'origine), sans repasser par les
* boites de dialogue.
*/
CDImp.Auto.repeterDerniereAction = function (doc) {
if (!CDImp.Auto.derniereAction) {
CDImp.Util.erreur("Aucune action a repeter : lancez d'abord un module d'imposition.");
return;
}
try {
doc = CDImp.Util.getDocActif(doc);
var fn = CDImp.Auto._dispatch[CDImp.Auto.derniereAction.cle];
var destDoc = fn(doc, CDImp.Auto.derniereAction.params);
CDImp.Util.info("Derniere action (" + CDImp.Auto.derniereAction.cle + ") rejouee avec succes.");
return destDoc;
} catch (e) {
CDImp.Util.erreur("Erreur lors de la repetition de l'action : " + e.toString());
}
};
/**
* Enregistre la derniere action sous un nom de preset persistant.
*/
CDImp.Auto.sauvegarderPreset = function (nomPreset) {
if (!CDImp.Auto.derniereAction) {
CDImp.Util.erreur("Aucune action a enregistrer : lancez d'abord un module d'imposition.");
return;
}
CDImp.Util.avecPrivileges(function () {
global.impositionSettings[nomPreset] = CDImp.Auto.derniereAction;
});
CDImp.Util.info("Preset \"" + nomPreset + "\" enregistre.");
};
/**
* Retourne la liste des noms de presets enregistres.
*/
CDImp.Auto.listerPresets = function () {
var noms = [];
for (var nom in global.impositionSettings) {
if (global.impositionSettings.hasOwnProperty(nom)) {
noms.push(nom);
}
}
return noms;
};
/**
* Charge un preset et l'execute directement sur le document fourni.
*/
CDImp.Auto.chargerEtExecuterPreset = function (doc, nomPreset) {
var action = global.impositionSettings[nomPreset];
if (!action) {
CDImp.Util.erreur("Preset introuvable : " + nomPreset);
return;
}
try {
doc = CDImp.Util.getDocActif(doc);
var fn = CDImp.Auto._dispatch[action.cle];
var destDoc = fn(doc, action.params);
CDImp.Util.info("Preset \"" + nomPreset + "\" execute avec succes.");
return destDoc;
} catch (e) {
CDImp.Util.erreur("Erreur lors de l'execution du preset : " + e.toString());
}
};
/**
* Supprime un preset enregistre.
*/
CDImp.Auto.supprimerPreset = function (nomPreset) {
CDImp.Util.avecPrivileges(function () {
delete global.impositionSettings[nomPreset];
});
};
// -----------------------------------------------------------------------
// Point d'entree du module
// -----------------------------------------------------------------------
CDImp.Auto.lancer = function (doc) {
try {
doc = CDImp.Util.getDocActif(doc);
} catch (e) {
CDImp.Util.erreur(e.toString());
return;
}
var presets = CDImp.Auto.listerPresets();
var options = [
{ cName: "Repeter la derniere action sur ce document", cReturn: "repeter" },
{ cName: "Enregistrer la configuration actuelle comme preset", cReturn: "sauvegarder" }
];
if (presets.length > 0) {
options.push({ cName: "Charger et executer un preset...", cReturn: "charger" });
options.push({ cName: "Supprimer un preset...", cReturn: "supprimer" });
}
var choix = app.popUpMenu.apply(app, options);
if (!choix) { return; }
switch (choix) {
case "repeter":
CDImp.Auto.repeterDerniereAction(doc);
break;
case "sauvegarder": {
var nom = app.response({ cQuestion: "Nom du preset :", cTitle: "Enregistrer la configuration", cDefault: "MonPreset" });
if (nom === null || nom === "") { return; }
CDImp.Auto.sauvegarderPreset(nom);
break;
}
case "charger": {
var presetsOptions = presets.map(function (n) { return { cName: n, cReturn: n }; });
var nomChoisi = app.popUpMenu.apply(app, presetsOptions);
if (!nomChoisi) { return; }
CDImp.Auto.chargerEtExecuterPreset(doc, nomChoisi);
break;
}
case "supprimer": {
var presetsOptionsSupp = presets.map(function (n) { return { cName: n, cReturn: n }; });
var nomASupprimer = app.popUpMenu.apply(app, presetsOptionsSupp);
if (!nomASupprimer) { return; }
CDImp.Auto.supprimerPreset(nomASupprimer);
CDImp.Util.info("Preset \"" + nomASupprimer + "\" supprime.");
break;
}
}
};