Copy link to clipboard
Copied
Hi,
I have multiple text layers in the document. I would like to filter through the contents of each text layer with regex and change the color of the found result.
In the example below I would like to find all the hastags in the text layer and change the color to blue.
Example:
This is a really long piece of text copy and #hashtag more copy here #anotherhastag #hashtagNo3
I've written a few scripts for indesign that involve GREP(regex) and it's fairly simple to change the color of the found results. I can't seem to find a solution for this in photoshop.
Thanks
Copy link to clipboard
Copied
I recommend abandoning the idea to do this in Photoshop, Indesign is oriented towards sophisticated text editing, so better to do it there.
In Photoshop changing parts of text within a Type Layer necessitates using AM code that can become quite unwieldy in my experience.
Copy link to clipboard
Copied
Thanks for the quick response.Unfortunately, I have to use photoshop for this job. So I'm stuck with photoshop. By "AM code" do you mean action manager? Any pointers in that direction will be very helpful.
Unfortunately, I can only use photoshop for this job. So I'm stuck with photoshop. By "AM code" do you mean action manager? Any pointers in that direction will be very helpful.
Thanks
Copy link to clipboard
Copied
Yes, I meant Action Manager code.
Try recording changing the color of one word in a larger text in a Type Layer with ScriptingListener.plugin and see how well manageable the resulting code seems to you.
Copy link to clipboard
Copied
I forgot to add: If there is any possibility that some parts within the type layer vary at all with regard to type properties (size, leading, font etc.) do include some such varying elements in your test.
Copy link to clipboard
Copied
Will give that a shot. Thank you.
One thing I am stumbling upon in my tests is finding a word with regex and affecting just those results. Any tips on how I would go about that? For example, in a regex search like this "foundText" ends up as just plain text. I can't seem to affect the properties of that "foundText". Like color size etc. Is this the right approach? Is there a better way?
var foundText = textLayer.match(new RegExp("#hashtag", "i"))
Copy link to clipboard
Copied
The foundText (the result of the match) is a String, it is not part of the actual Type Layer.
Copy link to clipboard
Copied
Here is an example changing text when the text layer is selected. Changing colour of a section of text has been done in the past by xbytor
and he might chip in, but I would not attempt it!
if(activeDocument.activeLayer.kind == LayerKind.TEXT){
var textContent = activeDocument.activeLayer.textItem.contents;
activeDocument.activeLayer.textItem.contents = textContent.replace(/mine/ig,"his");
}
Copy link to clipboard
Copied
Somebody mention me?
SourceForge is acting up so you will need to download xtools:
The files of interest are xtools/xlib/Text.jsx and xtools/xlib/TextTest.jsx. It can be done but it requires a bit of work.
Here is the relevant code from TextTest.jsx that shows you how it's done.
var layer = doc.layers[layerName];
doc.activeLayer = layer;
var opts = new TextOptions(layer.textItem);
opts.contents = "123567875688\radsfasfasdfasdfasdf\n(&#^&^#$";// construct the styles
var s1 = new TextStyle("ArialMT");
var s2 = new TextStyle("Courier-Bold", 30, Text.toRGBColor(0, 255, 0));// this style will use the default font...
var s3 = new TextStyle(undefined, 42, Text.toRGBColor(255, 0, 0));// superscript test
var s0 = new TextStyle(s1);
s0.baseline = PSString.superScript;// Now create the set of ranges to apply the styles over
var ranges = new TextStyleRanges();
ranges.add(new TextStyleRange(0, 1, s0));
ranges.add(new TextStyleRange(1, opts.contents.length, s1));
ranges.add(new TextStyleRange(5, 10, s2));
ranges.add(new TextStyleRange(7, 15, s3));
ranges.add(new TextStyleRange(25, undefined, s2));
opts.ranges = ranges;
Text.modifyTextLayer(doc, opts, doc.layers[layerName]);
As somebody said before, InDesign is a more appropriate tool.
Copy link to clipboard
Copied
Wow. Thanks for sharing these scripts. They are incredibly helpful. Very much appreciated.
Copy link to clipboard
Copied
Question: What is the best way for me to get the text ranges from the text layer with a regex search. My current solution returns a string which isn't very helpful. Is regex the right approach or is there a better way?
Example:
Below are the contents of the text layer and I want to find all the hashtags.
This is a really long piece of text copy and #hashtag more copy here #anotherhastag #hashtagNo3
var foundText = textLayer.match(new RegExp("\S*#(?:\[[^\]]+\]|\S+)", "i"))
Copy link to clipboard
Copied
I would use something like "IndexOf" but this will be more than 1 line of code.
Copy link to clipboard
Copied
Something like...
var s = activeDocument.activeLayer.textItem.contents;
var re = /(?:^|\W)#(\w+)(?!\w)/g, match, matches = [];
while (match = re.exec(s)) {
matches.push([[match[0].replace(/^\s+|\s+$/g,'')],[match.index+1],[match[0].replace(/^\s+|\s+$/g,'').length]]);
}
for(var a in matches){
$.writeln("Word found = " + matches[0] + " start = " + matches[1] + " length = " +matches[2]);
}
Copy link to clipboard
Copied
Thank you!
Copy link to clipboard
Copied
Hi xbytor2
Thank you for sharing all those scripts. I've been looking through the code and they've been a huge learning resource.
I tried to implement the TextTest.jsx script into my own version but I've been running into an issue.
When I run the below script I get this error: "Error: General Photoshop error occurred. This functionality may not be available in this version of Photoshop."
Is it something to do with this line in the script?
Text.modifyTextLayer(doc, opts, doc.layers[layerName]);
Here's the whole script.
//
// HashtagColor_v1.jsx
//
//============================================================================
//@includepath "~/Documents/xtools"
//
//@include "xlib/PSConstants.js"
//@include "xlib/stdlib.js"
//@include "xlib/Text.jsx"
//@include "xlib/Action.js"
//@include "xlib/xml/atn2xml.jsx"
//
function colorHashtag(doc) {
var layerName = "Test Layer";
var layer = doc.layers[layerName];
doc.activeLayer = layer;
var opts = new TextOptions(layer.textItem);
opts.contents = layer.textItem.contents;
//opts.contents = "123567875688\radsfasfasdfasdfasdf\n(&#^&^#$";
// construct the styles
var s1 = new TextStyle("ArialMT");
var s2 = new TextStyle("Courier-Bold", 30, Text.toRGBColor(0, 255, 0));
// this style will use the default font...
var s3 = new TextStyle(undefined, 42, Text.toRGBColor(255, 0, 0));
// superscript test
var s0 = new TextStyle(s1);
s0.baseline = PSString.superScript;
// Now create the set of ranges to apply the styles over
var ranges = new TextStyleRanges();
ranges.add(new TextStyleRange(0, 1, s0));
ranges.add(new TextStyleRange(1, opts.contents.length, s1));
ranges.add(new TextStyleRange(5, 10, s2));
ranges.add(new TextStyleRange(7, 15, s3));
ranges.add(new TextStyleRange(25, undefined, s2));
opts.ranges = ranges;
Text.modifyTextLayer(doc, opts, doc.layers[layerName]);
}
Text.test = function(doc) {
var doc;
if (app.documents.length) {
doc = app.activeDocument
}
colorHashtag(doc);
}
Text.test();
"HashtagColor_v1.jsx";
Copy link to clipboard
Copied
Upon further trial and error, I noticed that the "modifyTextLayer" function runs without issues on a text layer created by the script, but has issues with existing text layers created directly in the photoshop document.
So in your TextTest.jsx file, the newTextLayerDemo function creates a text layer. The rest of the script works when working with that text later. But in my version of the script, I am trying to work with an existing text layer in photoshop (ideally, the currently selected text layer), and I get that error.
Any thoughts or how to solve this?
Copy link to clipboard
Copied
So do you want to do something like Font Remaping script here: Magic scripts for Photoshop but with searched text and colors instead font family?
I agree with others. This can be very difficult.
You need to:
1) read whole text layer action descriptor
2) find all your strings and position where they start and where is end of string
3) if text layer has multiple styles, there is list of these styles and index where they start and where they end. So you need to read this descriptor, make copy and change color and position indexes and then put this descriptors into list and this list replace in original layer descritor
4) finaly you can use "set descriptor" action manager code and change the layer.
Like this:
Before:
After:
Copy link to clipboard
Copied
Wow. This is very helpful. Thank you. I'm going to give it a crack and see where I end up.
Clearly, this looks like a "long walk for a ham sandwich". But it'll be worth the learning experience. I guess??
I'll hit you up with questions as I go along.
Jarda, what app/plugin did you use in your images? It looks incredibly helpful. Where can i get that?
Thanks again, c.pfaffenbichler, SuperMerlinand Jarda Bereza.
Copy link to clipboard
Copied
It's my own tool. Not public yet, but maybe it could be public soon.
It's based on this code: GitHub - JavierAroche/descriptor-info: JSX module to recursively get all the properties in an Action...
And this code:
So this should help you. You also can explore my script with align to baseline and text columns. I am getting here descriptor, changing it and then setting back. Magic scripts for Photoshop
Copy link to clipboard
Copied
Great. Thanks much,
Those are some great scripts. Congrats.
Copy link to clipboard
Copied
I have a TODO line to implement low level text manipulation in my extension. But, as my collegues suggested, the AM structure is a bedrock layer under the swamp guarded by hydras. I don't have enough patience and passion and not smart enough to get to the lowest layers.
I wish Adobe would start the whole PS source code from scratch again and implement it a way which is allowing us to properly script the app.
Thanks xbytor2 for the .jsx code, ill try it!
Copy link to clipboard
Copied
I will tell you secret.
You can:
0) read layer actionDescriptor
1) use native function for converting actionDescriptor into JSON
2) use eval() on JSON string and create javascript object
3) modify regular JS object with well known object opertions. You need to do same thing as in my screenshot.
4) convert JS object into JSON string with ".toSource()" method
5) convert JSON string into ActionDescriptor with native function
6) and finally you can put modified descriptor into "executeAction"
I think I will create small library for this workflow.
Copy link to clipboard
Copied
Thanks Jarda. This is way above my punching weight, but I'm going to give it a shot. I have managed to get up to step 4. But I am struggling with the step 5 and 6. At the moment I'm keeping it pretty basic: change the color of the entire text to red. Once I get the core functionality working I'll jump into figuring out how to change text ranges to a different color.
Bear in mind that I've written the script referencing different sources across the forms. So it's probably not the cleanest and most efficient way to do it.
Any help on how I can get steps 5 and 6 working would be great.
Thanks
#include "./helpers/JSON.jsx"
// 0) read layer actionDescriptor
var ref = new ActionReference();
ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
var desc = executeActionGet(ref)
var convertDesc = new ActionDescriptor();
convertDesc.putObject(stringIDToTypeID("object"), stringIDToTypeID("object"), desc);
var jsonDesc = executeAction(stringIDToTypeID("convertJSONdescriptor"), convertDesc, DialogModes.NO);
// 1) use native function for converting actionDescriptor into JSON
jsonDesc.getString(stringIDToTypeID("json"));
// 2) use eval() on JSON string and create javascript object
var obj = JSON.parse(jsonDesc.getString(stringIDToTypeID("json")));
// 3) modify regular JS object with well known object opertions. You need to do same thing as in my screenshot.
obj["textKey"]["textStyleRange"][0]["textStyle"]["baseParentStyle"]["color"]["red"] = 255;
// 4) convert JS object into JSON string with ".toSource()" method
var objToStr = JSON.stringify(obj);
alert(objToStr);
// 5) convert JSON string into ActionDescriptor with native function
// 6) and finally you can put modified descriptor into "executeAction"
Copy link to clipboard
Copied
Here is first version of my tool:
Works good for me. You probably will need CC 2015.5 and higher
Keep in mind that pure AM will be faster something about 10-30ms per conversion into JS object and back.
Copy link to clipboard
Copied
Wow! Thank you. I'll play with it now.
Much appreciated.