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

Scripting: Setting text with characterRange causing unexpected justification change

Enthusiast ,
May 05, 2024 May 05, 2024

Copy link to clipboard

Copied

If a text layer contains multiple justifications on different layers, editing text using character range can cause justification to incorrectly change.

 

Steps to reproduce:

1. Open justifyText.aep and have Comp 1 open and active.
2. File > Run Script....run justifyText script.

 

Script output:
"top line justify = 7413 next line was = 7414
changing text to = *1234
*
top line justify now = 7414 next line now = 7414"


While the (0,5) character range covers a single justification setting across the top line of text,
changing the text (still 4 characters plus return) causes it to adopt the justification of the next line.


3. Undo the text change or Revert Project, comment out line 10 of script and try again with lines 11 then 12

Script output:
"top line justify = 7413 next line was = 7414
changing text to = *234
*
top line justify now = 7413 next line now = 7413"


Result: This time, the change seems to cause the second line to adopt the first line's justification.

 

Expected Result: Justification would remain essentially unchanged throughout.

 

Workaround: Keep track of the justification for the line return and next character then revert either if they change during the text edit. Lines 27-37 in script.


Apart from Kerning seemingly being broken currently, this is the only other oddity I've found with characterRange so far. Generally it's working very well.

I could see that if AE only allows one justification per line, something that might seem to affect the line return could cause an accidental re-evaluation of the justification across the two lines in question?


System info
    Application: After Effects (Beta) v24.5.0.27
    OS: macOS v12.6.8, RAM: 64.00 GB GB, CPUs (logical): 12

Bug Unresolved
TOPICS
Bug

Views

205

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
4 Comments
Adobe Employee ,
May 05, 2024 May 05, 2024

Copy link to clipboard

Copied

@Paul Tuersley

 

When you replace a whole paragraph of text, which is what you are doing with your experiment, under the covers the paragraph is deleted first. Then the next text is inserted...at the beginning of the (what was the next) paragraph and it thus takes on the attributes of that paragraph.

 

Text is not an attribute, so any changes to it will change the structure of the attributes which applied to it.

 

There is no mechanism at this point for simply replacingTextPreserveAttributes. Imagine that you had applied red to the beginning of the first line, and blue to the end of the first line - then set the text. You would lose the mixed state. This is the same thing which is happening to your test case but just on a paragraph level.

 

If you are trying to replace text in existing paragraphs (do not call them lines) then you must be careful to NOT include the CR in the replacement. This may lead to odd looking code but there is no getting around it.

 

// get the range of the first paragraph
var CR = td.paragraphRange(0).characterRange();
// assume it is not empty - adjust the range to not include the final CR
var CR_without_final_cr = td.characterRange(CR.characterStart, CR.characterEnd - 1);
// replace the text - this will smash all the character attributes, but will
// preserve the paragraph ones (like justification)
CR_without_final_cr = "replace existing text but saved the paragraph attributes";

 

Note that my replacement text does NOT include a CR at the end - when I saw your "1234\n" I knew your scheme was in trouble.

 

The one exception to this is the last paragraph of the text layer - it is the only CharacterRange which you can get from a ParagraphRange which does NOT include the final CR.

 

I am interested in hearing about requests to make pure text editing easier and less error prone - I imagine purely just trying to replace text and preserve attributes is rather popular with template workflows.

 

Now, what's this about Kerning? News to me.

 

Douglas Waterfall

After Effects Engineering

Votes

Translate

Translate

Report

Report
Enthusiast ,
May 05, 2024 May 05, 2024

Copy link to clipboard

Copied

@Douglas_Waterfall  Thanks for the explanation. I will play with this some more.

 

My systems for selectively replacing text (or other attributes) are a bit complex but generally work well, either starting from a (0,-1) range and checking for undefined across a wide range of attributes, then checking the same in character pairs, ultimately coming up with a set of style ranges for matching attribute groups. That lets me expand / contract the text in each 'style group' when editing as desired.

The other option is to loop through one character at a time. This has the benefit of not needing the exhaustive search for matching atrributes, but means the relationship between text and styles is somewhat lost as the text changes shuffle under the styles which stay locked to the character index.

 

