• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Is it possible to detect how many OpenType stylistic sets a font has in code?

Participant ,
Aug 19, 2024 Aug 19, 2024

Copy link to clipboard

Copied

Hello community,


I am working on a small script that has to do with toggling OpenType style sets. I've figured out how to actually turn on a specific stylistic set using this syntax:

textVariable.textRange.characterAttributes.stylisticSets = STYLYSTIC_SET_NUMBER;

 

The problem is that, by default, all fonts that support stylistic sets have 20 such sets defined in the UI, with most of them grayed out if not actually available. However, when I try to loop through the available Stylistic Set options of a selected text using a for loop, even the grayed out ones are picked up.


Do you know if there is a way to validate the actual available style sets so that I can count them properly?

TOPICS
Scripting

Views

352

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe
Community Expert ,
Aug 19, 2024 Aug 19, 2024

Copy link to clipboard

Copied

@Eduard Buta That's a good question... I couldn't work out how to find that out. As you discovered, if you set a stylistic set that the font doesn't have, it will set just fine, no error. You could potentially analyse the text metrics after the change to see if that particular text changed and that would prove that the stylistic set did exist in the font—but I can't imagine that would help in almost any real case. Sorry, I'm not much help.

- Mark

 

P.S. While I'm here, I'll just check that you understand an unusual feature of setting and getting `characterAttributes.stylisticSets`: that they take a "binary coded decimal", not a normal decimal. I've written a couple of functions to set and get stylistic sets. If you already know that, then you can ignore from here on. 🙂

 

You can use this to SET:

 

/**
 * Sets which stylistic sets are active on selected text.
 * Works with Indesign and Illustrator.
 */
(function () {

    // example usage: let's say we want to turn on stylistic sets 1, 2 and 5.
    setStylisticSets(app.activeDocument.selection, [1, 2, 5, 6]); // note: one-based indexing here

})();

/**
 * Set active Opentype stylistic sets.
 * @author m1b
 * @version 2022-12-24
 * @param {TextFrame|TextRange} text - the text.
 * @param {Array<Number>} stylisticSetIndices - indices of the stylistic sets to activate.
 */
function setStylisticSets(text, stylisticSetIndices) {

    if (
        text.constructor.name == 'Array'
        && text.length > 0
    )
        text = text[0];

    if (text.hasOwnProperty('textRange'))
        text = text.textRange;

    else if (text.hasOwnProperty('texts'))
        text = text.texts[0];

    if (app.name.search(/indesign/i) == -1)
        // convert to zero-based indexing
        for (var i = 0; i < stylisticSetIndices.length; i++)
            stylisticSetIndices[i]--;

    if (text.hasOwnProperty('characterAttributes'))
        text.characterAttributes.stylisticSets = getBinaryCodedDecimalForArray(stylisticSetIndices);

    else if (text.hasOwnProperty('otfStylisticSets'))
        text.otfStylisticSets = getBinaryCodedDecimalForArray(stylisticSetIndices);

    else {
        alert('Please select some text and try again.');
        return;
    }

};

/**
 * Returns a binary coded decimal number (BCD)
 * representing binary bits.
 *
 * Use case: in Indesign and Illustrator,
 * `characterAttributes.stylisticSets`
 * are stored as a BCD.
 *
 * Example 1:
 *   - for Opentype stylistic sets 1 and 5:
 *   - `numbers` array = [0,4]
 *   - returns 17 (10001 in binary).
 *
 * Example 2:
 *   - for opentype stylistic sets 3, 4 and 5:
 *   - `numbers` array = [2,3,4]
 *   - returns 28 (11100 in binary).
 *
 * @author m1b
 * @version 2023-07-12
 * @param {Array<Number>} numbers - the stylistic sets indices, eg. [2,4,5].
 * @returns {Number} - the decimal number representing the numbers.
 */
function getBinaryCodedDecimalForArray(numbers) {

    var arr = numbers.slice().sort(),
        bin = [],
        len = arr[arr.length - 1],
        i = len + 1,
        val = arr.pop();

    while (i--) {

        if (val == i) {
            bin[len - i] = 1;
            val = arr.pop();
        }

        else {
            bin[len - i] = 0;
        }

    }

    // join into binary number and parse binary as decimal
    return parseInt(bin.join(''), 2);

};

 

 

And use this to GET:

 

/**
 * Reads which stylistic sets are active given selected text.
 */
(function () {

    var flags = getActiveStylisticSetsForIllustrator(app.activeDocument.selection);

    if (flags) {

        // just for demonstrating:
        for (var i = flags.length - 1; i >= 0; i--)
            if (flags[i] == true)
                $.writeln('Stylistic Set ' + (flags.length - i) + ' is active.');

    }

})();

/**
 * Returns an array of booleans representing
 * active opentype stylistic sets.
 * Note: will only return results of stylistic
 * sets applied to the first character of `text`.
 * @author m1b
 * @version 2023-10-18
 * @param {TextRange} text - the text to test.
 * @returns {Array<Boolean>}
 */
