InDesign style variable - variables

Is there a way to use variables in styles (script?) to share styles with some variations, like color?
Example: I am laying out a book, with multiple chapters. Each chapter is an InDesign document. I would like to use common styles for all the documents in the book, but they would vary in color. So instead of having multiple objects styles like: RoundedBox-red, RoundedBox-blue etc, I would only have one style, RoundedBox, and just input the value of the color variable somewhere…

Do you have a reason to examine only rectangles, ovals, and polygons? If not, you can use pageItems. Ditch the shape selection and the switch, and use:
shapes = myDoc.allPageItems;
for (var i=0; i<shapes.length; i++)
{
if (shapes[i].appliedObjectStyle.name === oldStyle.name)
{
shapes[i].applyObjectStyle( newStyle );
}
}
Since you have each chapter in a separate document, you could also just change the definition of your object style:
oldStyle.fillColor = newSwatch;
so you don't have to loop over the actual objects. Not tested but it should work.

Well, I found out. The hardest part was really to find a good documentation for the Indesign javascript scripting API... Adobe's documentation is either hard to find or lacking. In addition, they publish everything as PDFs, which IMHO is really annoying. I found a good online documentation for CS6. I am working on CC but everything I used seems the same. Anyhow, I have created the following script, very incomplete and not perfect, but works for me...
// Setup the dialog UI
var myDialog = app.dialogs.add({
name: "Style Variables",
canCancel: true
});
// I usually never use 'with', but this is how it is done
// in Adobe's documentation...
with(myDialog.dialogColumns.add()) {
staticTexts.add({staticLabel: "Main Color swatch name:"});
staticTexts.add({staticLabel: "Style to replace:"});
staticTexts.add({staticLabel: "Replace style with:"});
staticTexts.add({staticLabel: "Choose shape type to target:"});
}
with(myDialog.dialogColumns.add()){
var swatchField = textEditboxes.add({editContents:'', minWidth:180}),
oldStyleField = textEditboxes.add({editContents:'', minWidth:180}),
newStyleField = textEditboxes.add({editContents:'', minWidth:180}),
shapeTypeField = dropdowns.add({stringList:['Rectangles', 'Ovals', 'Polygons']}); // Defaults to rectangles
}
// Get the user input and do stuff with it
var myResult = myDialog.show();
if (myResult === true)
{
var docs = app.documents,
myDoc = docs[0],
allStyles = myDoc.objectStyles,
oldStyle = allStyles.itemByName(oldStyleField.editContents),
newStyle = allStyles.itemByName(newStyleField.editContents),
swatches = app.documents[0].swatches,
newSwatch = swatches.itemByName(swatchField.editContents),
shapes;
// Get the shape type we are targetting:
switch(shapeTypeField.selectedIndex)
{
case 0:
shapes = myDoc.rectanges;
break;
case 1:
shapes = myDoc.ovals;
break;
case 2:
shapes = myDoc.polygons;
break;
default:
shapes = myDoc.rectangles;
}
// Set the base style color to the user chosen swatch:
newStyle.fillColor = newSwatch;
for (var i=0; i<shapes.length; i++)
{
if (shapes[i].appliedObjectStyle.name === oldStyle.name)
{
shapes[i].applyObjectStyle( newStyle );
}
}
}
else
{
alert('Script cancelled, nothing was done.');
}
// Destroy dialog box
myDialog.destroy();

Related

Adobe Photoshop Scripting - How to Select Bounding Box Around Current Selection?

Does anyone know whether it's possible, in Photoshop extend script, to convert an irregular selection (e.g. magic wand tool selection) into a rectangular selection encompassing the top, left, bottom and right bounds of the selection?
Here it is, I have documented the code so you can modify it later if you need. Also, check page 166 and following of Photoshop's JS reference manual, you may read more about selections - you can set feather, extend/intersect/etc. the selection if you need to.
Made for CS6, should work with latter.
#target photoshop
if (documents.length == 0) {
alert("nothing opened");
} else {
// start
//setup
var file = app.activeDocument;
var selec = file.selection;
//run
var bnds = selec.bounds; // get the bounds of current selection
var // save the particular pixel values
xLeft = bnds[0],
yTop = bnds[1],
xRight = bnds[2],
yBottom = bnds[3];
var newRect = [ [xLeft,yTop], [xLeft,yBottom], [xRight,yBottom], [xRight,yTop] ]; // set coords for selection, counter-clockwise
selec.deselect;
selec.select(newRect);
// end
}

Illustrator variables - dynamically line up two text strings next to each other when autogenerating

I am automating the generation of several thousand labels in Adobe Illustrator. The use of the VariableImporter script has made easy work of it so far, but now I have reached an issue where I am stumped. The original plan worked great, until the powers that be requested that one line of text have a bold text string, followed by a normal weight text string. Before, when the font weights were the same I could have connected the two strings of text in the CSV file prior to loading them into the drawing, and they would have came out lying right next to each other. This is now no longer possible and I can't think of a solution that is not incredibly fussy.
I don't know illustrator very well, so I am thinking I could just be unaware of some setting that would stick an object next to another one even as the other one moves.
Okay here is the way I figured out how to do this with help from Adobe forums and from Vasily.
First of all, use InDesign if possible. It is better at performing a Data Merge and can do this without your scripting.
Write out <variable1> <variable2> which is formatted as needed on the same line of text.
You will need to have the variables that you are putting in there somewhere in the illustration. Recommended to put it in a hidden layer behind everything.
replace variable1 and variable2 with the names of your variables where the functions getVariableContents() are called in this script
var idoc = app.activeDocument;
var vars = idoc.variables;
var replace1 = /<variable1>/g;
var replace2 = /<variable2>/g;
// author CarlosCanto on adobe forums
function getVariableContents(variableName) {
var idoc = app.activeDocument;
var ivar = idoc.variables.getByName(variableName);
return ivar.pageItems[0].contents;
}
var replaceWith1 = getVariableContents('variable1'), result;
var replaceWith2 = getVariableContents('variable2'), result;
// regex_changeContentsOfWordOrString_RemainFormatting.jsx
// regards pixxxel schubser
function exchangeWords(s, replacer) {
var s = s;
var replacer = replacer;
var atfs = activeDocument.textFrames;
for (var i = atfs.length - 1; i >= 0; i--) {
atf = atfs[i];
while (result = s.exec(atf.contents)) {
try {
aCon = atf.characters[result.index];
aCon.length = result[0].length;
aCon.contents = aCon.contents.replace(s, replacer);
} catch (e) {};
}
}
}
exchangeWords(replace1,replaceWith1);
exchangeWords(replace2,replaceWith2);
run the script
There is a way to accomplish this by having a script do some processing during the course of your batch output, and an organizational system which adds some overhead to your file, in terms of adding more text boxes and possibly an extra layer to your document. But - here's what you can have: a hidden layer with all your variables there in separate single point-text objects, and a layer with your regular template objects such as any point text or area-text objects. Your art text objects will need to be re-worked to contain a string with multiple variable placeholders like this: "Hello, <FirstName> <LastName>". The placeholders can be styled, and a processing script would then need to replace the <placeholder> words with your real variable values. Where are the varible values? They are going to be populating into your hidden layer which has your separate text objects and the script would need to read the contents of each of those to put into the <placeholders>. ~~Those same text fields can be styled as you wish, and the script could apply the same styles to your text when it is replaced in the main text body.~~ -actually this won't be necessary of your routine backs up the original text frame with the placeholder in it, therefore preserving the styling, but it may be necessary if you are going to instead use an external text file to keep your original text in. And of course, it will need to make a backup of the original text with all the <placeholders> so that it will reset the text for every new dataset during your batch process.
However, this is much easier done in Indesign, can you not use ID for your task?
I modified script from #tucker-david-grebitus's answer. So now it gets all textual variables and replaces all their names edged by percent symbol
for (var i = activeDocument.variables.length - 1; i >= 0; i -= 1) {
var variable = activeDocument.variables[i];
if (variable.kind !== VariableKind.TEXTUAL || !variable.pageItems.length) {
continue;
}
var search = new RegExp('%' + variable.name + '%', 'g');
var value = variable.pageItems[0].contents;
for (var j = activeDocument.textFrames.length - 1; j >= 0; j -= 1) {
var textFrame = activeDocument.textFrames[j];
textFrame.contents = textFrame.contents.replace(search, value);
}
}

Indesign script: how to pasteboard items

I have this script that finds a paragraph style, puts an item from a library at the very end and applies object style:
myDoc = app.documents[0];
myLib = app.libraries[0];
myObjectStyle = myDoc.objectStyles.item ("marker");
app.findTextPreferences = app.changeTextPreferences = null;
app.findTextPreferences.appliedParagraphStyle = "Custom"
var myFound = app.activeDocument.findText(true);
alert (myFound.length);
try {
for (i = 0; i < myFound.length; i++) {
myIcon = myLib.assets.itemByName("winieta_tr").placeAsset (myFound[i].insertionPoints[-2])[0];
myIcon.appliedObjectStyle = myObjectStyle;
// myFound[i].remove ();
}
}
catch (e) {alert (e.message)}
I don't know how to alter it, so the items are obtained not from library but form pasteboard - any help would be appreciated.
Is it possible to find elements that are in the document by name, as it is with library elements?
Yes, you can find an object by name (you would assign that name in the layers panel) simply by using
myDoc.pageItems.itemByName("myItemName");
If you are looking for the same thing on a specific spread (for example if several items on several spreads have the same name), you can use
myDoc.spreads[0].pageItems.itemByName("myItemName");
Or if you just want to use the currently active spread
app.activeWindow.activeSpread.pageItems.itemByName("myItemName");
Just make sure not to use the page to address a page item on the pasteboard as the pasteboard does not belong to any page.
Is it possible to find elements that are in the document by name, as it is with library elements?
You can apply a script labels to the frame on the pasteboard to give it a name.