It's really only this one case where the line return affects the justification that has even come up. I've been hoping to avoid getting into Paragraph Range....at least until I start getting a bunch of complaints about text getting screwed up. Now I'm thinking maybe the simplest code might be as you say to just leave out the line return from the text range being edited if it's replacing like by like. Just shrink the CR by one for the text editing part only while still applying other styles to the original range so it doesn't introduce an additional split in the style grouping.

 

Here's the broken kerning thing:

https://community.adobe.com/t5/after-effects-beta-bugs/scripting-kerning-doesn-t-appear-to-work-prop...

 

 

 

Votes

Translate

Translate

Report

Report
Enthusiast ,
May 06, 2024 May 06, 2024

Copy link to clipboard

Copied

@Douglas_Waterfall Based on your advice, I think I now have a better solution for the case of replacing text ending in a line return:

 

if (CR.text.charAt(CR.text.length-1) == "\r" && newText.charAt(newText.length-1) == "\r") {

 CR2 = textDoc.characterRange(CR.characterStart, CR.characterEnd-1);

 CR2.text = newText.substring(0, newText.length -1);

}

(I generally make sure all line returns are converted to \r if necessary)

 

On the subject of ParagraphRange, my avoiding using it so far, and you telling me not to says 'lines' I thought I should fix my lack of understanding about what 'paragraph' refers to, as "Description: The ParagraphRange object is an accessor to a paragraph range" was not helpful in that.

 

So paragraph means 'each block of text ending with a line return, or the last block of text'?

 

And the reason you said not to say lines is because of Paragraph Text where a paragraph can wrap across multiple 'lines'? And a paragraph can be a line that just consists of a line return, perhaps that is separating two blocks of text that in the real world would be considered to be two paragraphs, not three.

 

It was only after I noticed the paragraphCount textDoc atribute that it started to make sense how to even approach using paragraphRange(). I was quite puzzled about it until then.

 

I only mention this if it's helpful in seeing how someone interprets the docs. The ParagraphRange object docs are quite lengthy already, but perhaps just missing that paragraph definition and mention of pargraphCount that I needed before it dives into all the characterRange stuff.

 

Something I've run into a fair bit in the scripting docs, for text attributes in particular, under any given 'obscureFeature' attribute you generally get told:

Description: The layer's 'obscureFeature' atrribute.

and then I've got to look around the UI trying to find what that feature might be or what it might be called if referenced in the conventional help docs.

Some examples:

- I am thankful that TextDocument.baselineDirection mentions Tate-Chi-Yoko as I'd never have made that leap!

- I have no idea why TextDocument.digitSet offers far more than just the Hindi Digits which I see in the UI.

- I have no idea what TextDocument.everyLineComposer does at all.

 

Votes

Translate

Translate

Report

Report
Adobe Employee ,
May 06, 2024 May 06, 2024

Copy link to clipboard

Copied

LATEST

@Paul Tuersley 

 

The everyLineComposer attribute is exposed in the Paragraph Panel dropdown as Adobe Single-Line/Every-Line Composer.

 

digitSet has to support all the digit sets which are possible values of the attribute, even ones which do not appear in After Effects UI. AI/Ps expose more of these than AE does.

 

I get precise about Lines vs Paragraphs because loose thinking get scripters in trouble - not all paragraphs compose as one line. This is one of the reasons that the new object ComposedLine is named the way it is - it represents composed state, which is different from model state.

 

When you refer to a Paragraph or CharacterRange it is independent of the composed state. The same Paragraph could compose as a single line, multiple lines, or even be entirely overset (if it did not fit in the box). None of that would affect how you could talk to it with the ParagraphRange object.

 

The After Effects user sees a lots and lots of single line Paragraphs and so the temptation is to just talk about lines - which is fine as it goes until one makes the assumption that lines are always Paragraphs. Scripters cannot make those assumptions and have to be prepared for anything - inclding whether you allow or strip string inputs which include a final CR.

 

Douglas Waterfall

After Effects Engineering

Votes

Translate

Translate

Report

Report
Resources