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

How to break apart in illustrator?

Explorer ,
Feb 04, 2022 Feb 04, 2022

Copy link to clipboard

Copied

Hi community, is there a way to break apart text in Illustrator like Corel does, if anyone is familiar?

I saw this post since 2009. I do try like the post says, select the text and then: Flatten transparancy, but not all the text breaks up to seperate it indvidually. 

 

 

https://community.adobe.com/t5/illustrator-discussions/break-apart-in-illustrator/m-p/1201629#M672

TOPICS
How to

Views

568

Likes

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

correct answers 2 Correct answers

Community Expert , Feb 05, 2022 Feb 05, 2022

Hi Stefan, I think you already have answers to this, but for others who come to see this question in the future, I've written a splitting-text-into-characters script that has some extra capabilities. I took Carlos' script for inspiration and made the following modifications:

1. It handles area type.

2. It handles multi-line text.

3. It handles left, center, right justification.

4. It handles kerning, baseline shift, different fonts and point sizes.

5. It can split textframes into lines, words or

...

Likes

Translate

Translate
Community Expert , Feb 08, 2022 Feb 08, 2022

Here is the action, Mark:

 

Char Splitter

 

The file contains an action set file (char_splitter_1.aia) and a sample Illustrator file (char_splitter_sample.ai) with ten different type objects for test purposes.

 

Instruction:

 

- Unzip the file and open char_splitter_sample.ai

- In the Actions palette import char_splitter_1.aia

- Select one or a couple of type objects

- Run the action

 

You may also check the type objects with your script. The script will sometimes make funny things.

 

Likes

Translate

Translate
Community Expert ,
Feb 04, 2022 Feb 04, 2022

Copy link to clipboard

Copied

There's no built-in method, but did you try Carlos's scripts, linked in that topic?

 

https://community.adobe.com/t5/illustrator-discussions/how-to-divide-all-textframes-in-one-character...

Likes

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
Explorer ,
Feb 04, 2022 Feb 04, 2022

Copy link to clipboard

Copied

I tried using his script method, but get this error:

 

StefanCargoski_0-1644028252789.png

 

Likes

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 ,
Feb 05, 2022 Feb 05, 2022

Copy link to clipboard

Copied

Hi Stefan, I think you already have answers to this, but for others who come to see this question in the future, I've written a splitting-text-into-characters script that has some extra capabilities. I took Carlos' script for inspiration and made the following modifications:

1. It handles area type.

2. It handles multi-line text.

3. It handles left, center, right justification.

4. It handles kerning, baseline shift, different fonts and point sizes.

5. It can split textframes into lines, words or characters.

 

EDIT: Updated code with some improvements, including a warning if you've selected lots of text (thanks Kurt!) also I found an obscure bug in converted overset area text frames that continue to advertise the original overset text even though it's no longer present—I had to explicitly remove overset text before converting.

 

Edit: another update, I improved some things, added a UI, worked around Illustrator bug with parsing of words when punctuation is adjacent, added "Discard punctuation" option, and "Monospace" option for character spacing (this is for no reason—just because I can).

 

Edit: just cleaned up structure a bit.

 

Update: I've had a look at @Kurt Gold's char_splitter action posted in this thread and it's *really* good for splitting text into characters. Which to use char_splitter action or splitTextFrame script? Have a look at this table:

 

char_splitter_1 action

splitTextFrame script

Handle point text       

Handle rectangular area text with one cell

Handle area text with columns or rows

Handle path text

Handle text inside non-rectangular frame

Break text into Lines

Break text into Words

Break Text into Characters

Monospace characters

 

 

- Mark

 

 

 

/*
Split TextFrames.js
For Adobe Illustrator

by m1b
here: https://community.adobe.com/t5/illustrator-discussions/script-error/m-p/12730864/thread-id/308920

• Inspired by CarlosCanto's splitSelectedWordsIntoCharacters.jsx
• Can split textframe into lines, words or characters.
• The `Discard punctuation` option discards non-alphanumeric characters
• The `Monospace` option spaces each character by a specified number of points.

*/


var SplitType = {
    LINES: 0,
    WORDS: 1,
    CHARACTERS: 2
};


// global defaults
var settings = {

    // split into lines, words or characters
    splitType: SplitType.CHARACTERS,

    // when splitting into words, ignore non alpha-numeric characters
    discardPunctuation: false,

    // when splitting into characters,
    // space each character by n points
    // 0 = do not space
    monoSpace: false,
    monoSpacePts: 10,

    // show UI, or just go with settings we have here
    showUI: true

};


// Wrd class just gave me a little help to break content into words
Wrd = function (contents, start, end, type) {
    this.contents = contents;
    this.start = start;
    this.end = end;
    this.type = type;
}
Wrd.type = {
    NONWORD: 0,
    WORD: 1,
}
Wrd.prototype.toString = function () {
    return '['
        + this.contents + ' '
        + this.start + '-' + this.end + ' '
        + ['NONWORD', 'WORD'][this.type]
        + ']';
}


main();

function main() {

    var doc = app.activeDocument,
        items = doc.selection;

    if (items.length < 1) {
        alert('Please select one or more text frames and try again.');
        return;
    }

    settings.items = items;
    settings.action = splitTextFrames;
    settings.getActionParams = function () { return [settings.items, settings.splitType] };

    // show the UI
    if (settings.showUI) {
        invokeUI();

    } else {
        settings.action.apply(null, settings.getActionParams());

    }

}


function splitTextFrames(items, splitType) {

    // split into lines, words or characters
    var splitFunction;
    if (splitType === SplitType.WORDS) {
        splitFunction = splitTextFrameLineIntoWords;
    } else if (splitType === SplitType.CHARACTERS || splitType == undefined) {
        splitFunction = splitTextFrameLineIntoCharacters;
    }

    // always work with array
    if (!items.hasOwnProperty('0')) items = [items];

    // check if large number of splits
    var numberOfSplits = 0,
        splitThis;
    if (splitType === SplitType.LINES) {
        splitThis = 'lines';
    } else if (splitType === SplitType.WORDS) {
        splitThis = 'words';
    } else if (splitType === SplitType.CHARACTERS) {
        splitThis = 'characters';
    }
    for (var i = items.length - 1; i >= 0; i--)
        numberOfSplits += items[i][splitThis].length;

    // warn user if large number of splits
    if (numberOfSplits > 999)
        if (!(confirm('Are you sure you wish to make ' + numberOfSplits + ' splits? It may take a long time and can\'t be cancelled once started.')))
            return;

    // loop over items
    for (var i = items.length - 1; i >= 0; i--) {
        var item = items[i];

        // ignore everything except textFrames
        if (item.constructor.name != 'TextFrame') continue;

        // sorry can't handle text on path
        if (item.kind == TextType.PATHTEXT) continue;

        // change to point text, not area text
        var item = convertAreaTextToPointText(item);

        // split into lines
        var itemLines = splitTextFrameIntoLines(item);

        // stop here if we just want lines
        if (splitFunction == undefined) continue;

        // split each line further
        for (var j = itemLines.length - 1; j >= 0; j--) {
            splitFunction(itemLines[j], settings.discardPunctuation, settings.monoSpace && settings.monoSpacePts);
        }
    }
}


function splitTextFrameIntoLines(item) {
    var y = item.position[1],
        itemLines = [];

    removeAutoLeading(item);

    // loop over lines
    for (i = item.lines.length - 1; i >= 0; i--) {
        var h = item.height;

        // ignore empty lines
        if (item.lines[i].contents.length == 0) continue;

        var _line = item.duplicate();
        // remove other lines
        for (var j = _line.lines.length - 1; j >= 0; j--) {
            if (j != i) _line.lines[j].remove();
        }

        // remove a line for height calculation
        item.lines[item.lines.length - 1].remove();

        // remove \u0003 or height will be wrong
        while (
            item.lines.length > 0
            && item.characters[item.characters.length - 1].contents == '\u0003'
        )
            item.characters[item.characters.length - 1].remove();

        // remove empty lines at start
        while (_line.lines[0].contents.length == 0)
            _line.characters[0].remove();

        // remove \u0003 from end of line
        while (_line.characters[_line.characters.length - 1].contents == '\u0003')
            _line.characters[_line.characters.length - 1].remove();

        // position
        _line.top = y - h + _line.height;

        itemLines.push(_line);
    }

    // remove the original item
    item.remove();

    return itemLines;
}


