Copy link to clipboard
Copied
Hello,
I'm looking for ways to increase the utility of data merge on indesign, and the project I've come up with is I'd like to set a different background color on each page of a merged document (lets say around 200 pages, all unique colors, kind of like a swatch booklet).
I have a spreadsheet and each row has a color column (hex or rgb, whatever works best).
I've been having chatgpt help me write some code, which it seems to have given a decent starting point, but of course comes up with errors. Those don't scare me, but I am having the hardest time finding any documentation to give insight on how to come up with a solution.
Couple of questions
I'll post the chatgpt code for reference, but it doesn't work (error 55: object does not support the property or method 'dataMergeRecords'
// ChangeBackgroundColor.jsx
// Main function
function changeBackgroundColors() {
var doc = app.activeDocument;
var dataMerge = doc.dataMergeProperties;
var dataMergeRecords = dataMerge.dataMergeRecords;
// Ensure the data merge is enabled
if (!dataMerge.dataMergePreferences.dataSource) {
alert("No data merge file found. Please import a data merge file first.");
return;
}
// Loop through each data merge record
for (var i = 0; i < dataMergeRecords.length; i++) {
var record = dataMergeRecords[i];
// Get the value of the background color field from the data merge
var colorValue = record.getField("BackgroundColor");
// Convert color value to RGB/CMYK
var color = convertToColor(colorValue);
// Create a new rectangle for the background
var page = doc.pages[i];
var backgroundRect = page.rectangles.add({
geometricBounds: [0, 0, doc.documentPreferences.pageHeight, doc.documentPreferences.pageWidth],
fillColor: color
});
// Send the rectangle to the back
backgroundRect.sendToBack();
}
}
// Function to convert color value to RGB/CMYK Color
function convertToColor(value) {
// Assuming value is in hex format for this example
var color = app.activeDocument.colors.itemByName(value);
if (!color.isValid) {
// Create new color if it doesn't exist
color = app.activeDocument.colors.add();
color.name = value;
color.space = ColorSpace.RGB;
color.colorValue = hexToRgb(value);
}
return color;
}
// Function to convert hex color to RGB array
function hexToRgb(hex) {
var bigint = parseInt(hex.replace('#', ''), 16);
var r = (bigint >> 16) & 255;
var g = (bigint >> 8) & 255;
var b = bigint & 255;
return [r, g, b];
}
// Run the main function
changeBackgroundColors();
Hi @dougw50331988, I do this all the time... like this example:
Goal: I want to do a data merge, but I want to assign a different master page depending on the "BRAND" field in the data csv file.
1) I make a field in the data file csv that corresponds to the master page name, if it doesn't already exist.
2) I make a text frame in the Indesign template document. I usually put it in the slug area, off the page, but slightly overlapping—the text frame must overlap the page or I think datamerge
...Copy link to clipboard
Copied
No you cannot run a script while a file is being merged. For what you're trying to accomplish, you'd want to do it after the merge is complete. You'll need to store the color elements, then iterate thru each page and apply.
Copy link to clipboard
Copied
You have this error - because DataMerge doesn't give access to the linked DataSource.
https://www.indesignjs.de/extendscriptAPI/indesign-latest/#DataMerge.html
You can select DataSource as a file - but that's it.
If you want to have access to the contents of each record and field - you need to "open" it yourself.
And that's another example why ChatGPT is useless - most of the time.
Copy link to clipboard
Copied
Hi @dougw50331988, I do this all the time... like this example:
Goal: I want to do a data merge, but I want to assign a different master page depending on the "BRAND" field in the data csv file.
1) I make a field in the data file csv that corresponds to the master page name, if it doesn't already exist.
2) I make a text frame in the Indesign template document. I usually put it in the slug area, off the page, but slightly overlapping—the text frame must overlap the page or I think datamerge won't populate it. There are other ways to do this step but this is a simple approach. I assign a script label to this text frame, eg. "BRAND" so the script can read it later.
3) I do the data merge and now have a multi-page document, say 1000 pages. At the top of each page is the BRAND text frame (with the brand name or ID in it from the data csv file).
4) I run a script that assigns whatever master page matches the name in the BRAND text frame.
See script example below. I use this to (a) set the master page to match the brand, and (b) swap certain paragraph styles to match the brand. This isn't specific to your needs, but should help you understand.
- Mark
/**
* Set Paragraph Styles and Master Pages.js
* @author m1b
* @version 2024-03-15
*
* Script to run after a datamerge
* to apply master pages and paragraph styles
* based on a "BASE BRAND" variable on each page.
*
* - the BASE BRAND variable is found in a text frame on each page.
* - for each page, apply a master page named for that page's BASE BRAND.
* - every paragraph of each page is checked, and if a paragraph style
* name matches the `brandMatcher` RegExp, then change the paragraph
* style to the style matching the page's BASE BRAND.
*
* If a master page or a paragraph style named for the BASE BRAND doesn't
* exist, then the change won't be made. This can be intentional.
*/
function main() {
var settings = {
brandFrameName: 'BASE BRAND',
brandMatcher: /^[^-]+/, // matches the prefix before a hyphen, eg. "myBrand-Body"
};
var doc = app.activeDocument,
counter = 0;
for (var i = 0, page, paras, len = doc.pages.length; i < len; i++) {
page = doc.pages[i];
paras = page.textFrames.everyItem().paragraphs.everyItem().getElements();
var brandFrame = page.textFrames.itemByName(settings.brandFrameName);
if (!brandFrame.isValid)
continue;
var newMaster = getThing(doc.masterSpreads, 'baseName', brandFrame.contents);
if (newMaster && page.appliedMaster !== newMaster)
page.appliedMaster = getThing(doc.masterSpreads, 'baseName', brandFrame.contents);
doSomethingToThings(paras,
function changeParagraphStyle(paragraph) {
if (!paragraph.isValid)
return;
var style = paragraph.appliedParagraphStyle,
match = style.name && style.name.match(settings.brandMatcher);
if (!match)
return;
var newStyle = getThing(doc.allParagraphStyles, 'name', style.name.replace(settings.brandMatcher, brandFrame.contents));
if (!newStyle || newStyle === style)
return;
paragraph.appliedParagraphStyle = newStyle;
counter++;
}
);
}
alert(counter);
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Set Paragraph Styles');
/**
* Performs function `doThisTo` on each element of `things`.
* @param {Array<*>} things - array of things.
* @param {Function} doThisTo - the function to execute, given a thing.
*/
function doSomethingToThings(things, doThisTo) {
for (var i = things.length - 1; i >= 0; i--)
doThisTo(things[i]);
};
/**
* Returns a thing with matching property.
* @param {Array|collection} things - the things to look through, eg. PageItems.
* @param {String} key - the property name, eg. 'name'.
* @param {*} value - the value to match.
* @returns {*} - the thing.
*/
function getThing(things, key, value) {
for (var i = 0; i < things.length; i++)
if (things[i][key] == value)
return things[i];
};
Copy link to clipboard
Copied
For the sake of closing this thread up, this was my final code for my needs. This structure of looping through each page, grabbing the named text frame and altering it in some way based on the script is a powerful tool to have in the arsenal. Thanks again
function main() {
var doc = app.activeDocument,
counter = 0;
for (var i = 0, page, paras, len = doc.pages.length; i < len; i++) {
page = doc.pages[i];
var valueFrame = page.textFrames.itemByName("value-title");
if (doc.colors.item(valueFrame.contents) == null) {
var colorFrame = page.textFrames.itemByName("background-color");
var color = colorFrame.contents;
var colorSum = 0;
var colorArr;
// Color formatted as "rgb(255, 255, 255)"
if (color !== null) {
colorArr = color.substring(4, color.length - 1).split(", ");
for (var j = 0; j < colorArr.length; j++) {
colorArr[j] = parseInt(colorArr[j]);
colorSum += colorArr[j];
}
doc.colors.add({name:valueFrame.contents, space:ColorSpace.RGB, colorValue:colorArr});
}
colorFrame.fillColor = doc.colors.item(valueFrame.contents);
colorFrame.contents = "";
if (colorSum <= 450 || colorArr[1] <= 180) {
for (var k = 0; k < valueFrame.paragraphs.length; k++) {
if (k == 0) {
valueFrame.paragraphs[k].applyParagraphStyle(doc.paragraphStyles.itemByName("h1-reverse"));
} else {
valueFrame.paragraphs[k].applyParagraphStyle(doc.paragraphStyles.itemByName("p-reverse"));
}
}
}
}
}
};
Copy link to clipboard
Copied
There are some major issues with this ChatGpt code.
1. The error that you get. Because that property does not exist. The property that does exist is dataMergeFields
2. The code here is based on the premise that the InDesign DOM does provide us with the data of each record in the CSV. Which sadly is not the case. The dataMergeFields property just gives information about the fields i.e. the header row. Apart from that you get things like no. of records etc. So this logic won't work.
You have two options either to read the CSV via your code and do whatever you like for each record. Or for your current use case the approach given by @m1b would work fine.
-Manan
Copy link to clipboard
Copied
I have several articles over at CreativePro.com about changing colours during a data merge that might assist you:
1) change QR colours during a data merge: https://creativepro.com/creating-colored-qr-codes-during-a-data-merge/
2) Basic change of colours during a data merge: https://creativepro.com/changing-colors-during-a-data-merge/
3) Apply any CMYK combination during a data merge: https://creativepro.com/applying-any-cmyk-color-during-a-data-merge/
4) Colorising greyscale images during a data merge: https://creativepro.com/colorizing-variable-grayscale-images-during-data-merge/
5) trying out all colour combinations in a logo design using data merge: https://creativepro.com/exploring-color-combinations-with-data-merge/
Copy link to clipboard
Copied
Can I just say how impressively helpful this community is. Thank you for all the advice and solutions! It was helpful to know that there isn't currently a way to alter documents as they're merged as mentioned by @brian_p_dts, so whatever I do needs to be done to the merged document.
I think @m1b's solution will work for my needs, and will read up on @colcol2's blog posts to see if they'll work as well. I wish they didn't feel so hacky (hiding a text box so the value gets passed is pretty janky, but whatever works).
It has been close to 10 years since I've used InDesign and datamerge in any really significant capacity. It's a little dissapointing to see that the feature has been hardly touched or expanded by Adobe in that time. It could be an absolute powerhouse if a little more attention was given to it.
Copy link to clipboard
Copied
Can I just say how impressively helpful this community is. Thank you for all the advice and solutions! It was helpful to know that there isn't currently a way to alter documents as they're merged. [...]
By @dougw50331988
Built-in DataMerge is quite simple / limited.
There are solutions that can offer much greater functionality - including building document(s) from scratch any way you want - but they are not free.