Nalezení stylů fontu ve vrstvách

lekce 11.

Automatizování úloh v Adobe Photoshop

Pomocí skriptovacího jazyka JavaScript

Cíl lekce

Náplní této lekce je nalezení fontů ve všech vrstvách dokumentu. Skript je užitečný v případě, kdy uživatel dostane grafiku na upravení a potřebuje zjistit styly fontů, jakými jsou texty v dokumentu napsány. Skript prohledá všechny vrstvy zda-li obsahují fonty a uloží je do pole. Poté je prohledá, aby pole neobsahovalo duplicitní prvky a nakonec setřídít. V lekci využívám již znamý ActionDescriptor a ActionReference s konstantami.

images/lekce_9Puvodni.png.png

Původní obrázek - obrázek 11.1

images/lekce_9Vysledek.png

Výsledek - obrázek 11.2


Využité třídy v této lekci

  • Application: pro práci s atributy dokumentu.
  • Konstanty: konstanty typu event ID.
  • Descriptor: pro popis prováděných akcí z rozhraní.
  • Reference: pro zadání objektů do ActionDescriptoru.

Funkce na odstranění duplicit v poli

Jako první krok si vytvořím funkci, která bude mít účel výsledné pole s fonty projít a odstranit duplicitní fonty. K tomuto si vytvořím pomocné pole, které poté se poté bude vracet jako výsledné. Dále potřebuji dva cykly, jeden k procházení vstupního pole předaným parametrem a druhý pro procházení lokálního pole a zjišťování zda-li pole již tento prvek obsahuje. Použiji (for) cykly, abych opět jako v předchozích lekcích mohl pracovat s indexy. Dále do této části již doplním známe metody na získání event ID.


function getArrayWithoutDuplicits(array){
    //pole
    var temp = []
        
    //projdi pole z parametru
   for(var i = array.length; i >= 0;i--){
        //projdi seřazené pole
        var found = false;
        for(j = temp.length; j >= 0;j--){
            //zkontroluj duplicitu
            if(array[i] == temp[j]) {
                found = true;
            }
         }
     
     //pokud ještě není v poli, přidej
     if(!found) {
            temp.push(array[i]);
        }
    }
    return temp;
}
				

Konstanty v této lekci

V této lekci opět využívám již více konstant pro popis akce pro program Adobe Photoshop. Jako v předchozích lekcí je přehled konstant možné najít v rozšířených skriptovacích příručkách v dodatku nebo v github repozitáři zde. Vyvětlení konstant: Dcmn dokument třídy, Ordn druh výčtu, Trgt cíl, NmbL klíčové číslo vrstvy, Lyr třída vrstvy, textKey text jako klíč, textStyleRange seznam textů, textStyle textové styly, fontPostScriptName dané fonty PostScriptu.


Funkce pro vrácení fontů

Teď již mám vše připravené a mohu vytvořit hlavní funkci pro získání fontů z vrstev. Vytvořím si objekt třídy ActionReference a následně do něj vložím konstanty pro procházení. Celý objekt poté vložím do metody executeAction, kde ještě dodám, že chci vrátit číslo. Tedy počet vrstev plus jedna, jelikož hodlám zahrnout i pozadí. Počet si uložím do proměnné a zárověň si vytvořím pole pro získané fonty. Dále vytvořím první cyklus, který má za úkol projít všechny vrstvy, cyklus jde od největšího po nulu a to z toho důvodu, že maximální index má v programu Adobe Photoshop vždy první vrstva, tedy z většiny případů vrtsva pozadí. V cyklu si vytvořím referenci, descriptor, seznam pro fonty. Do reference vložím aktuální vrstvu, která je pro ActionDescriptor jako navrátová hodnota z metody executeAction pro popsání objektu. Toto musím obalit do bloku try {}catch(){}, abych ošetřil výjimku, že vrstva neosahuje fonty, v případě výjimky jednoduše pokračuji dál. Poté se zeptám jestli ActionDescriptor obsahuje klíč pro font. V případě že ne, přeskočím na další vrstvu. Pokud obsahuje font, pomocí metody getObjectValue() získám seznam fontů. Ten v následujícím cyklu procházím a jako při obyčejné práci s pole k němu přes index přistupuji, stejnou metodou si vrátím font a ten poté vložím na konec pole. Nakonci již pouze vrátím pole fontů, které ovšem může obsahovat i duplicitní prvky. Toto vše ilustruje kód níže.