function splitTextFrameLineIntoWords(item, discardPunctuation) {
    var x = item.position[0],
        itemWords = [],
        wordCharacters = /[A-Za-zÀ-ÖØ-öø-ÿ0-9-]+/g,
        wordAndPunctuationCharacters = /[^\s]+/g,
        matcher = discardPunctuation ? wordCharacters : wordAndPunctuationCharacters,
        matched,
        wrds = [],
        lastStart = 0;

    // match words in item.contents
    // (using Wrd objects to store start, end and type)
    matcher.lastIndex = 0;
    while ((matched = matcher.exec(item.contents)) !== null) {
        if (lastStart + matcher.lastIndex - matched[0].length != lastStart) {
            // add non-word at start
            wrds.push(new Wrd(
                /* contents */ item.contents.substr(lastStart, matcher.lastIndex - matched[0].length - lastStart),
                /* start    */ lastStart,
                /* end      */ matcher.lastIndex - matched[0].length - 1,
                /* type     */ Wrd.type.NONWORD
            ));
        }

        // add word
        wrds.push(new Wrd(
            /* contents */ matched[0],
            /* start    */ matcher.lastIndex - matched[0].length,
            /* end      */ matcher.lastIndex - 1,
            /* type     */ Wrd.type.WORD
        ));
        lastStart = matcher.lastIndex;
    }

    // add non-word at end
    if (matcher.lastIndex > 0 && matcher.lastIndex <= item.contents.length - 1) {
        wrds.push(new Wrd(
        /* contents */ item.contents.substr(matcher.lastIndex, item.contents.length - matcher.lastIndex),
        /* start    */ matcher.lastIndex,
        /* end      */ item.contents.length - 1,
        /* type     */ Wrd.type.NONWORD
        ));
    }

    // loop over wrds
    var wrd;
    while (wrd = wrds.pop()) {
        var w = item.width;

        if (wrd.type == Wrd.type.WORD) {

            // duplicate textFrame
            var dup = item.duplicate();

            // remove characters before wrd
            for (var i = wrd.start - 1; i >= 0; i--)
                dup.characters[i].remove();

            // position
            dup.left = x + w - dup.width;

        }

        // trim wrd from item
        for (var i = wrd.end; i >= wrd.start; i--)
            item.characters[i].remove();

        itemWords.push(dup);
    }

    // remove the original item
    item.remove();

    return itemWords;
}


function splitTextFrameLineIntoCharacters(item, discardPunctuation, monoSpace) {

    var x = item.position[0],
        wordCharacters = /[A-Za-zÀ-ÖØ-öø-ÿ0-9-]+/g,
        wordAndPunctuationCharacters = /[^\s]+/g,
        matcher = discardPunctuation ? wordCharacters : wordAndPunctuationCharacters,
        itemCharacters = [];

    // loop over characters
    for (i = item.characters.length - 1; i >= 0; i--) {

        var w = item.width;

        // ignore spaces and linefeeds
        if (matcher.test(item.characters[i].contents)) {

            // duplicate textFrame
            var _character = item.duplicate();
            // remove all other chars
            for (var j = _character.characters.length - 1; j >= 0; j--) {
                if (j != i) _character.characters[j].remove();
            }

            // position
            if (monoSpace != false) {
                _character.left = x + (monoSpace * i);
            } else {
                _character.left = x + w - _character.width;
            }
        }

        // remove character from original item
        item.characters[i].remove();

        itemCharacters.push(_character);
    }

    // remove the original item
    item.remove();

    return itemCharacters;
}


function discardOversetText(item) {
    var lastVisibleChar = item.lines[item.lines.length - 1].characters[item.lines[item.lines.length - 1].characters.length - 1],
        lastOffset = lastVisibleChar.characterOffset;

    // remove overset characters
    for (var i = item.characters.length - 1; i >= lastOffset; i--)
        item.characters[i].remove();
}


function convertAreaTextToPointText(item) {
    if (item.kind == TextType.AREATEXT) {
        var temp = item.duplicate();

        // oversetText causes errors later
        discardOversetText(temp);

        // convert
        temp.convertAreaObjectToPointObject();

        // get fresh reference to converted textFrame
        for (var i = 1; i < item.layer.textFrames.length; i++) {
            if (item.layer.textFrames[i].uuid == item.uuid) {
                var convertedItem = item.layer.textFrames[i - 1];
                item.remove();
                item = convertedItem;
                break;
            }
        }

        // remove unnecessary spaces before \u0003
        for (var i = item.characters.length - 1; i >= 0; i--) {
            if (item.characters[i].contents == '\u0003') {
                if (i > 1 && item.characters[i - 1].contents == ' ')
                    item.characters[i - 1].remove();
            }
        }
        if (item.characters[item.characters.length - 1].contents == ' ')
            item.characters[item.characters.length - 1].remove();
    }

    return item;
}


function removeAutoLeading(item) {
    var items = [];
    if (item.hasOwnProperty('paragraphs')) {
        items = item.paragraphs;
    } else if (item.hasOwnProperty('lines')) {
        items = item.lines;
    } else if (item.hasOwnProperty('characters')) {
        items = item.characters;
    }
    for (var i = 0; i < items.length; i++) {
        if (items[i].contents.length == 0) continue;
        var hasAutoLeading = items[i].characterAttributes.autoLeading;
        if (hasAutoLeading) {
            items[i].characterAttributes.leading = items[i].paragraphAttributes.autoLeadingAmount / 100 * items[i].characterAttributes.size;
            items[i].characterAttributes.autoLeading = false;
        }
    }
}


// just UI from here

function invokeUI(p) {
    var dialog = makeUI(p)
    dialog.center();
    var result = dialog.show();
    dialog = null;

    if (result === -1) {
        // cancelled
        return;

    } else if (result === 1) {
        // do action
        settings.action.apply(null, settings.getActionParams());
    }

    function makeUI(p) {
        var menuItems = ['Lines', 'Words', 'Characters'],
            w = new Window("dialog", 'Split TextFrame'),
            menuRow = w.add("Group {orientation:'row', alignment:['fill','top']}"),
            discardPunctuationGroup = w.add("Group {orientation:'column', alignment:['fill','top'], margins:[0,7,0,0] }"),
            stack = w.add("Group {orientation:'stack', alignment:['fill','fill']}"),
            buttonRow = w.add("Group {orientation:'row', alignment:['right','top']}"),
            mainMenu = menuRow.add("Dropdownlist {alignment:['left','center']}"),
            monoSpaceGroup = stack.add("Group {orientation:'row', alignment:['fill','top']}"),
            monoSpaceCheckbox = monoSpaceGroup.add("Group {orientation:'column', alignment:['fill','top'], margins:[0,7,0,0] }")
                .add("Checkbox { alignment:['left','fill'], text:'Monospace', value:false, margins:[0,7,0,0] }"),
            monoSpaceEditText = monoSpaceGroup.add("edittext", undefined, "0"),
            discardPunctuationCheckbox = discardPunctuationGroup.add("Checkbox { alignment:['left','fill'], text:'Discard punctuation', value:false }"),
            cancelButton = buttonRow.add('button', undefined, 'Cancel', { name: 'cancel' }),
            okButton = buttonRow.add('button', undefined, 'Split', { name: 'ok' });

        monoSpaceGroup.add("statictext", undefined, "pts");
        for (var i = 0; i < menuItems.length; i++)
            mainMenu.add('item', menuItems[i]);

        discardPunctuationCheckbox.value = settings.discardPunctuation;
        monoSpaceCheckbox.value = settings.monoSpace;
        monoSpaceEditText.text = settings.monoSpacePts;
        monoSpaceEditText.characters = 3;
        mainMenu.preferredSize.width = 180;
        mainMenu.title = 'Split into';
        mainMenu.selection = settings.splitType;
        updateUI();

        okButton.onClick = splitButtonClicked;
        cancelButton.onClick = cancelButtonClicked;
        mainMenu.onChange = updateUI;
        monoSpaceCheckbox.onClick = updateUI;


        return w;

        function splitButtonClicked() {
            settings.splitType = mainMenu.selection.index;
            settings.discardPunctuation = discardPunctuationCheckbox.value;
            settings.monoSpace = monoSpaceCheckbox.value;
            settings.monoSpacePts = Number(monoSpaceEditText.text);
            w.close(1);
        };

        function cancelButtonClicked() {
            w.close(-1);
        }

        function updateUI() {
            var s = mainMenu.selection ? mainMenu.selection.index : 0;
            monoSpaceGroup.visible = (s == SplitType.CHARACTERS);
            monoSpaceEditText.enabled = monoSpaceCheckbox.value
            discardPunctuationGroup.visible = (s != SplitType.LINES);
        }
    }
}

 

 

 

 

 

 

 

Likes

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 ,
Feb 05, 2022 Feb 05, 2022

Copy link to clipboard

Copied

Works fine, thanks!

Likes

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 ,
Feb 05, 2022 Feb 05, 2022

Copy link to clipboard

Copied

That's a very good contribution, Mark.

 

One thing you may consider is to include an alert that informs about a possible very long execution time in case the type objects contain a lot of text.

 

Just did a test (yes, a rather cruel one) with a pretty large area type object and had to force quit Illustrator because I'm sure it will take half a decade to complete the script's activity.

 

Just a well-meant idea.

 

Likes

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 ,
Feb 05, 2022 Feb 05, 2022