Using extendscript (javascript) how can I get the color values from the Photoshop color table

I'm writing a Photoshop script in extendscript/javascript and I'm trying to verify that the document is using just one color (plus transparency). What I would like to do is change the document mode to Indexed Color and then get the values in the color table.
I have successfully changed the document mode to Indexed Color but can't figure out how to access the color table or the color values inside of it.
My working alternative is to use a colorSampler to compare the values of each pixel, but that can take a couple of minutes to run on larger documents and speed is an issue for this project.
Please let me know if there is a way to access the color table or if you see a way to reduce the time it takes to run this function.
function sample_color(doc, sample_rate) {
var status = 'PASS'
var color_sampler = doc.colorSamplers.add([0,0])
var color_val = false //first (and hopefully only) color value in the document
var broke = false
for (x=1; x < doc.width; x+=sample_rate){
if (broke){
break
}
for (y=1; y < doc.height; y+=sample_rate){
color_sampler.move([UnitValue(x, 'px'), UnitValue(y, 'px')])
try{
var color = color_sampler.color //color of the current pixel
} catch(e) {
var color = false //color_sampler.color fails if the pixel is transparent
}
if (color != false){
if (color_val != false){
if (!color.isEqual(color_val)){
status = 'FAIL'
broke = true
break
}
} else {
color_val = color
}
}
}
}
color_sampler.remove()
return status
}
xbytor has written a couple of scripts for accessing colour tables. This link may be of use to you.

JSFL: selecting items returned by fl.findObjectInDocByType()

I can't seem to use the info returned by fl.findObjectInDocByType() with fl.getDocumentDOM().selection.
I want to use document.setTextRectangle to re-size some text fields from an array generated using fl.findObjectInDocByType().
I can easily access all the textObject properties but since document.setTextRectangle requires a current selection, I am at a loss.
The example in the documentaion for setting selection is:
fl.getDocumentDOM().selection = fl.getDocumentDOM().getTimeline().layers[0].frames[0].elements[0];
fl.findObjectInDocByType() returns an array of objects with the attributes: (object.timeline, object.layer, object.frame, object.parent)
But these are objects, and don't have a property for array index numbers required by fl.getDocumentDOM().selection=...
var doc = fl.getDocumentDOM();
var textFieldArray = fl.findObjectInDocByType("text", doc);
for (var i=0; i < textFieldArray.length; i ++){
fnResizeTheTextField(textFieldArray[i]);
}
function fnResizeTheTextField(theTextField){
//force current selection to be theTextField
//doc.selection MUST be an array, so assign theTextField to an array...
var selectArray = new Array();
selectArray[0] = theTextField.obj;
var theTimeline =theTextField.timeline;
var theLayer =theTextField.layer;
var theFrame =theTextField.frame;
doc.currentTimeline =theTextField.timeline;
doc.selection = doc.getTimeline().theLayer.theFrame.selectArray;//error
//resize the text rectangle
doc.setTextRectangle({left:0, top:0, right:1000, bottom:1000});
}
}
Result: Error:doc.getTimeline().theLayer has no properties
It turns out, the ObjectFindAndSelect.jsfl script already contains a function specifically for this: fl.selectElement(). Much more elegant:
var doc = fl.getDocumentDOM();
// generate an array of elements of type "text"
var textFieldArray = fl.findObjectInDocByType("text", doc);
for (var i=0; i < textFieldArray.length; i ++){
fnResizeTheTextField(textFieldArray[i]);
}
function fnResizeTheTextField(theTextField){
//force current selection to be theTextField
fl.selectElement(theTextField,false);//enter 'edit mode' =false...
//resize the text rectangle
doc.setTextRectangle({left:0, top:0, right:1000, bottom:1000});
}
}
I found the answer. In order to select anything for a document level operation, you have to also make flash focus on the keyframe of that object.
so, if I loop through an array of objects created by fl.findObjectInDocByType(), I use this code to make flash focus on the object correctly:
function fnMakeFlashLookAt(theObject){
doc.currentTimeline =theObject.timeline;
doc.getTimeline().currentLayer =theObject.layer;
doc.getTimeline().currentFrame =theObject.frame;
}
this may not work on objects nested inside a symbol however.
I had a similar issue recently, and apparently all google results about setTextRectangle() direct us here. It's unbelievable how poorly documented jsfl is :)
If you need to use setTextRectangle() inside an library item that is not on stage, you need to open for edit the item first.
Here's the code that solved my problem:
library.selectItem(libraryItemName);
doc.selection = [tf];//where tf is the reference to textfield we need to edit
doc.library.editItem(libraryItemName);
doc.setTextRectangle({left:l, top:t, right:r, bottom:b});
doc.selectNone();
If you have a better working solution, please post. I hope it saves somebody's time. Good luck!