Skip to content

Instantly share code, notes, and snippets.

@ff6347
Created May 11, 2012 14:15
Show Gist options
  • Save ff6347/2659939 to your computer and use it in GitHub Desktop.
Save ff6347/2659939 to your computer and use it in GitHub Desktop.
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;
};
}
@ff6347
Copy link
Author

ff6347 commented May 11, 2012

This Gist is part of MT4D coming soon on fabiantheblind.info

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment