﻿#target indesign

/*
 * CONFIG
 * KNOWN BUGS AND TODO:  - there is actualy only one line possible, maybe change that.
 *                       - which color for the rectangle-line?
 *                       - the config.color is hidden in the code, would be nice to bring to top
 *                       - due to the fact the font is now transparent, the shadow on
 *                         the bottom will shine through, this will generate an emboss effect.
 *                         so atm its not possible to use shadow with faded or u want the effect.
 *                       - shear is not centered
 */

/*
 * MAIN CONFIGURATION
 */
var config = {
    // size of the document in mm
    pageWidth:      297,
    pageHeight:     210,

    // this is the spacing between the word in mm
    gutter:         2,

    // if u use "---" in the array, it will
    // be replaced with a rectangle this is the height in mm
    lineHeight:     2,

    // choose a font, this is quite tricky maybe u should
    // generate a list of your fonts with this script:
    // https://github.com/fabiantheblind/auto-typo-adbe-id/wiki/Fonts
    // fontFamily:     'Dezen Pro	_Heavy',

    // this is the content of your generated typo
    // each line represents a new line in the document
    contents:       [
                        "this is",
                        "example",
                        "text"
                    ]
};

config.margins = {
    // this are the margins for the document
    // the right and left margin are quite important
    // they are forcing the generated typo to shrink or grow
    top:            40,
    right:          100,
    bottom:         40,
    left:           100
};

config.bounds = {
    // this is for calculating some internal stuff
    // you propably don't need to edit this
    top:            config.margins.top,
    right:          config.pageWidth - config.margins.right,
    bottom:         config.pageHeight - config.margins.bottom,
    left:           config.margins.left,
    width:          config.pageWidth - (config.margins.right + config.margins.left),
    height:         config.pageHeight - (config.margins.top + config.margins.bottom)
};

config.customization = {
    // turn shadow on or off
    shadow:         0,

    // turn shear on or off
    shear:          0,

    // turn rotation on or off
    rotate:         0,

    // turn centered on or off
    centered:       1,

    // turn the faded-effect on or off
    faded:          0,

    // turn a transparency mask on or off
    vignette:       1,

    // turn the perspective on or off
    perspective:    0
};

config.shadow = {
    // this is the left offset for the shadow (remember, its mm)
    left:           0.5,
    // this is the right offset ...
    top:            0.5
};

config.shear = {
    // this is the angle for shearing
    angle:          15
};

config.rotate = {
    // this is the angle for rotating
    angle:          15
};

config.vignette = {
    // thats the strenght of the vignette (opacity)
    strength:       20
};

config.colors = {
    // set the saturation for the background color
    saturation:     100,
    // set the brightness for the background color
    brightness:     20
};


/*
 * Helper Functions
 */

var helpers = {
    size: function(obj) {
        // well thats a helper-funtion to calculate the size
        // of a textframe/polygon/rectangle whatever has a geometricBound
        var rtn = {
            height:     obj.geometricBounds[2] - obj.geometricBounds[0],
            width:      obj.geometricBounds[3] - obj.geometricBounds[1]
        };
        rtn.multiplier = config.bounds.width / rtn.width;
        return rtn;
    },
    colors: function(doc, num) {
        // this is the old unharmonic random color generator
        var rtn = [];
        num = num || 10;
        for(var i = 0; i < num; i++) {
            var myColor = doc.colors.add();

            var r = Math.random() * 255;
            var g = Math.random() * 255;
            var b = Math.random() * 255;

            myColor.name       = "rnd color: " + i;
            myColor.model      = ColorModel.PROCESS;
            myColor.space      = ColorSpace.RGB;
            myColor.colorValue = [r, g, b];

            // calculate the greyness
            myColor_brightness = (0.299 * r + 0.587 * g + 0.114 * b);

            rtn.push({ color: myColor, name: myColor.name, brightness: myColor_brightness });
        }
        return rtn;
    },
    contrast: function(doc, brightness) {
        // this is a lame atempt to check if we should use a black or white font :D
        if(brightness < 200) {
            // this is actualy the white swatch
            return doc.swatches.item(2);
        } else {
            // this is actualy the black swatch
            return doc.swatches.item(3);
        }
    },
    threshold: function(sensivity, multiplier) {
        var opacity = sensivity * multiplier;
        if( opacity > 100) opacity = 100;
        return opacity;
    },
    hsl2rgb: function(h, s, l) {
        // http://www.codingforums.com/showthread.php?t=11156
        var m1, m2, hue;
        var r, g, b;

        s /= 100;
        l /= 100;

        if(s === 0) {
            r = g = b = (l * 255);
        } else {
            if(l <= 0.5) {
                m2 = l * (s + 1);
            } else {
                m2 = l + s - l * s;
            }
            m1 = l * 2 - m2;
            hue = h / 360;
            r = this.hue2rgb(m1, m2, hue + 1/3);
            g = this.hue2rgb(m1, m2, hue);
            b = this.hue2rgb(m1, m2, hue - 1/3);
        }

        return {r: r, g: g, b: b};
    },
    hue2rgb: function(m1, m2, hue) {
        // http://www.codingforums.com/showthread.php?t=11156
        var v;
        if(hue < 0) {
            hue += 1;
        } else if(hue > 1) {
            hue -= 1;
        }

        if(6 * hue < 1) {
            v = m1 + (m2 - m1) * hue * 6;
        } else if(2 * hue < 1) {
            v = m2;
        } else if(3 * hue < 2){
            v = m1 + (m2 - m1) * (2/3 - hue) * 6;
        } else {
            v = m1;
        }

        return 255 * v;
    },
    hslRange: function(start, max) {
        // this is based on:
        // https://github.com/fabiantheblind/auto-typo-adbe-id/wiki/HSL-Color-Wheel
        var rtn = [];
        for(var degrees = start; degrees < (start + max); degrees++) {

            var radians             = ((degrees/360) % 360) * 2 * Math.PI;

            var temp_hue            = degrees; // the hue of the color
            var temp_saturation     = config.colors.saturation; // the saturation of the color
            var temp_lightness      = config.colors.brightness; // the lighness of the color
            var temp_rgb            = this.hsl2rgb(temp_hue, temp_saturation, temp_lightness);

            var clrSw = doc.colors.add();// add a color every iteration
            clrSw.properties = { /* set some props */
              name:         "color " + degrees,
              model:        ColorModel.PROCESS,
              space:        ColorSpace.RGB,
              colorValue:   [temp_rgb.r, temp_rgb.g, temp_rgb.b]
            };
            var rtnObj = { color: clrSw, brightness: temp_lightness * 2.55 };
            rtn.push(rtnObj);
        }
        return rtn;
    }
};


/*
 * MAIN SETUP
 */

// add a new document
var doc = app.documents.add({
    documentPreferences: {
        pageWidth:      config.pageWidth,
        pageHeight:     config.pageHeight,
        facingPages:    false
    }
});

// generate 10 random swatches
// old lame color-function
//~ var swatches = helpers.colors(doc, 10);
//~ var swatch = swatches[Math.floor(swatches.length * Math.random())];

// generate hsl colors
// new color-function
// take a random value from 0-360 and generate a range from ex. 124 to 134
var swatches = helpers.hslRange(Math.random() * 360, 10);
// get a random swatch and use it
var swatch = swatches[Math.floor(swatches.length * Math.random())];

// config colors object
// you maybe should not change it
config.colors = {
    sensivity:  6,
    background: swatch.color,
    text:       helpers.contrast(doc, swatch.brightness)
};

// set screenmode to preview
app.activeWindow.screenMode = ScreenModeOptions.PREVIEW_TO_PAGE;

// get first page from document
var page = doc.pages.firstItem();

// set page boundaries
page.marginPreferences.properties = {
    top:                config.margins.top,
    right:              config.margins.right,
    bottom:             config.margins.bottom,
    left:               config.margins.left
};

// add a background rectangle - locked
var bg = page.rectangles.add({
    geometricBounds:    [
                            0,
                            0,
                            config.pageHeight,
                            config.pageWidth
                        ],
    fillColor:          config.colors.background,
    strokeWeight:       0,
    // locked it, so you can easily select the output in indesign
    locked:             true
});

// add a radial gradient to the background
if(config.customization.vignette) {
    // add a seconds rectangle with a black-color
    var vignette = page.rectangles.add({
        geometricBounds:    [
                                0,
                                0,
                                config.pageHeight,
                                config.pageWidth
                            ],
        fillColor:          doc.swatches.item(3),
        strokeWeight:       0,
        locked:             true
    });

    // set the transparency setting
    vignette.transparencySettings.blendingSettings.properties = {
        // set the blend-mode to multiply
        blendMode: BlendMode.MULTIPLY,
        opacity: config.vignette.strength
    };

    // set the gradient-type to radial
    vignette.transparencySettings.gradientFeatherSettings.properties = {
        applied: true,
        angle: 90,
        type: GradientType.RADIAL
    };

    // set the first stop opacity to 0, so you can look through
    vignette.transparencySettings.gradientFeatherSettings.opacityGradientStops.item(0).properties = {
        opacity: 0
    };

    // set the seconds stop opacity to 100, so it will be black
    vignette.transparencySettings.gradientFeatherSettings.opacityGradientStops.item(1).properties = {
        opacity: 100
    };
}



/*
 * GENERATE ALL TEXTFRAMES AND CONVERT TO POLYGONS
 */

var obsRect     = 0;
var posObj      = {};

for(var i = 0; i < config.contents.length; i++) {

    // check if content is not "---"
    if(config.contents[i] !== '---') {

        // generate a textframe with the i-content from array
        var tf = page.textFrames.add({
            geometricBounds:    [
                                    config.bounds.top,
                                    config.bounds.left,
                                    config.bounds.bottom,
                                    config.bounds.right
                                ],
            contents:           config.contents[i]
        });

        // get the first paragraph from the textframe
        var p = tf.paragraphs.firstItem();

        // apply a font to it, and color it white or black
        p.properties = {
            // appliedFont:        config.fontFamily,
            fillColor:          config.colors.text
        };

        // change text to uppercase
        p.changecase(ChangecaseMode.UPPERCASE);

        // convert to polygons
        tf.createOutlines();

        // get the polygon and append it to posObj
        var pos = 'pos-' + (i + obsRect);
        posObj[pos] = page.polygons.firstItem();

        // if we don't select the element, it won't work
        // looks like a bug to me?
        // tried without all the stuff here, did work without
        // maybe there is something that will trigger the bug
        // so this is the bugfix for me
        posObj[pos].select();

    } else {

        // add a rectangle to the document
        var line = page.rectangles.add({
            geometricBounds: [
                                config.bounds.top,
                                config.bounds.left,
                                config.bounds.top + config.lineHeight,
                                config.bounds.right
                             ],
            fillColor:       doc.swatches.item(3),
            strokeWeight:    0,
            fillTint:        90
        });

        // add it to posObj
        posObj['pos-' + i] = page.rectangles.firstItem();

        // remove the "---" from the array
        config.contents.splice(i, 1);

        // substract one, so we dont end in an endless loop
        i--;

        // this is the counter for the rectangles,
        // so we have the right "positions" in the posObj
        obsRect++;

    }

}

/*
 * SIZE AND POSITION ALL POLYGONS TO THE RIGHT POSITION
 */

var history = {
    // storage for the y-positions, that will be generated
    height: config.bounds.top
};

// size polygons
for(var i = 0; i < page.polygons.length; i++) {

    // get all polygons on the document
    var p = page.polygons;

    // take the last and count down
    var rev = (page.polygons.length - 1) - i;

    // get the size with the helper-function
    var size = helpers.size(p.item(rev));

    // resize the polygons to the max-width which is possible
    p.item(rev).resize(
        CoordinateSpaces.PAGE_COORDINATES,
        AnchorPoint.CENTER_ANCHOR,
        ResizeMethods.MULTIPLYING_CURRENT_DIMENSIONS_BY,
        [size.multiplier, size.multiplier]
    );

    // if the customization "faded" is on, it will calculate the "right" opacity
    // bigger typos are loud and have less opacity
    // smaller typos are quite and have more opacity
    if(config.customization.faded) {
        p.item(rev).transparencySettings.blendingSettings.properties = {
            blendMode: BlendMode.NORMAL,
            opacity: helpers.threshold(config.colors.sensivity, size.multiplier)
        };
    }

}


// position the objects in the posObj-Object
var groupArr = [];
for(pos in posObj) {
    // get the obj
    var obj = posObj[pos];
    // move it to the right position
    obj.move([config.bounds.left, history.height]);
    // increase the history.height
    history.height += helpers.size(obj).height + config.gutter;
    // add to groupArr < will be our group later
    groupArr.push(posObj[pos]);
}

// deselect all, just in case :p
app.activeDocument.select(NothingEnum.NOTHING);