function getActiveStylisticSetsForIllustrator(text) {

    var setsNumber;

    if (text.constructor.name == 'Array')
        text = text[0];

    if (text.constructor.name == 'TextFrame')
        text = text.textRange;

    if (text.hasOwnProperty('stylisticSets'))
        setsNumber = text.stylisticSets;

    else if (text.hasOwnProperty('characterAttributes'))
        setsNumber = text.characterAttributes.stylisticSets;

    if (setsNumber != undefined)
        return getNumberAsBits(setsNumber);

};

/**
 * Convert a decimal number into an array of
 * boolean values for each binary place.
 * For example:
 * for n: 2 -> [true, false].
 * for n: 37 -> [true, false, false, true, false, true].
 * @author m1b
 * @version 2023-10-18
 * @param {Number} n - a decimal number.
 * @returns {Array<Boolean>} - n as binary number represented by array of booleans.
 */
function getNumberAsBits(n) {

    var bits = [],
        b = (n).toString(2);

    for (var i = 0; i < b.length; i++)
        bits[i] = b[i] == '1';

    return bits

};

/**
 * Returns a binary coded decimal number (BCD)
 * representing binary bits.
 * 
 * Use case: in Indesign and Illustrator,
 * `characterAttributes.stylisticSets`
 * are stored as a BCD.
 * 
 * Example 1:
 *   - for Opentype stylistic sets 1 and 5:
 *   - `numbers` array = [0,4]
 *   - returns 17 (10001 in binary).
 * 
 * Example 2:
 *   - for opentype stylistic sets 3, 4 and 5:
 *   - `numbers` array = [2,3,4]
 *   - returns 28 (11100 in binary).
 * 
 * @author m1b
 * @version 2023-07-12
 * @param {Array<Number>} numbers - the stylistic sets indices, eg. [2,4,5].
 * @returns {Number} - the decimal number representing the numbers.
 */
function getBinaryCodedDecimalForArray(numbers) {

    var arr = numbers.slice().sort(),
        bin = [],
        len = arr[arr.length - 1],
        i = len + 1,
        val = arr.pop();

    while (i--) {

        if (val == i) {
            bin[len - i] = 1;
            val = arr.pop();
        }

        else {
            bin[len - i] = 0;
        }

    }

    // join into binary number and parse binary as decimal
    return parseInt(bin.join(''), 2);

};

 

Edit 2024-08-20: removed unused function.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Aug 19, 2024 Aug 19, 2024

Copy link to clipboard

Copied

Thank you for your detailed response, @m1b 

You said the following: "You could potentially analyse the text metrics after the change to see if that particular text changed and that would prove that the stylistic set did exist in the font". I've already thought about this approach, and I think that's what I'm going to do, since it would most likely work in my use case.

 

I also wanted to let you know that I definitely did not know about binary coded decimal. So far I've got my code to a certain point, but I'm sure this will help me polish it further. Thank you very much!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 19, 2024 Aug 19, 2024

Copy link to clipboard

Copied

That will be good if you still have a way forward. Yeah you can think of the stylistic sets number as 20 "switches" in binary, where 1 means turned on. So 10000000000000000010 means that sets 2 and 20 are turned on, and is I think 524,290 in decimal. Anyway good luck with your project!

- Mark

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Aug 20, 2024 Aug 20, 2024

Copy link to clipboard

Copied

Hey, @m1b . I found a bug in my existing logic, and I was wondering if you know how could I detect the actual height of the letterforms in a text object via scripting? The object.height is unreliable for my use case, since it picks up the text's leading spaces.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 20, 2024 Aug 20, 2024

Copy link to clipboard

Copied

Hi @Eduard Buta, I've written a function to do that. It's a bit involved! Read a discussion and get the script here.

- Mark

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Aug 20, 2024 Aug 20, 2024

Copy link to clipboard

Copied

LATEST

Thank you very much. This helped me get to the bottom of my problem. Much appreciated!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Aug 19, 2024 Aug 19, 2024

Copy link to clipboard

Copied

Two files in the Illustrator SDK look relevant to your interests, courtesy of a third-party repo that posted it (Adobe’s own developer page isn’t working in my Firefox):

 

https://github.com/WestonThayer/Bloks/blob/master/BloksAIPlugin/Vendor/illustratorapi/ate/ATESuites....

 

The CharFeaturesSuite has functions for querying and setting OT features on a range of text in a text frame. (Look for the phrase “OpenType feature”.) I expect JSX’s CharacterAttributes.stylisticSets uses that under the hood.

 

https://github.com/WestonThayer/Bloks/blob/master/BloksAIPlugin/Vendor/illustratorapi/illustrator/AI...

 

AIFontSuite’s functions provides a lot more font information  than JSX’s painfully limited TextFont class. (Look for “glyph set”.)

 

See if there’s anything there that whets your appetite. You’ll need a C/C++ developer to make use of it if you don’t know C yourself; there’s a few around these forums, typically under the SDK tag.

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Aug 19, 2024 Aug 19, 2024

Copy link to clipboard

Copied

Thank you for the resources, @hhas01 

 

Albeit, these might be a bit overkill for my simple need at the moment. I'm also pretty much struggling with ExtendScript as it is, so I doubt I'd be able to understand these. I managed to do what I wanted with what the API already offers and a bit of creative thinking on the side. Your answer is much appreciated either way!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines