Pages From CSV
Script for Adobe InDesign
Create a document of multiple pages each a different size. The number of pages possible is limited only by InDesign.
- Choose ruler units for the document and pages
- Option to add page ID
- Adapt open source to customize or create other scripts
How-to Video
NOTE: after video production, features have been added in response to user feedback: option to add page ID; recognize CSV columns for margins, page ID, page columns, and page column gutter. See instructions below for details.
Before using the script
Using the script requires some preparation. Create a spreadsheet with three columns these precise names: Prefix, Width, and Height. Use any spreadsheet application, for example, Excel.
The script creates pages the sizes listed for width and height. The prefix column is the section prefix applied. Note that InDesign imposes a limit on section prefixes of 8 characters. This includes the trailing hyphen that separates the prefix from the page number. If the value for prefix in the data row exceeds 7 characters, the value is truncated.
There are four optional columns: Margin, Page ID, Columns, and Gutter.
Margin — the page margins. If a single number, all four margins are set the same. For margins that vary, enter four numbers each separated by a comma. The order is left, top, right, and bottom. If the optional column is absent, or no value is present, or a value is faulty, the page margins are zero.
Page ID — identifying text for each page. If the optional column is absent and the the option to include Page ID is enabled, the column Prefix is used for Page ID.
Columns — the page is set to this number of columns. If omitted the page is a single column.
Gutter — the page is set to this column gutter. If omitted the column gutter is set to zero.
The measurement units for Width, Height, Margin, and Gutter are selected in the script interface.
Once the spreadsheet is ready, save it as Comma Separated Values (CSV).
How to use the script
The interface has two sections: Data file and Options. Select the data file, ruler units, and whether to include Page ID. Click the OK button to begin.
Section 1: Data File (CSV)
Delimiter — the character that separates columns of the data file. The default is comma, normal in the United States. Some European countries use semicolon rather than comma. Select the delimiter used in your region of the world.
File — select the CSV data file.
Section 2: Options
Ruler units — in the drop-down list, choose the ruler units for the values in the CSV columns Width, Height, Margin, and Gutter. Choices are Points, Picas, Inches, Millimeters, Centimeters, and Pixels. The document created is set to the selected units, and the size of pages added use the same units.
Add page ID — include text to identify each page. If the optional column Page ID is absent, the column Prefix is used for Page ID.
Top/Bottom — identifying text may appear at the top, above the page, or the bottom, below the page.
Source code
(download button below)
/*
Pages From CSV
Copyright 2024 William Campbell
All Rights Reserved
https://www.marspremedia.com/contact
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
(function () {
var title = "Pages From CSV";
if (!/indesign/i.test(app.name)) {
alert("Script for InDesign", title, false);
return;
}
// Script variables.
var columnIndex;
var columnNames;
var dataArray;
var doneMessage;
var error;
var measurementUnits;
var measurementUnitsEnum;
// Reusable UI variables.
var g; // group
var p; // panel
var w; // window
// Permanent UI variables.
var btnCancel;
var btnFileData;
var btnOk;
var cbPageId;
var grpPageId;
var listUnits;
var rbComma;
var rbPageIdTop;
var rbSemicolon;
var txtFileData;
// LANGUAGE EXTENSIONS
if (!Array.prototype.indexCi) {
Array.prototype.indexCi = function (x) {
var xLc = x.toLowerCase();
for (var i = 0; i < this.length; i++) {
if (this[i].toLowerCase() == xLc) {
return i;
}
}
return -1;
};
}
// SETUP
columnIndex = [
// 0 Prefix
// 1 Width
// 2 Height
// 3 Margin (optional)
// 4 Page ID (optional)
// 5 Columns (optional)
// 6 Gutter (optional)
];
columnNames = [
"Prefix", // 0
"Width", // 1
"Height", // 2
"Margin", // 3 (optional)
"Page ID", // 4 (optional)
"Columns", // 5 (optional)
"Gutter" // 6 (optional)
];
dataArray = [];
measurementUnits = [
"Points",
"Picas",
"Inches",
"Millimeters",
"Centimeters",
"Pixels"
];
measurementUnitsEnum = [
2054188905,
2054187363,
2053729891,
2053991795,
2053336435,
2054187384
];
// CREATE USER INTERFACE
w = new Window("dialog", title);
w.alignChildren = "fill";
// Panel 'Data file'
p = w.add("panel", undefined, "Data file (CSV)");
p.alignChildren = "left";
p.margins = [12, 18, 12, 12];
g = p.add("group");
g.margins = [0, 0, 0, 4];
g.add("statictext", undefined, "Delimiter:");
g = g.add("group");
g.margins = [0, 4, 0, 0];
rbComma = g.add("radiobutton", undefined, "Comma");
rbComma.value = true;
rbSemicolon = g.add("radiobutton", undefined, "Semicolon");
g = p.add("group");
btnFileData = g.add("button", undefined, "File...");
txtFileData = g.add("statictext", undefined, "", {
truncate: "middle"
});
txtFileData.preferredSize.width = 300;
// Panel 'Options'
p = w.add("panel", undefined, "Options");
p.alignChildren = "left";
p.margins = [12, 18, 12, 12];
g = p.add("group");
g.add("statictext", undefined, "Ruler units:");
listUnits = g.add("dropdownlist", undefined, measurementUnits);
g = p.add("group");
cbPageId = g.add("checkbox", undefined, "Add page ID");
grpPageId = g.add("group");
rbPageIdTop = grpPageId.add("radiobutton", undefined, "Top");
grpPageId.add("radiobutton", undefined, "Bottom");
// Action Buttons
g = w.add("group");
g.alignment = "center";
btnOk = g.add("button", undefined, "OK");
btnCancel = g.add("button", undefined, "Cancel");
// Panel Copyright
p = w.add("panel");
p.add("statictext", undefined, "Copyright 2024 William Campbell");
// SET UI VALUES
listUnits.selection = 2; // inches
rbPageIdTop.value = true;
configureUi();
// UI ELEMENT EVENT HANDLERS
// Panel 'Data file'
btnFileData.onClick = function () {
var f;
var message;
var validateFile;
message = "Select CSV file";
if (File.fs == "Macintosh") {
validateFile = function (f) {
if ((f instanceof Folder) || f.alias || /\.csv$/i.test(f.name)) {
return true;
}
return false;
};
} else {
// Windows
validateFile = "Comma Separated Values:*.csv";
}
f = File.openDialog(message, validateFile);
if (f) {
if (/\.csv$/i.test(f.name)) {
message = readData(f);
if (!message) { // Success
txtFileData.text = File.decode(f.fullName);
}
}
if (message) { // Fail
txtFileData.text = "";
alert(message, " ", false);
}
}
};
// Panel 'Options'
cbPageId.onClick = configureUi;
// Action Buttons
btnOk.onClick = function () {
if (!(dataArray && dataArray.length)) {
alert("Select CSV file", " ", false);
return;
}
w.close(1);
};
btnCancel.onClick = function () {
w.close(0);
};
// DISPLAY THE DIALOG
if (w.show() == 1) {
doneMessage = "";
try {
process();
doneMessage = "Done";
} catch (e) {
error = error || e;
doneMessage = "An error has occurred.\nLine " + error.line + ": " + error.message;
}
doneMessage && alert(doneMessage, title, error);
}
//====================================================================
// END PROGRAM EXECUTION, BEGIN FUNCTIONS
//====================================================================
function configureUi() {
grpPageId.enabled = cbPageId.value;
}
function createDoc() {
// Create new doc.
var d = app.documents.add();
d.documentPreferences.facingPages = false;
d.documentPreferences.allowPageShuffle = true;
d.documentPreferences.documentBleedTopOffset = 0;
d.documentPreferences.documentBleedUniformSize = true;
// Margins to zero, single column.
d.marginPreferences.left = 0;
d.marginPreferences.top = 0;
d.marginPreferences.right = 0;
d.marginPreferences.bottom = 0;
d.marginPreferences.columnCount = 1;
d.marginPreferences.columnGutter = 0;
// Set the measurement units and ruler origin.
d.viewPreferences.horizontalMeasurementUnits = measurementUnitsEnum[listUnits.selection.index];
d.viewPreferences.verticalMeasurementUnits = measurementUnitsEnum[listUnits.selection.index];
d.viewPreferences.rulerOrigin = RulerOrigin.PAGE_ORIGIN;
d.zeroPoint = [0, 0];
// Remove excess master page.
if (d.masterSpreads[0].pages.length > 1) {
d.masterSpreads[0].pages[1].remove();
}
// Set master page margins.
d.masterSpreads[0].pages[0].marginPreferences.left = 0;
d.masterSpreads[0].pages[0].marginPreferences.top = 0;
d.masterSpreads[0].pages[0].marginPreferences.right = 0;
d.masterSpreads[0].pages[0].marginPreferences.bottom = 0;
d.masterSpreads[0].pages[0].marginPreferences.columnCount = 1;
d.masterSpreads[0].pages[0].marginPreferences.columnGutter = 0;
// Return new doc.
return d;
}
function marginArray(v) {
var a;
var n;
if (/,/.test(v)) {
a = v.split(",");
if (a.length != 4) {
a = [0, 0, 0, 0];
}
} else {
n = Number(v) || 0;
a = [n, n, n, n];
}
return a;
}
function parseCsv(contents, delimiter) {
// contents: String = contents of a CSV file
// delimiter: character that separates columns
// undefined defaults to comma
// Returns: Array [row][column]
var c = ""; // Character at index.
var d = delimiter || ","; // Default to comma.
var index = 0;
var maxIndex = contents.length - 1;
var q = false; // "Are we in quotes?"
var result = []; // Array of rows.
var row = []; // Array of columns.
var rowSum = 0; // Count of row contents.
var v = ""; // Column value.
while (index < contents.length) {
c = contents[index];
if (q) { // In quotes.
if (c == "\"") {
// Found quote; look ahead for another.
if (index < maxIndex && contents[index + 1] == "\"") {
// Found another quote means escaped.
// Increment and add to column value.
index++;
v += c;
} else {
// Next character not a quote; last quote not escaped.
q = !q; // Toggle "Are we in quotes?"
}
} else {
// Add character to column value.
v += c;
}
} else { // Not in quotes.
if (c == "\"") {
// Found quote.
q = !q; // Toggle "Are we in quotes?"
} else if (c == "\n" || c == "\r") {
// Reached end of line.
// Test for CRLF.
if (c == "\r" && index < maxIndex) {
if (contents[index + 1] == "\n") {
// Skip trailing newline.
index++;
}
}
// Column and row complete.
row.push(v);
rowSum += v.length;
if (rowSum) {
// Add row only when it has content.
result.push(row);
}
v = "";
row = [];
rowSum = 0;
} else if (c == d) {
// Found delimiter; column complete.
row.push(v);
rowSum += v.length;
v = "";
} else {
// Add character to column value.
v += c;
}
}
if (index == maxIndex) {
// Reached end of data; flush.
row.push(v);
rowSum += v.length;
if (rowSum) {
// Add row only when it has content.
result.push(row);
}
break;
}
index++;
}
return result;
}
function process() {
var columnCount;
var columnGutter;
var doc;
var docScratch;
var i;
var ii;
var objectStyle;
var pageScratch;
var page;
var pageHeight;
var pageId;
var pageMargin;
var pagePrefix;
var pageWidth;
var paragraphStyle;
var swatchNone;
var textFrame;
// Set script preferences.
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;
app.scriptPreferences.measurementUnit = measurementUnitsEnum[listUnits.selection.index];
try {
doc = createDoc(pageWidth, pageHeight);
doc.sections[0].continueNumbering = false;
doc.sections[0].pageNumberStart = 1;
swatchNone = doc.swatches[0];
if (cbPageId.value) {
// Make paragraph style.
paragraphStyle = doc.paragraphStyles.add();
paragraphStyle.alignToBaseline = false;
paragraphStyle.appliedFont = app.fonts.itemByName("Arial\tRegular");
paragraphStyle.fillColor = doc.swatches.itemByName("Black");
paragraphStyle.fillTint = 100;
paragraphStyle.name = "Page ID";
paragraphStyle.overprintFill = false;
paragraphStyle.pointSize = 0.1875;
paragraphStyle.justification = Justification.CENTER_ALIGN;
// Make object style.
objectStyle = doc.objectStyles.add();
objectStyle.name = "Page ID";
objectStyle.fillColor = swatchNone;
objectStyle.strokeColor = swatchNone;
objectStyle.textFramePreferences.verticalJustification = VerticalJustification.CENTER_ALIGN;
}
// Add pages.
for (i = 0, ii = 1; i < dataArray.length; i++) {
pagePrefix = dataArray[i][columnIndex[0]];
pageWidth = Number(dataArray[i][columnIndex[1]]);
pageHeight = Number(dataArray[i][columnIndex[2]]);
pageMargin = null;
pageId = null;
if (columnIndex[3]) {
pageMargin = marginArray(dataArray[i][columnIndex[3]]);
}
if (columnIndex[4]) {
pageId = dataArray[i][columnIndex[4]];
}
if (columnIndex[5]) {
columnCount = Number(dataArray[i][columnIndex[5]]);
}
if (columnIndex[6]) {
columnGutter = Number(dataArray[i][columnIndex[6]]);
}
docScratch = createDoc(pageWidth, pageHeight);
docScratch.documentPreferences.pageWidth = pageWidth;
docScratch.documentPreferences.pageHeight = pageHeight;
pageScratch = docScratch.pages[0];
app.activeDocument = doc;
page = doc.pages.add(LocationOptions.AT_END);
pageScratch.duplicate(LocationOptions.AFTER, page);
page.remove();
docScratch.close(SaveOptions.NO);
doc.sections[ii].sectionPrefix = pagePrefix.substr(0, 7) + "-";
doc.sections[ii].continueNumbering = false;
doc.sections[ii].pageNumberStart = 1;
page = doc.pages.lastItem();
if (pageMargin) {
page.marginPreferences.left = pageMargin[0];
page.marginPreferences.top = pageMargin[1];
page.marginPreferences.right = pageMargin[2];
page.marginPreferences.bottom = pageMargin[3];
}
if (cbPageId.value) {
textFrame = page.textFrames.add();
textFrame.itemLayer = doc.layers[0];
if (rbPageIdTop.value) {
textFrame.geometricBounds = [-0.5, 0, -0.125, pageWidth];
} else {
textFrame.geometricBounds = [pageHeight + 0.125, 0, pageHeight + 0.5, pageWidth];
}
textFrame.fillColor = swatchNone;
textFrame.strokeColor = swatchNone;
textFrame.textFramePreferences.verticalJustification = VerticalJustification.CENTER_ALIGN;
// Character style 'None' must precede adding contents.
textFrame.insertionPoints[0].appliedCharacterStyle = doc.characterStyles[0];
textFrame.contents = pageId || pagePrefix;
// Set styles.
textFrame.paragraphs[0].appliedParagraphStyle = paragraphStyle;
textFrame.appliedObjectStyle = objectStyle;
}
if (columnCount) {
page.marginPreferences.columnCount = columnCount;
}
if (columnGutter) {
page.marginPreferences.columnGutter = columnGutter;
} else {
page.marginPreferences.columnGutter = 0;
}
// Increment section.
ii++;
}
// Remove page 1.
doc.pages[0].remove();
// View pages vertically instead of by alternate layout.
app.panels.itemByName("Pages").pagesViewSetting = PageViewOptions.VERTICALLY;
} finally {
// Restore script preferences.
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.INTERACT_WITH_ALL;
}
}
function readData(file) {
var data;
var header = [];
var i;
// Read data file.
if (!file.open("r")) {
return "Failed to open data file";
}
try {
data = file.read();
} catch (e) {
return "Error reading data file: " + e.message;
} finally {
file.close();
}
// Parse data file.
dataArray = parseCsv(data, rbSemicolon.value ? ";" : ",");
// Two rows minimum.
if (dataArray.length < 2) {
return "Data file missing rows";
}
// Extract header row.
header = dataArray.shift();
// Indexes 0 to 2 are required.
columnIndex = []; // Clear array.
for (i = 0; i < 3; i++) {
columnIndex[i] = header.indexCi(columnNames[i]);
if (columnIndex[i] == -1) {
return "Data file missing column '" + columnNames[i] + "'";
}
}
// Indexes 3 to 6 are optional.
for (i = 3; i < 7; i++) {
columnIndex[i] = header.indexCi(columnNames[i]);
if (columnIndex[i] == -1) {
columnIndex[i] = null;
}
}
return null; // Success
}
})();
Pages From CSV
For help installing scripts, see How to Install and Use Scripts in Adobe Creative Cloud Applications.
IMPORTANT: scripts are developed for the latest Adobe Creative Cloud applications. Many scripts work in CC 2018 and later, even some as far back as CS6, but may not perform as expected, or run at all, when used in versions prior to 2018. Photoshop features Select Subject and Preserve Details 2.0 definitely fail prior to CC 2018 (version 19) as the features do not exist in earlier versions. For best results use the latest versions of Adobe Creative Cloud applications.
IMPORTANT: by downloading any of the scripts on this page you agree that the software is provided without any warranty, express or implied. USE AT YOUR OWN RISK. Always make backups of important data.
IMPORTANT: fees paid for software products are the purchase of a non-exclusive license to use the software product and do not grant the purchaser any degree of ownership of the software code. Author of the intellectual property and copyright holder William Campbell retains 100% ownership of all code used in all software products regardless of the inspiration for the software product design or functionality.