Copy link to clipboard

Copied

Haha, thanks Kurt! Once again you have pushed one of my scripts to its (very modest) limits. 🙂

 

I think soon I will post a question to the forum about performance improvements and efficiently processing large numbers of objects. It is one of my weakest areas.

 

For now your suggestion is sensible. I will add it.

- Mark

Likes

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
Explorer ,
Feb 05, 2022 Feb 05, 2022

Copy link to clipboard

Copied

Thanks so much for this! 

 

Kind regards.

Likes

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 ,
Feb 06, 2022 Feb 06, 2022

Copy link to clipboard

Copied

Your modified version of the script is a very good improvement, Mark. Thanks for sharing it.

 

As for the performance issues, I would not care too much. I don't know many reasons why one wants to split really large type objects into single characters.

 

By the way, the exercise can also be done with a dirty action. Unlike the script at its current state the action would properly support multiple column/row textframes, rotated type objects and type on a path objects. But it may as well be slow with a lot of text (probably a bit slower than your excellent script).

 

Likes

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 ,
Feb 08, 2022 Feb 08, 2022

Copy link to clipboard

Copied

I've given it another update—now has a rudimentary UI. As always I'm fascinated with your "dirty actions" haha. My brain just doesn't seem to work that way. I'd like to see something like that, or at least an idea of the technique behind it.

- Mark

Likes

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 ,
Feb 08, 2022 Feb 08, 2022

Copy link to clipboard

Copied

Thanks for the modified version of your script, Mark.

 

I think in line 415 there is a little lapse:

 

okButton = buttonRow.add('button', undefined, buttonTitle, { name: 'ok' });

 

Probably you will have to change it to something like this:

 

okButton = buttonRow.add('button', undefined, 'OK', { name: 'ok' });

 

Otherwise the script won't run.

 

As for the dirty action (yes, it is really dirty, but works in almost all cases): I will post it this evening or tomorrow.

 

Likes

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 ,
Feb 08, 2022 Feb 08, 2022

Copy link to clipboard

Copied

Thanks Kurt. Bug fixed. No hurry of course for the action, but I'm curious. 🙂

- Mark

Likes

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 ,
Feb 08, 2022 Feb 08, 2022

Copy link to clipboard

Copied

Here is the action, Mark:

 

Char Splitter

 

The file contains an action set file (char_splitter_1.aia) and a sample Illustrator file (char_splitter_sample.ai) with ten different type objects for test purposes.

 

Instruction:

 

- Unzip the file and open char_splitter_sample.ai

- In the Actions palette import char_splitter_1.aia

- Select one or a couple of type objects

- Run the action

 

You may also check the type objects with your script. The script will sometimes make funny things.

 

Likes

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 ,
Feb 08, 2022 Feb 08, 2022

Copy link to clipboard

Copied

I tried your action @Kurt Gold  and it's amazing—some magic in there! You are right that your action splits all different types of text frames whereas my script just handles absolute vanilla, one-cell, rectangular textframes and point text. char_splitter_1 is definitely the winner! 🙂

 

By the way, I'm embarrassed to say that I didn't know there was a rotate field in the Text Panel! I assume it's been there for decades. Haha.

- Mark

Likes

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 ,
Feb 08, 2022 Feb 08, 2022

Copy link to clipboard

Copied

Very smart Kurt!

Likes

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 ,
Feb 08, 2022 Feb 08, 2022

Copy link to clipboard

Copied

Thanks for testing, Mark.

 

I think it's not really important to declare a winner. My approach is just another one of my bumpy actions. Your script is excellent and I'm pretty sure that it could be improved, such that it supports all kinds of type objects.

 

On the other hand, I also think that Illustrator already has a sophisticated tool to handle type objects as if they were split. It's the Touch Type tool. In many cases it can do things without actually splitting the type objects into separate characters.

 

Well, but of course it's always fun to find some ways to really split things.

 

Likes

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 ,
Feb 08, 2022 Feb 08, 2022

Copy link to clipboard

Copied

LATEST

Haha, I've never used the touch type tool before either! It's fun! I've so much to learn.

 

As far as winners and losers go, yeah it's not important, but I like to end up with the best solution to a problem people have, even if it isn't mine 🙂 I'm doing this for fun mostly, but I also like to build things people can use in the future, as I suspect you do too.

 

Your stress-testing always helps make a better script, and I'm always blown-away by your dirty magic. Haha. sorry to call it that, but it's so funny.

- Mark

Likes

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