function getFonts() {
    var actionRef = new ActionReference();
    actionRef.putEnumerated(charIDToTypeID('Dcmn'),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));
   //získání počtu vrstev
    var countLayers = executeActionGet(actionRef).getInteger(charIDToTypeID('NmbL'))+1,
        fonts = [];

    //procházení každé vrstvy
    for(var i = countLayers; i >= 0; i--){
        var reference = new ActionReference();
        var descriptor;
        var list;

        //získání vrstvy do reference
        reference.putIndex( charIDToTypeID( 'Lyr ' ), i );

        // získání popisu
        try {
            descriptor = executeActionGet(reference);
        } catch (e) {
            continue;
        }

        // pokud popis obsahuje klíč fontu
        if(!descriptor.hasKey(stringIDToTypeID( 'textKey' )))
            continue;

        //získání seznamu fontů
        list = descriptor.getObjectValue(stringIDToTypeID('textKey'))
            .getList(stringIDToTypeID('textStyleRange'));
        if(!list) continue;

        //prochází seznamu a vkládání fontu do pole
        var countStyles = list.count;

        while (countStyles--) {
            var textStyle = list.getObjectValue(countStyles)
                                    .getObjectValue(stringIDToTypeID('textStyle'));
            //pokud neobsahuje proměnná fontPostScriptName, pokračuj
            if(!textStyle || !textStyle.hasKey(stringIDToTypeID('fontPostScriptName'))) continue;

            var n = textStyle.getString(stringIDToTypeID('fontPostScriptName'));
            fonts.push(n);
        }
    }
    //využití funkce pro eliminování duplicit a setřídění metodou sort()
    return getArrayWithoutDuplicits(fonts).sort();
}

Vypsání fontů uživateli

V poslední části jen zbývá vypsat velikost pole a obsah pole uživateli, to provedu jednoduchou metodou alert(), v které zároveň použiji metodu join() pro spojování řetězců.

if (documents.length) {
    var fonts = getFonts();
   
    alert(" Nalezené fonty: \n'+fonts.join('\n')", "Počet fontů " + fonts.length);
}else{
	alert("Žádný dokument není otevřený.", "Varování");
}

Závěr

Na závěr této lekce bych chtěl dodat upozornění k tomuto skriptu. Při psaní jsem objevil chybu, která může způsobit nepřesné provedení skriptu. Jedná se o případ kdy při čerstvě špuštěném Photoshopu může Photoshop špatně, či vůbec nastavit atribut textové vrstvy. Konkrétně postScriptName atribut. V případě změny fontů, se atribut přenastaví a problém nenastane. Ovšem při počátečních hodnatách se při prvním zadaní může tato chyba objevit. Je to chyba v programu a ani po dlouhém hledání na developerských fórech a příručkách, jsem nebyl schopný najít odpověď na tento nedostatek, ani najít způsob jak se mu vyvarovat. Proto bych v závěrem této lekce na tuto chybu upozornil a varoval tak uživatele, který skript použijí.


Výsledný zdrojový kód

if (documents.length) {
    var fontsFound = getFonts();
    alert('Nalezené fonty: \n' + fontsFound.join('\n'), "Počet fontů: " + fontsFound.length);
}

function getArrayWithoutDuplicits(array) {
    //pole
    var temp = []

    //projdi pole z parametru
    for (var i = array.length; i >= 0; i--) {
        //projdi seřazené pole
        var found = false;
        for (j = temp.length; j >= 0; j--) {
            //zkontroluj duplicitu
            if (array[i] == temp[j]) {
                found = true;
            }
        }

        //pokud ještě není v poli, přidej
        if (!found) {
            temp.push(array[i]);
        }
    }
    return temp;
}


function getFonts() {
    var actionRef = new ActionReference();
    actionRef.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
    //získání počtu vrstev
    var countLayers = executeActionGet(actionRef).getInteger(charIDToTypeID('NmbL')) + 1,
        fonts = [];

    //procházení každé vrstvy
    for (var i = countLayers; i >= 0; i--) {
        var reference = new ActionReference();
        var descriptor;
        var list;

        //získání vrstvy do reference
        reference.putIndex(charIDToTypeID('Lyr '), i);

        // získání popisu
        try {
            descriptor = executeActionGet(reference);

        } catch (e) {
            continue;
        }

        // pokud popis obsahuje klíč fontu
        if (!descriptor.hasKey(stringIDToTypeID('textKey')))
            continue;

        //získání seznamu fontů
        list = descriptor.getObjectValue(stringIDToTypeID('textKey'))
                            .getList(stringIDToTypeID('textStyleRange'));
        if (!list) continue;

        //prchází seznamu a vkládání fontu do pole
        var countStyles = list.count;

        while (countStyles--) {
            var textStyle = list.getObjectValue(countStyles)
                                    .getObjectValue(stringIDToTypeID('textStyle'));

            //pokud neobsahuje proměnná fontPostScriptName, pokračuj
            if (!textStyle || !textStyle.hasKey(stringIDToTypeID('fontPostScriptName'))) continue;

            //nalezení názvu fontu
            var n = textStyle.getString(stringIDToTypeID('fontPostScriptName'));
            //vložení fontu do pole
            fonts.push(n);
        }
    }
    //využití funkce pro eliminování duplicit a setřídění metodou sort()
    return getArrayWithoutDuplicits(fonts).sort();
}