// add groupArray to a group
page.groups.add(groupArr);

// get the group
var group = page.groups.firstItem();



/*
 * ADDITIONAL CUSTOMIZATIONS
 */

// if shadow is turned on, duplicate group
if(config.customization.shadow) {

    // duplicate the first group
    group.duplicate([
        config.bounds.left + config.shadow.left,
        config.bounds.top + config.shadow.top
    ]).sendBackward();

    // get the new duplicated group
    var duplicated = page.groups.lastItem();

    // fill all polygons to black
    duplicated.polygons.everyItem().properties = {
        fillColor:  doc.swatches.item(1),
        fillTint:   100
    };

    // fill all rectangles to black
    duplicated.rectangles.everyItem().properties = {
        fillColor:  doc.swatches.item(1),
        fillTint:   100
    };

}

// if shear is turned on, shear it with the transformation matrix
if(config.customization.shear) {

    var shear = app.transformationMatrices.add();
    shear = shear.shearMatrix(config.shear.angle);
    group.transform(
        CoordinateSpaces.PASTEBOARD_COORDINATES,
        AnchorPoint.CENTER_ANCHOR,
        shear
    );

    if(config.customization.shadow) {
        duplicated.transform(
            CoordinateSpaces.PASTEBOARD_COORDINATES,
            AnchorPoint.CENTER_ANCHOR,
            shear
        );
    }

}

// if rotation is turned on, rotate it with the transformation matrix
if(config.customization.rotate) {

    var rotate = app.transformationMatrices.add();
    rotate = rotate.rotateMatrix(config.rotate.angle);
    group.transform(
        CoordinateSpaces.PASTEBOARD_COORDINATES,
        AnchorPoint.CENTER_ANCHOR,
        rotate
    );

    if(config.customization.shadow) {
        duplicated.transform(
            CoordinateSpaces.PASTEBOARD_COORDINATES,
            AnchorPoint.CENTER_ANCHOR,
            rotate);
    }

}

if(config.customization.centered) {

    // get the size of the group
    var size = helpers.size(group);

    // move it to the center of the page
    group.move([
        (config.pageWidth / 2) - (size.width / 2),
        (config.pageHeight / 2) - (size.height / 2)
    ]);

    // if there is a shadow, move it too with the right offset
    if(config.customization.shadow) {
        duplicated.move([
            (config.pageWidth / 2) - (size.width / 2) + config.shadow.left,
            (config.pageHeight / 2) - (size.height / 2) + config.shadow.top
        ]);
    }


}

if(config.customization.perspective) {

    var size = helpers.size(group);
    var depth = 70;
    var offset = [50, 50];

    // work in progress, to easily set an offset, which is understandable
    offset[0] = depth - offset[0];
    offset[1] = depth - offset[1];

    // duplicate the first group i-times
    for(var i = 0; i < depth; i++) {
        group.duplicate([
            (config.pageWidth / 2) - (size.width / 2) - (i / offset[0]),
            (config.pageHeight / 2) - (size.height / 2) - (i / offset[1])
        ]).sendBackward();

        // get the new duplicated group
        var duplicated = page.groups.firstItem();

        // set the transparency setting
        duplicated.transparencySettings.blendingSettings.properties = {
            blendMode: BlendMode.NORMAL
            // opacity: (i * 80) / 100
        };

        // subtract from the tint, so it will become "darker"
        duplicated.polygons.everyItem().properties = {
            fillColor: config.colors.background,
            fillTint: 100 - (i / 2)
        };
    }

    // this is the top-layer, which will be the front of the perspective
    group.transparencySettings.blendingSettings.properties = {
        blendMode: BlendMode.NORMAL,
        opacity: 100
    };

    // fill all with white
    group.polygons.everyItem().properties = {
        fillColor: doc.swatches.item(2),
        fillTint: 100
    };

    // move the new generated group to the center of the page
    group.move([
        (config.pageWidth / 2) - (size.width / 2) - (depth / offset[0]),
        (config.pageHeight / 2) - (size.height / 2) - (depth / offset[1])
    ]);

    // the last item will add a white "halo"-effect, i just deleted it, to solve :p
    page.groups.lastItem().transparencySettings.blendingSettings.properties = {
        blendMode: BlendMode.NORMAL,
        opacity: 0
    };

}

// this is what the script will output in the javascript-console :)
"woosh.";
// you reached the end of the script.
// yay.