Copy link to clipboard
Copied
Hi everyone,
With After Effects (Beta) 24.2.0 build 17, we have added some very significant and long-awaited additions to the TextDocument DOM:
You can read the updated docs here: https://ae-scripting.docsforadobe.dev/introduction/changelog.html.
The CharacterRange and ParagraphRange will benefit from a short explanation.
The former is returned by the characterRange() function where the parameters are character indexes, and the latter by the paragraphRange() function where the parameters are paragraph indexes. With them, you have all the same familiar character and paragraph properties that are available on the TextDocument, but now they apply to the effective character index range which was used to construct the specific range object.
For example:
var td = ....layer(1).property(“Source Text”).value;
td.text; => “Hello, World!”
var cr = td.characterRange(0,5);
cr.text; => “Hello”
cr.text = “After Effects”; // replace “Hello” with “After Effects”
cr.text; => “After”
td.text; => “After Effects, World!”
Though these new objects share the same character and paragraph property definitions, there are still a few differences of note.
We have built a demo script exercising the new CharacterRange and ParagraphRange hooks to get you started. You can get it from our Github repository: https://github.com/AdobeDocs/after-effects/blob/main/samples/PerCharacter_StyleEdit_ScriptUISample.j...
Remember, these scripting hooks are currently in Beta and are subject to change.
We can't wait to see what you'll be building with these new hooks.
Douglas, John and Sebastien
Hi all,
Thank you so much for your feedback on these new per-character scripting APIs. We are happy to announce that these are available for use in the release version of After Effects 24.3. The documentation on https://ae-scripting.docsforadobe.dev/ has also been updated to reflect the wider release of the APIs.
Thanks again,
- Douglas, John,and Sebastien
Copy link to clipboard
Copied
@Douglas_Waterfall Do you have any thoughts on how best to identify mixed styles in an existing text layer? An obvious example would be doing find/replace on a certain font or color inside a mix style text layer.
It looks like I can characterRange the whole text then say, see if font comes back undefined, then continue to drill down ever more fine grained (or characterRange each pair of characters, or just compare every single character). But of course there are any number of font attributes that can be different between each character.
Ideally it would be something like returning and array of character ranges that encompassed every difference, or we could choose between all attributes or just certain ones. I realise that is something we can potentially code, even if it's fiddly and laborious.
It appears that any time you set a character range, it will push the style settings for the first character onto all characters in that range. Not entirely surprising although I hoped it would just touch the altered attributes. That suggests the safest alternative would be to set each character one at a time, but that seems non-ideal.
Copy link to clipboard
Copied
Hi Paul.
After Effects does not have styles (or exposes them) so there is no exposed concept of "difference".
Thus attributes just are without a claim to one value being more important than another.
If you are looking for mixed attributes then the only way to do it is indeed to walk each character and find out what the attribute is and see if it does not match what you expected.
The only style we have is conceptually default which is what you get when you call resetChar/ParaStyle in the UI or in scripting. Having an API which would return overrides relative to this style has seemed useful to me, and it is on my list to explore. However, I do not think this would help you with figuring out different overrides on a span of text.
Not sure what you mean by this:
It appears that any time you set a character range, it will push the style settings for the first character onto all characters
Unless you've discovered a bug, setting an attribute via a CharacterRange immediately applies only that attribute to the range of character spanned by the CharacterRange. Likewise if you read the attribute, we access that same range and return the value or mixed, as appropriate.
Sounds like you are seeing something different, or have a different mental model of what is going on. If you the former - I want to know about it immediately.
Perhaps you mean what happens when you apply text via a CharacterRange? That is implemented internally as a delete/insert operation so after the delete only one set of attributes remains and those then are extended as the text is inserted. Think about what happens when you interact with a TextLayer by hand - it is the same conceptual model.
Are you perhaps trying to replace text character for character and preserve the original styling?
Douglas Waterfall
After Effects Engineering
Copy link to clipboard
Copied
When I talk about styles I basically just mean all the various different text attributes. I realise there aren't any style groups as such. Yes I'm talking about making only certain changes while preserving all other styling.
And yes I meant apply text via the CharacterRange. I had two characters with different colors, used CharacterRange to change the text for those two characters and the second character got the first character's color. It makes sense when you describe it as an insert/delete.
Manual editing text does let you select a character range and then only affect the attribute being changed in the Character Panel, so I hoped perhaps it would be more like that. So I was just checking if there might be some fast track to know if or where those differences existed. But that's fine. I was already beginning to suspect character by character was the only way and it is at least possible now.
I guess I could grab a CharacterRange for the whole text and check every single exposed text attribute (I'm not sure if everything has been exposed yet, but it's probably close enough), then if any come back as undefined I will know to create a CharacterRange for each character one at a time to apply whatever specific attribute changes are required. Maybe because I'm just editing the CharacterRange / TextDocument rather than repeatedly applying it to the SourceText prop it won't be too slow.
Just doing a search/replace on say font or color that preserves all other styles is a lot easier for me to wrap my head around than what to do when changing the text characters in an existing mixed style text layer. I guess at that point the user really needs to be selecting ranges and editing manually in the comp so the intent is clear.
I could preserve the styles but any change in character length would not result in those styles shuffling accordingly. Unless I did come up with a solution for splitting then into groups of matching attributes then present them for editing in that way.
Thanks as always.
Paul
Copy link to clipboard
Copied
@Paul Tuersley wondering if you are wishing for this API
CharacterRange.replaceText(String text)
Here we would replace the character without disturbing the styling, and if the new string is longer than the current range then the last attribute is just applied to the extra.
Perhaps you might share more information on the transformation/edit you are trying to achieve.
Douglas Waterfall
After Effects Engineering
Copy link to clipboard
Copied
@Douglas_Waterfall Saw your response after posting my reply to your first response. CharacterRange.replaceText(String text) does sound like something that could be very useful.
My general focus on this is for my pt_TextEdit script. It gives people a list of all text layers in a project, and the ability to edit both the text and style attributes all from that one panel, along with search/replace for fonts and text across all keyframes. So it's basically about giving users as much control as possible for whatever they might want to do.
The main problem has always been that it destroys any mixed styles. So project-wide search/replace fonts and text without destroying styles is one part of that and it sounds like that replaceText thing could certainly help on the text replace side (font replace will already work even if it means going one character at a time).
But then rather than the single EditText box that lets the user edit a single text layer's contents (updating live in the project) I'm thinking of how to present the text for editing when it contains mixed styles. So rather than a single EditText it could be a separate EditText box for each 'style block'.
And it seems I can brute force that. Compare every attribute on every character then define a new range each time there's a difference. My initial hunch is the replaceText thing won't be necessary in this case, as I'd just be doing an insert/delete for each CharacterRange block (from last to first I imagine) but for the search/replace text side of things it could save me having to do these extra steps. I've not got far enough yet to find out how long it'll take a script to compare every attribute of every character on every keyframe of every text layer in a project!
Briefly on the allfonts stuff, pt_TextEdit always just presented a list of previously found fonts. Now I can tap into the font list that will also dramatically improve and I've made good progress with fontID, a new font list manager (filters for styles, character sets, saved groups). Now it just needs access to read/write favorites so it can benefit people wanting to manage the Character Panel font list too!
Thanks!
Copy link to clipboard
Copied
@Douglas_Waterfall Just following up now I've experimented more. It was good to know I wasn't missing anything before I got fully into it.
I have found a decent way to detect mixed style attributes in existing text. Start with checking all attributes on a full character range, then check each character pair on any flagged attributes, to create an array of 'style block' ranges. Presenting this as a series of edittext boxes with onChanging live comp updates while preserving those style blocks all seems to work well.
For search/replace I'm doing it one character at a time if the whole range comes back as mixed. On last loop either extend the CR range, or add more characters into the last CR range depending on if search or replace string was longer. Trying to preserve style intent when replacing mixed single/multi-byte characters would add more complexity but I imagine I could get there eventually.
So I don't really need that CR.replaceString() API. I guess it might be nice to have, but it seems quite solvable with what we already have. Thanks!
Copy link to clipboard
Copied
Hi, im not sure how much experience you have with after effects scripting but whenever I try to use the new characterRange function it is always undefined. i have even copy and pasted the code from both youtube and adobe developers but I always get an error at the Characterrange section of the code. My after effects is the newest version and i use Extend Script debugger. Do you have any ideas what could be going worng
Copy link to clipboard
Copied
Is it the beta version? Otherwise you won't be able to use the CharacterRange
Copy link to clipboard
Copied
ohhhhh thats so helpful, ive been trying to figure this out for 5 hours lol. thank you so much!
Copy link to clipboard
Copied
No worries! 😄
And if you want a function to test if it's available:
function isCharacterRangeWorking(){
var test = new TextDocument("docText");
// Check if the TextDocument object has the characterRange method
if (test.hasOwnProperty("characterRange")) {
// The method is available
return true;
} else {
// The method is not available
return false;
}
}
Copy link to clipboard
Copied
I acually wrote this function where I turn the first character in a text layer blue, is it possible to use the setValueAtTime function to turn the firts character blue 10 frames after it starts being displayed. this is my current code:
Copy link to clipboard
Copied
all my current attemps give me an error
Copy link to clipboard
Copied
Can you grab what you need from here (sorry in a hurry)?
var ts = layer.sourceText; // you need the layer
var td = ts.value;
var myCh = td.characterRange(1);
myCh.fillColor = RGBcolor; // you need the RGBcolor
if (ts.isTimeVarying) {
var atTime = app.project.activeItem.time;
ts.setValueAtTime(atTime, td);
}else{
ts.setValue(td);
}
Copy link to clipboard
Copied
Thank you for your response. I implimented it and for some reason
Copy link to clipboard
Copied
isTimeVarying was to check if the property has keyframes.... but in your case you can remove that part. Not needed.
If I put this in a jsx file it seems to work fine:
var comp = app.project.activeItem;
// Create text layer
var textLayer = comp.layers.addText("Bounce Text");
// Customizing text properties
var textProp = textLayer.property("Source Text");
var textDocument = textProp.value;
var firstCharRange = textDocument.characterRange(0);
textDocument.resetCharStyle();
textDocument.fontSize = 50;
textDocument.fillColor = [1, 1, 1];
textDocument.strokeColor = [0, 0, 0];
textDocument.strokeWidth = 2;
textDocument.strokeOverFill = true;
textDocument.applyStroke = true;
textDocument.applyFill = true;
textDocument.text = "Let's try this";
textDocument.justification = ParagraphJustification.CENTER_JUSTIFY;
textDocument.tracking = 50;
// Change the color of the first character to blue
firstCharRange.fillColor = [0, 0, 1]; // Blue color
textProp.setValue(textDocument);
Copy link to clipboard
Copied
Thank you for this response, but my goal is the set the first character to blue 1 second after the text layer begins displaying. I need this logic to create a color animation on the text.
Copy link to clipboard
Copied
Ha ok, so after setting the tracking with
textDocument.tracking = 50;
you need this:
textProp.setValue(textDocument);
textProp.addKey(0); // CREATE A KEYFRAME FIRST
firstCharRange.fillColor = [0, 0, 1]; // Blue color
// THEN USE SETVALUEATTIME TO CREATE ANOTHER KEY WITH THE BLUE CHARACTER
textProp.setValueAtTime(1,textDocument);
Copy link to clipboard
Copied
It Worked! Thank you so much!! I can finally finish my project!! You saved me hours of tedius documentation reading lol, I really apreciate this!
Copy link to clipboard
Copied
No worries, sorry if I didn't get you wanted from the beginning.
Copy link to clipboard
Copied
Its all good, you soved the two things I was completly stuck on lol. I don't think I would have figured this out on my own. Thank you again!
Copy link to clipboard
Copied
Thank you again for your help! I now have an animation to where I can change the font color of each word. I was also wondering if you know how to add a background highlight box behind each word also, I sent a youtube video that has the captioning style I'm talking about in this reply
Copy link to clipboard
Copied
this is my code so far, I'm wondering if it is possible to add the highlighted box behind each word as well:
Copy link to clipboard
Copied
No sorry bro, you're going a bit too far now. And I don't even know how to get the width of a single word inside a text layer...
Copy link to clipboard
Copied
its all good, do you think the functions textDocument.boxTextPos, and textDocument.boxTextSize would make this possible? Also to you know how to make a box cause i have no idea lol. If your not sure I'll test it out when I get a chance, and I can send you the solution if your interested.
Copy link to clipboard
Copied
I would make a rectangle underneath the text and change it's position and dimension according to the text.
Not sure about those scripting hooks sorry... They are new to me.
This scripts does it so you just need to find out how
https://madebyloop.co.uk/after-effects-scripts/highlight-underline-after-effects-script/#:~:text=Her...
Some clues here: https://creativecow.net/forums/thread/text-highlight-expression-selector/