creates a InDesign book from jekyll .md files
// createBook.jsx
// creates a InDesign Book from .md files
// dependencies as follow
// font
// folder called images in jekyll folder
// some find change queries
// https://github.com/fabiantheblind/MT4D-GREP
// place them under:
// /Users/[YOUR USERNAME]/Library/Preferences/Adobe InDesign/Version [NUMBER]/[LOCALIZATION de_DE]/Find-Change Queries/GREP
// Copyright (c) 2012
// Fabian "fabiantheblind" Morón Zirfas
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to
// whom the Software is furnished to do so, subject to
// the following conditions:
// The above copyright notice and this permission notice
// shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTIO
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// see also http://www.opensource.org/licenses/mit-license.php
{
var DEBUG = true;
main();
/**
* Main function without params
* evrything is in there
*
*/
function main(){
// ------------ the GREP FC Queries names are + .xml ------------
var findChangeXMLs = new Array ();
findChangeXMLs.push("md h1");
findChangeXMLs.push("md h2");
findChangeXMLs.push("md h3");
findChangeXMLs.push("md h4");
findChangeXMLs.push("md h5");
findChangeXMLs.push("md h6");
findChangeXMLs.push("md to bold 1");
findChangeXMLs.push("md to bold 2");
findChangeXMLs.push("md to code");
findChangeXMLs.push("md to bq");
// findChangeXMLs.push("md to ul li");
findChangeXMLs.push("md code tabstops");
var findChangeIMAGEGrep = "md find images";
var meta = loadFiles();// get the files from the folder
if(meta == null){return};// if the user cancels
var allMarkdownFiles = meta.files;// allMarkdown
var theFolder = meta.folder;// the folder where to work
var imageFolder = new Folder(theFolder.fsName+"/"+ "images");
var allImages = imageFolder.getFiles("*.*");
var IDtargetFolder = new Folder(theFolder.fsName +"/"+ "IDfiles");
if(!IDtargetFolder.exists){
IDtargetFolder.create();
};
var allDocs = new Array();// for the book
// alert(allMarkdownFiles);
// ------------ now loop the .md files ------------
for (var i in allMarkdownFiles){
var theFile = allMarkdownFiles[i];// File.openDialog ("select your .md file","*.*",false);
// strip of the extension
var nameExtensionless = allMarkdownFiles[i].name.substring(0, allMarkdownFiles[i].name.length-3);
var doc = createDoc(nameExtensionless);//a new doc with that name
var dp = doc.documentPreferences; // 4 better handling
var mp = doc.marginPreferences; // 4 better handling
var styles = createStyles(doc); // create the styles
doc.pages.item(0).appliedMaster = doc.masterSpreads.item(0);
var tf = doc.pages.item(0).textFrames.add({
geometricBounds:[mp.top,mp.left,dp.pageHeight - mp.bottom, dp.pageWidth - mp.right]
});// add the first textframe on page 0
var FileString = readinFile(theFile);// read in data from the file
tf.contents = FileString; // push it into the textframe
DumbRunPages (doc, tf.parentStory, 0);// now run the pages so ther is no overflow
// ------------ needs a basic par style ------------
for (var l = doc.textFrames.length - 1; l >= 0; l--) {
doc.textFrames[l].paragraphs.everyItem().applyParagraphStyle(doc.paragraphStyles.item("body"));
}; // close l loop
DumbRunPages (doc, tf.parentStory, 0);// run it again if it overflows now
// ------------ Find change all markdown - this is a still a bit buggy ------------
for(var k = 0; k< findChangeXMLs.length; k++){
app.loadFindChangeQuery (findChangeXMLs[k], SearchModes.grepSearch);
app.activeDocument.changeGrep();
//Clear the find/change grep preferences.
app.findGrepPreferences = NothingEnum.nothing;
app.changeGrepPreferences = NothingEnum.nothing;
}; // close k loop
// this seems more buggy ;)
// markdown(doc,styles);
// now create a file in that folder and safe the doc into it
var theIDFile = new File (IDtargetFolder.fsName + "/" + nameExtensionless + ".indd");
if(theIDFile.exists){
theIDFile.remove(); // we want to keep it clean and not overwrite
};
getImageNames(doc, allImages);
doc.save(theIDFile); // save the doc
// add that doc to a list
allDocs.push(doc);
}; // close i loop
// now make a book
var theBookRef = new File(IDtargetFolder.fsName + "/" + "mt4dBook.indb");
if(theBookRef.exists){
theBookRef.remove();
};
var theBook = app.books.add(theBookRef);
// loop the docs
for (var j in allDocs) {
theBook.bookContents.add(allDocs[j].fullName); // add every doc to the book
};
theBook.styleSourceDocument = allDocs[0];
theBook.synchronizeBulletNumberingList =true;
theBook.synchronizeCharacterStyle =true;
theBook.synchronizeMasterPage =true;
theBook.synchronizeObjectStyle =true;
theBook.synchronizeParagraphStyle =true;
theBook.synchronizeSwatch =true;
theBook.save();
};
/**
* loads the markdown files and defiens the folder
*
*/
function loadFiles(){// the function that loads the files
var theFolder = Folder.selectDialog ("Choose the FOLDER");// user select a folder
if(!theFolder){// if the folder is not a folder cancel the script
return;// this cancels the whole function image_loadFiles \(\)
}; // end foldr check
var theFileType = "*.md";// use only jpg
var allMarkdownFiles = null; // will hold the md files
try{// encapsulate in a try catch
allMarkdownFiles = theFolder.getFiles(theFileType);// get the files by type
}catch(e){// this is the error catcher
alert("Error with this:\n" +e);// e is what InDesign says
};// end catch
if((allMarkdownFiles.length < 1)||(allMarkdownFiles == null) ){// again if check if there are images
alert("There are no .md files");// hm something went wrong? User error
return null;// so we cancel the function
}else{
return {"files":allMarkdownFiles,"folder":theFolder};// give back the md files. Success!
};// end all images check
};
/**
* This reads in the data from the found .md files
*
*/
function readinFile(theFile){
// var textFile = File.openDialog("Select a text file to import.", "*.*",false);
// var path = ((new File($.fileName)).path);
// var textFile = File( path+"/world_geo_json/" + THESELECTEDFILENAME);
var textFile = File(theFile); // File.openDialog ("select your .md file","*.*",false)
if (textFile != null) {
var textLines = new Array();
textFile.open("r", "TEXT", "????");
while (!textFile.eof){
textLines[textLines.length] = textFile.readln();
}
textFile.close();
}
if(!textLines){
return;
}
var str = textLines.join("\r");
// var reg = new RegExp("\n|\r","g");
// str.replace (reg, " ");
// alert(str);
// var obj = eval("(" + str + ")"); // evaluate the JSON code
return str;
};
/**
* This finds the image referenzes in the text
* and places the images from the folder called images
*
*/
function getImageNames(doc, allImages){
for(var i =0; i < allImages.length;i++){
app.findTextPreferences = NothingEnum.nothing;
app.changeTextPreferences = NothingEnum.nothing;
app.findTextPreferences.findWhat = allImages[i].name;
var result = doc.findText();
for (var k = result.length-1; k >= 0; k--){
var ip = result[k].insertionPoints[0];
var pp = ip.parentStory.parent;
var target = pp.rectangles.add({
geometricBounds:[0,0,100,doc.documentPreferences.pageWidth],
label: allImages[i].name
});
target.applyObjectStyle( doc.objectStyles.item("images"),true,false);
// we need to catch this. if there are subfolders or so
try{ target.place(allImages[i]);}catch(e){/* if(DEBUG)alert(e +"\n" + allImages[i].name) */};
target.fit(FitOptions.PROPORTIONALLY);
target.fit(FitOptions.CENTER_CONTENT);
};
};
};
/**
* This creates the paragraph styles
* fine thing is it does that from an
* JSON Object
*
*/
function createStyles(theDoc){
var obstyles = {
"styles":[
{
"name": "images",
"strokeWeight":0,
"enableTextWrapAndOthers":true,
"textWrapPreferences":{
"textWrapMode":1650552420 /*TextWrapModes.JUMP_OBJECT_TEXT_WRAP*/,
"textWrapOffset":[5,5]
},
"transparencySettings":{
"blendingSettings":{
"opacity":100
}
}
}
]
};
theDoc.objectStyles.add(obstyles.styles[0]);
var basesize = 12;
var h456sz = goldenRatioRetMajor((basesize/3 )*2);
var h3sz = goldenRatioRetMajor((h456sz/3 )*2);
var h2sz = goldenRatioRetMajor((h3sz/3 )*2);
var h1sz = goldenRatioRetMajor((h2sz/2 )*2);
var parStyles = {
"styles":[
{
"name":"h1" ,
"appliedFont":app.fonts.item(50)/* Calluna black */
, "pointSize":h1sz,
"basedOn":theDoc.paragraphStyles.item(0),
"alignToBaseline":true,
"leading": goldenRatioRetMinor(h1sz),
},
{"name":"h2" ,"appliedFont":app.fonts.item(50) , "pointSize":h2sz,"alignToBaseline":true,"leading":h2sz },
{"name":"h3" ,"appliedFont":app.fonts.item(49) , "pointSize":h3sz ,"alignToBaseline":true,"leading":(h3sz)},
{"name":"h4" ,"appliedFont":app.fonts.item(49) , "pointSize":h456sz,"alignToBaseline":true ,"leading":(h456sz)},
{"name":"h5" ,"appliedFont":app.fonts.item(48) , "pointSize":h456sz,"alignToBaseline":true,"leading":(h456sz)},
{"name":"h6" ,"appliedFont":app.fonts.item(48) , "pointSize":h456sz ,"alignToBaseline":true,"leading":(h456sz)},
{
"name":"body" ,
"appliedFont":app.fonts.item(47) ,
"pointSize":basesize /* ,"basedOn":theDoc.paragraphStyles.item(0) */,
"justification":1818915700 /* left justified*/,
"firstLineIndent": goldenRatioRetMinor(goldenRatioRetMinor(basesize)),
"alignToBaseline":true,
"leading":15,
},
{"name":"ul" ,"appliedFont":app.fonts.item(47), "pointSize":11,"alignToBaseline":true},
{"name":"ol" ,"appliedFont":app.fonts.item(47) , "pointSize":11,"alignToBaseline":true},
{
"name":"blockquote" ,
"appliedFont":app.fonts.item(48) ,
"pointSize":12 , "basedOn":theDoc.paragraphStyles.item(0),
"justification":1818915700,/* left justified*/
"leftIndent":5,
"rightIndent":5,
"alignToBaseline":true
},
{
"name":"marginal" ,
"appliedFont":app.fonts.item(51) ,
"pointSize":6, /* "basedOn":theDoc.paragraphStyles.item(0) */
"justification":1818584692, /*left align*/
/* "gridAlignment":1247896172, */
"gridAlignFirstLineOnly":true
}
],
};
// ------------ CHARCTER STYLES ------------
var charStyles = {
"styles":[
{"name":"strong" ,"appliedFont":app.fonts.item(47)},
{"name":"em" ,"appliedFont":app.fonts.item(47)},
{"name":"code" ,"appliedFont":app.fonts.item(93)}
],
};
// ------------ loop the JSON objects ------------
for(var i in parStyles.styles){
try{
theDoc.paragraphStyles.add(parStyles.styles[i]);
}catch(e){ if(DEBUG)alert(e + "\n" + i +"\n"+ parStyles.styles[i].name);};
};
for(var i in charStyles.styles){
try{
theDoc.characterStyles.add(charStyles.styles[i]);
}catch(e){ if(DEBUG)alert(e + "\n" + i+"\n"+ charStyles.styles[i].name);};
};
// ------------ this is a special style with nested Grep styles ------------
var code = build_code_paragraphStyle(theDoc);
// ------------ this is the styles object that comes back ------------
var styles ={
/* Par styles*/
"h1" :theDoc.paragraphStyles.item("h1"),
"h2" :theDoc.paragraphStyles.item("h2"),
"h3" :theDoc.paragraphStyles.item("h3"),
"h4" :theDoc.paragraphStyles.item("h4"),
"h5" :theDoc.paragraphStyles.item("h5"),
"h6" :theDoc.paragraphStyles.item("h6"),
"ol" :theDoc.paragraphStyles.item("ol"),
"ul" :theDoc.paragraphStyles.item("ul"),
"code" :theDoc.paragraphStyles.item("code"),
"blockquote" :theDoc.paragraphStyles.item("blockquote"),
/* char styles*/
"strong":theDoc.characterStyles.item("strong"),
"em" :theDoc.characterStyles.item("em"),
"code" :theDoc.characterStyles.item("code")
};
return styles;
};
/**
* Creates our doc with lots of specs
*
*/
function createDoc (theDocName) {
var ph = 250;
var pw = goldenRatioRetMinor(ph);
var gutter = 4;
var marginalwidth = goldenRatioRetMinor(55);
var doc = app.documents.add({name:theDocName, facingPages:true, documentPreferences:{pageWidth: pw, pageHeight:ph }})
doc.gridPreferences.baselineDivision = "15pt";
doc.viewPreferences.properties = {
horizontalMeasurementUnits: MeasurementUnits.MILLIMETERS,
verticalMeasurementUnits:MeasurementUnits.MILLIMETERS,
rulerOrigin: RulerOrigin.PAGE_ORIGIN
};
var b = goldenRatioRetMinor(42);
var t = 30;
var l = goldenRatioRetMinor(t);
var r = goldenRatioRetMinor(30);
var rhalf =r/2;
var rightnleft = (rhalf + l);
doc.marginPreferences.properties = {
top:t,
left:l,
bottom:b,
right:r
};
var mp = doc.marginPreferences;
doc.documentPreferences.properties = {
documentBleedBottomOffset : 3,
documentBleedTopOffset : 3,
documentBleedInsideOrLeftOffset : 3,
documentBleedOutsideOrRightOffset : 3
};
var ms1 = doc.masterSpreads.item(0);
var ms1p1 = ms1.pages.item(0);// edit the masterspreads
ms1p1.marginPreferences.properties = {
right: mp.right,
top: mp.top,
left: mp.left,
bottom: mp.bottom,
columnGutter:gutter
};
var ms1p2 = ms1.pages.item(1);//edit the other masterspred
ms1p2.marginPreferences.properties = {
right: mp.right,
top: mp.top,
left: mp.left,
bottom: mp.bottom,
columnGutter:gutter
};
var ms2 = doc.masterSpreads.add();
var ms2p1 = ms2.pages.item(0);// edit the masterspreads
ms2p1.marginPreferences.properties = {
right: rhalf,
top: mp.top,
left: mp.left,
bottom: mp.bottom,
columnGutter:gutter,
columnsPositions:[0,
marginalwidth,
marginalwidth + gutter,
pw - rightnleft
]
};
var ms2p2 = ms2.pages.item(1);// edit the masterspreads
ms2p2.marginPreferences.properties = {
right: rhalf ,
top: mp.top,
left: mp.left ,
bottom: mp.bottom,
columnGutter:gutter,
columnsPositions:[0,
pw - ( marginalwidth + gutter + rhalf + mp.left) ,
pw - ( marginalwidth + rhalf + mp.left) ,
pw - (rhalf +mp.left) ]
};
return doc;
};
// you can use ths solo
// alert($.fileName);
// var d = app.documents.add();
// var par = build_code_paragraphStyle(d);
function build_code_paragraphStyle(d){
var colkw = color_add (d, "keywords", ColorModel.PROCESS, [0,0,0,100]);
var colcmnt = color_add (d, "comments", ColorModel.PROCESS, [0,0,0, 66]);
var colop = color_add (d, "operators", ColorModel.PROCESS, [0,0,0,100]);
var colsep = color_add (d, "separators", ColorModel.PROCESS, [0,0,0,100]);
var colnum = color_add (d, "numbers", ColorModel.PROCESS, [0,0,0,100]);
// color_add (d, "comment", ColorModel.PROCESS, [100,0,75,0]);
var colstr = color_add (d, "strings", ColorModel.PROCESS, [0,0,0,100]);
var charStyles = new Array();
var keywords = d.characterStyles.add({name:"keywords", fillColor: colkw});
var comments = d.characterStyles.add({name:"comments", fillColor: colcmnt});
var operators = d.characterStyles.add({name:"operators", fillColor: colop});
var separators = d.characterStyles.add({name:"separators", fillColor: colsep});
var numbers = d.characterStyles.add({name:"numbers", fillColor: colnum});
var comment = d.characterStyles.add({name:"comment", fillColor: colcmnt});
var string = d.characterStyles.add({name:"strings", fillColor: colstr});
var code = d.paragraphStyles.add({name:"code",appliedFont:"DejaVu Sans Mono Book",alignToBaseline:true, "pointSize":10 });
//change language (only in this paragraphStyle) to get the right "" for the code
code.appliedLanguage = app.languagesWithVendors.item("English: USA")
var grp = code.nestedGrepStyles.add();
grp.appliedCharacterStyle = keywords;
grp.grepExpression = "abstract|boolean|break|byte|case|catch|char|class|const|continue|debugger|default|delete|\sdo\s|double|else|enum|export|extends|false|final|finally|float|for|function|goto|if|implements|import|\sin\s|instanceof|int|interface|long|native|\snew\s|null|package|private|protected|public|return|short|static|super|switch|synchronized|this|throw|throws|transient|true|try|typeof|var|void|volatile|while|with";
grp = code.nestedGrepStyles.add();
grp.appliedCharacterStyle = keywords;
grp.grepExpression = "is|new|sizeof|typeof";
grp = code.nestedGrepStyles.add();
grp.appliedCharacterStyle = operators;
grp.grepExpression = "[-~\\[\\]!$%&*+/:<=>?^|]+";
grp = code.nestedGrepStyles.add();
grp.appliedCharacterStyle = separators;
grp.grepExpression = "[(){},;\\s]+";
grp = code.nestedGrepStyles.add();
grp.appliedCharacterStyle = numbers;
grp.grepExpression = "\\<[0-9]+(\\.[0-9]+)?([eE][-+]?[0-9]+)?";
grp = code.nestedGrepStyles.add();
grp.appliedCharacterStyle = comments;
grp.grepExpression = "/\\*+[^*]*\\*+([^/*][^*]*\\*+)*/";
grp = code.nestedGrepStyles.add();
grp.appliedCharacterStyle = comment;
grp.grepExpression = "//.*";
grp = code.nestedGrepStyles.add();
grp.appliedCharacterStyle = string;
grp.grepExpression = "\".*?\"";
return code;
};
// _ _ _______ _____ _ _____ _______ _____ ______ _____
// | | | |__ __|_ _| | |_ _|__ __|_ _| ____|/ ____|
// | | | | | | | | | | | | | | | | | |__ | (___
// | | | | | | | | | | | | | | | | | __| \___ \
// | |__| | | | _| |_| |____ _| |_ | | _| |_| |____ ____) |
// \____/ |_| |_____|______|_____| |_| |_____|______|_____/
/**
* calc the golden ratio
*
*/
function goldenRatioRetMinor(val){
var res = val / 1.618033989;
return res;
};
function goldenRatioRetMajor(val){
var res = 1.618033989 * val;
return res;
};
// found on http://bit.ly/h5EobK indisnip.wordpress.com ->
// how to apply:
// add CMYK color
//myColorAdd(app.activeDocument, "My Custom Color", ColorModel.PROCESS, [80,50,30,10]);
// add RGB color
//myColorAdd(app.activeDocument, "My Custom Color", ColorModel.PROCESS, [33,66,99]);
// add HEX color
//myColorAdd(app.activeDocument, "My Custom Color", ColorModel.PROCESS, "ABCDEF");
// add color directly
// add CMYK color to document
// and asign it to selected object
//app.selection[0].fillColor = myColorAdd(app.activeDocument, "My Custom Color", ColorModel.PROCESS, [80,50,30,10]);
function color_add(myDocument, myColorName, myColorModel, myColorValue){
if(myColorValue instanceof Array == false){
myColorValue = [(parseInt(myColorValue, 16) >> 16 ) & 0xff, (parseInt(myColorValue, 16) >> 8 ) & 0xff, parseInt(myColorValue, 16 ) & 0xff ];
myColorSpace = ColorSpace.RGB;
}else{
if(myColorValue.length == 3)
{
myColorSpace = ColorSpace.RGB;
}else{
myColorSpace = ColorSpace.CMYK;
};
};
try{
myColor = myDocument.colors.item(myColorName);
myName = myColor.name;
} catch (myError){
myColor = myDocument.colors.add();
myColor.properties = {name:myColorName, model:myColorModel, space:myColorSpace ,colorValue:myColorValue};
};
return myColor;
};
/**
* This is dumb run pages. (takes care of the overflow)
*/
// by Dave Saunders - where are you Dave?
// UPDATE 2012 05 10 Dave is back!
// http://jsid.blogspot.de/2005/12/function-snippets.html
function DumbRunPages(theDoc, theStory, mssp_id) {
// What makes this "dumb" is that default master pages are used.
var uRuler = theDoc.viewPreferences.rulerOrigin;
theDoc.viewPreferences.rulerOrigin = RulerOrigin.spreadOrigin;
while (theStory.textContainers[theStory.textContainers.length-1].overflows) {
/*
// Original: Seite nach der letzten Dokumentseite einfügen
theDoc.documentPreferences.pagesPerDocument = theDoc.documentPreferences.pagesPerDocument + 1;
var backPage = theDoc.pages[-1];
*/
//alternativ: Seite nach der letzten Textrahmenseite einfügen
var backPage = theDoc.pages.add();
backPage.appliedMaster = theDoc.masterSpreads.item(0);
app.activeWindow.activePage = backPage;
//~ backPage.appliedMaster = theDoc.pages[-2].appliedMaster;
var myPbounds = backPage.bounds;
var myNewTF = backPage.textFrames.add();
if ((backPage.name % 2 == 1) || (!theDoc.documentPreferences.facingPages)) {
myNewTF.geometricBounds =
[myPbounds[0] + backPage.marginPreferences.top,
myPbounds[1] + backPage.marginPreferences.left,
myPbounds[2] - backPage.marginPreferences.bottom,
myPbounds[3] - backPage.marginPreferences.right];
} else {
myNewTF.geometricBounds =
[myPbounds[0] + backPage.marginPreferences.top,
myPbounds[1] + backPage.marginPreferences.right,
myPbounds[2] - backPage.marginPreferences.bottom,
myPbounds[3] - backPage.marginPreferences.left];
}
myNewTF.itemLayer = theStory.textContainers[theStory.textContainers.length-1].itemLayer;
myNewTF.previousTextFrame = theStory.textContainers[theStory.textContainers.length-1];
if (myNewTF.characters.length == 0){
theDoc.viewPreferences.rulerOrigin = uRuler;
alert("Permanently overset"); // This indicates a permanent overset condition so break out of loop
break;
}
}
theDoc.viewPreferences.rulerOrigin = uRuler;
};
}