Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Guide maker to support range of units help

Contributor ,
Jul 25, 2025 Jul 25, 2025

Hello, I am struggling to make this incorportate units say if a user types 20px or 30pt or 60cm etc 

Also it spits out an xml file to the document folder from which it can read past settings a user inputed, this would need to also read say 20px et 

Help on this would be most useful 

Best

Smyth 


 

 

 

SmythWharf_0-1753488068364.png

/**
 * Illustrator Margins, Grid & Baseline Script [Mac-Ready]
 * No ES6 code—fully ExtendScript compatible. Tested on Mac.
 */

var settingsXMLPath = Folder.myDocuments + "/IllustratorGridSettings.xml";

// --- XML functions ---
function loadSettingsFromXML(xmlFilePath, abIdx, defaults) {
    var file = new File(xmlFilePath);
    if (!file.exists) return defaults;
    file.open('r');
    var content = file.read();
    file.close();
    var abTag = new RegExp('<Artboard index="' + abIdx + '">([\\s\\S]*?)<\\/Artboard>');
    var found = content.match(abTag);
    if (found) {
        var vals = {};
        var tags = ["Top","Bottom","Left","Right","Columns","Rows","VertGutter","HorGutter","BaselineStart","BaselineInc"];
        for (var i=0;i<tags.length;i++) {
            var tag = new RegExp('<'+tags[i]+'>((.|\\n|\\r)*?)<\\/'+tags[i]+'>');
            var t = found[1].match(tag);
            vals[tags[i]] = t ? parseFloat(t[1]) : defaults[tags[i]];
        }
        return vals;
    }
    return defaults;
}

function saveSettingsToXML(xmlFilePath, abIdx, newVals) {
    var file = new File(xmlFilePath);
    var xmlContent = '';
    if (file.exists) {
        file.open('r');
        xmlContent = file.read();
        file.close();
    }
    var abTag = new RegExp('<Artboard index="' + abIdx + '">([\\s\\S]*?)<\\/Artboard>','g');
    xmlContent = xmlContent.replace(abTag, '');
    var abXML = '  <Artboard index="' + abIdx + '">\n';
    for (var key in newVals) {
        abXML += '    <' + key + '>' + newVals[key] + '</' + key + '>\n';
    }
    abXML += '  </Artboard>\n';
    if (xmlContent.indexOf('<GridSettings>') === -1) {
        xmlContent = '<GridSettings>\n' + abXML + '</GridSettings>';
    } else {
        xmlContent = xmlContent.replace('</GridSettings>', abXML + '</GridSettings>');
    }
    file.open('w');
    file.write(xmlContent);
    file.close();
}

// --- Helpers ---
function mmToPt(mm) { return mm * 2.83465; }
function ptToMm(pt) { return pt / 2.83465; }
function makeRGB(r, g, b) {
    var color = new RGBColor();
    color.red = r; color.green = g; color.blue = b;
    return color;
}
function findLayer(doc, name) {
    for (var i = 0; i < doc.layers.length; i++) {
        if (doc.layers[i].name === name) return doc.layers[i];
    }
    return null;
}
function deleteLayerByExactName(doc, name) {
    for (var i = doc.layers.length - 1; i >= 0; i--) {
        if (doc.layers[i].name === name) {
            doc.layers[i].locked = false;
            doc.layers[i].visible = true;
            doc.layers[i].remove();
        }
    }
}
function removePathsForArtboard(layer, abIdx) {
    var abTag = " ab" + abIdx + " ";
    for (var i = layer.pathItems.length - 1; i >= 0; i--) {
        var p = layer.pathItems[i];
        if (p.note && p.note.indexOf(abTag) === 0) p.remove();
    }
}

// --- Guide Creation (with per-artboard tagging for merged layers) ---
function addMarginsAndColumnsToLayer(layer, ab, abIdx, top_mm, bottom_mm, left_mm, right_mm, columns, vertGutter_mm, rows, horGutter_mm) {
    var rect = ab.artboardRect;
    var left = rect[0], top = rect[1], right = rect[2], bottom = rect[3];
    var width = right - left, height = top - bottom;
    var top_pt = mmToPt(top_mm), bottom_pt = mmToPt(bottom_mm), left_pt = mmToPt(left_mm), right_pt = mmToPt(right_mm);
    var vertGutter_pt = mmToPt(vertGutter_mm), horGutter_pt = mmToPt(horGutter_mm);
    var abTag = " ab" + abIdx + " ";
    // Margin rectangle
    var path = layer.pathItems.rectangle(
        top - top_pt,
        left + left_pt,
        width - left_pt - right_pt,
        height - top_pt - bottom_pt
    );
    path.stroked = true;
    path.strokeColor = makeRGB(224, 0, 255);
    path.strokeWidth = 0.1;
    path.filled = false;
    path.guides = true;
    path.note = abTag + "margin";
    // Columns as guides
    var gridColor = makeRGB(0, 176, 255);
    if (columns > 1) {
        var usableWidth = width - left_pt - right_pt;
        var colWidth = (usableWidth - (columns - 1) * vertGutter_pt) / columns;
        var x = left + left_pt;
        for (var c = 0; c < columns - 1; c++) {
            x += colWidth;
            var vGuide1 = layer.pathItems.add();
            vGuide1.setEntirePath([[x, top - top_pt], [x, bottom + bottom_pt]]);
            vGuide1.stroked = true; vGuide1.strokeColor = gridColor; vGuide1.strokeWidth = 0.1; vGuide1.filled = false;
            vGuide1.guides = true;
            vGuide1.note = abTag + "colg";
            x += vertGutter_pt;
            var vGuide2 = layer.pathItems.add();
            vGuide2.setEntirePath([[x, top - top_pt], [x, bottom + bottom_pt]]);
            vGuide2.stroked = true; vGuide2.strokeColor = gridColor; vGuide2.strokeWidth = 0.1; vGuide2.filled = false;
            vGuide2.guides = true;
            vGuide2.note = abTag + "colg";
        }
    }
    // Rows as guides
    if (rows > 1) {
        var usableHeight = height - top_pt - bottom_pt;
        var rowHeight = (usableHeight - (rows - 1) * horGutter_pt) / rows;
        var y = top - top_pt;
        for (var r = 0; r < rows - 1; r++) {
            y -= rowHeight;
            var hGuide1 = layer.pathItems.add();
            hGuide1.setEntirePath([[left + left_pt, y], [right - right_pt, y]]);
            hGuide1.stroked = true; hGuide1.strokeColor = gridColor; hGuide1.strokeWidth = 0.1; hGuide1.filled = false;
            hGuide1.guides = true; hGuide1.note = abTag + "rowg";
            y -= horGutter_pt;
            var hGuide2 = layer.pathItems.add();
            hGuide2.setEntirePath([[left + left_pt, y], [right - right_pt, y]]);
            hGuide2.stroked = true; hGuide2.strokeColor = gridColor; hGuide2.strokeWidth = 0.1; hGuide2.filled = false;
            hGuide2.guides = true; hGuide2.note = abTag + "rowg";
        }
    }
}

function addBaselineGridToLayer(layer, ab, abIdx, top_mm, bottom_mm, left_mm, right_mm, baselineStart_mm, baselineInc_pt) {
    var rect = ab.artboardRect;
    var left = rect[0], top = rect[1], right = rect[2], bottom = rect[3];
    var top_pt = mmToPt(top_mm), bottom_pt = mmToPt(bottom_mm), left_pt = mmToPt(left_mm), right_pt = mmToPt(right_mm);
    var baselineStart_pt = mmToPt(baselineStart_mm);
    var abTag = " ab" + abIdx + " ";
    var y0 = top - top_pt - baselineStart_pt;
    var yEnd = bottom + bottom_pt;
    while (y0 > yEnd) {
        var line = layer.pathItems.add();
        line.setEntirePath([[left + left_pt, y0], [right - right_pt, y0]]);
        line.stroked = true;
        line.strokeColor = makeRGB(80, 80, 80);
        line.strokeWidth = 0.05;
        line.filled = false;
        line.guides = true;
        line.note = abTag + "baseline";
        y0 -= baselineInc_pt;
    }
}

// --- UI Dialog (always merged mode) ---
function showMarginsDialog() {
    if (app.documents.length === 0) { alert("Please open a document first."); return; }
    var doc = app.activeDocument;
    var abIdx = doc.artboards.getActiveArtboardIndex();
    var defaults = {
        Top:12.7, Bottom:12.7, Left:12.7, Right:12.7, Columns:2, Rows:2,
        VertGutter:4.233, HorGutter:4.233, BaselineStart:0, BaselineInc:12
    };
    var settings = loadSettingsFromXML(settingsXMLPath, abIdx, defaults);

    var w = new Window("dialog", "Margins, Grid & Baseline (for Artboard " + (abIdx + 1) + ")");
    w.orientation = "column";
    w.alignChildren = "left";
    w.spacing = 10;

    var marginPanel = w.add("panel", undefined, "Margins (mm)");
    marginPanel.orientation = "column";
    marginPanel.alignChildren = "left";
    marginPanel.margins = 12;
    var mRow1 = marginPanel.add("group");
    mRow1.orientation = "row";
    mRow1.alignChildren = "center";
    mRow1.add("statictext", undefined, "Top:");
    var topInput = mRow1.add("edittext", undefined, settings.Top);
    topInput.characters = 5;
    mRow1.add("statictext", undefined, "mm       Left:");
    var leftInput = mRow1.add("edittext", undefined, settings.Left);
    leftInput.characters = 5;
    mRow1.add("statictext", undefined, "mm");
    var mRow2 = marginPanel.add("group");
    mRow2.orientation = "row";
    mRow2.alignChildren = "center";
    mRow2.add("statictext", undefined, "Bottom:");
    var bottomInput = mRow2.add("edittext", undefined, settings.Bottom);
    bottomInput.characters = 5;
    mRow2.add("statictext", undefined, "mm     Right:");
    var rightInput = mRow2.add("edittext", undefined, settings.Right);
    rightInput.characters = 5;
    mRow2.add("statictext", undefined, "mm");

    var gridPanel = w.add("panel", undefined, "Grid");
    gridPanel.orientation = "row";
    gridPanel.alignChildren = "center";
    gridPanel.margins = 12;
    gridPanel.spacing = 10;
    gridPanel.add("statictext", undefined, "Columns:");
    var colInput = gridPanel.add("edittext", undefined, settings.Columns);
    colInput.characters = 3;
    gridPanel.add("statictext", undefined, "Vertical Gutter:");
    var vertGutterInput = gridPanel.add("edittext", undefined, settings.VertGutter);
    vertGutterInput.characters = 5;
    gridPanel.add("statictext", undefined, "mm");
    gridPanel.add("statictext", undefined, "Rows:");
    var rowInput = gridPanel.add("edittext", undefined, settings.Rows);
    rowInput.characters = 3;
    gridPanel.add("statictext", undefined, "Horizontal Gutter:");
    var horGutterInput = gridPanel.add("edittext", undefined, settings.HorGutter);
    horGutterInput.characters = 5;
    gridPanel.add("statictext", undefined, "mm");

    var basePanel = w.add("panel", undefined, "Baseline Grid");
    basePanel.orientation = "row";
    basePanel.alignChildren = "center";
    basePanel.margins = 12;
    basePanel.spacing = 10;
    basePanel.add("statictext", undefined, "Start:");
    var baselineStartInput = basePanel.add("edittext", undefined, settings.BaselineStart);
    baselineStartInput.characters = 5;
    basePanel.add("statictext", undefined, "mm     Increment:");
    var baselineIncInput = basePanel.add("edittext", undefined, settings.BaselineInc);
    baselineIncInput.characters = 5;
    basePanel.add("statictext", undefined, "pt");

    var applyAllGroup = w.add("group");
    applyAllGroup.orientation = "row";
    var applyAllChk = applyAllGroup.add("checkbox", undefined, "Apply to all artboards");
    applyAllChk.value = (doc.artboards.length === 1);

    var btnGroup = w.add("group");
    btnGroup.orientation = "row";
    btnGroup.alignment = "right";
    btnGroup.add("button", undefined, "Cancel", { name: "cancel" });
    var okBtn = btnGroup.add("button", undefined, "OK", { name: "ok" });

    okBtn.onClick = function() {
        var top = parseFloat(topInput.text), bottom = parseFloat(bottomInput.text),
            leftVal = parseFloat(leftInput.text), rightVal = parseFloat(rightInput.text),
            cols = parseInt(colInput.text), rows = parseInt(rowInput.text),
            vertGutter = parseFloat(vertGutterInput.text), horGutter = parseFloat(horGutterInput.text),
            baselineStart = parseFloat(baselineStartInput.text), baselineInc = parseFloat(baselineIncInput.text);

        if (
            isNaN(top) || isNaN(bottom) || isNaN(leftVal) || isNaN(rightVal) ||
            isNaN(cols) || isNaN(rows) ||
            isNaN(vertGutter) || isNaN(horGutter) ||
            isNaN(baselineStart) || isNaN(baselineInc) ||
            top < 0 || bottom < 0 || leftVal < 0 || rightVal < 0 ||
            cols < 0 || rows < 0 ||
            vertGutter < 0 || horGutter < 0 ||
            baselineInc <= 0
        ) {
            alert("Please enter valid (positive) numbers. Columns and Rows and gutters can be 0 or more, Increment > 0.");
            return;
        }
        function checkGridFit(abIdx, top, bottom, leftVal, rightVal, cols, rows, vertGutter, horGutter) {
            var rect = doc.artboards[abIdx].artboardRect;
            var artboardW = rect[2] - rect[0] - mmToPt(leftVal) - mmToPt(rightVal);
            var artboardHmm = rect[1] - rect[3] - mmToPt(top) - mmToPt(bottom);
            var mmW = ptToMm(artboardW), mmH = ptToMm(artboardHmm);
            var minColWidth = 1, minRowHeight = 1;
            var failCol = false, failRow = false;
            if (cols > 1) {
                var totalColGutters = (cols - 1) * vertGutter;
                var usable = mmW - totalColGutters;
                if (usable <= 0 || (usable/cols) < minColWidth) { failCol = true; }
            }
            if (rows > 1) {
                var totalRowGutters = (rows - 1) * horGutter;
                var usable = mmH - totalRowGutters;
                if (usable <= 0 || (usable/rows) < minRowHeight) { failRow = true; }
            }
            return {failCol: failCol, failRow: failRow};
        }
        var abIndices = [];
        if (applyAllChk.value) {
            for (var i = 0; i < doc.artboards.length; i++) abIndices.push(i);
        } else {
            abIndices.push(abIdx);
        }
        var vertGutterOverride = {}, horGutterOverride = {};
        var warnedCol = false, warnedRow = false;
        for (var i=0; i<abIndices.length; i++) {
            var check = checkGridFit(abIndices[i], top, bottom, leftVal, rightVal, cols, rows, vertGutter, horGutter);
            if (check.failCol) {
                if (!warnedCol) alert("Your column/gutter combination cannot fit on at least one artboard. Vertical gutter is being reset to the default.");
                vertGutterOverride[abIndices[i]] = defaults.VertGutter;
                warnedCol = true;
            }
            if (check.failRow) {
                if (!warnedRow) alert("Your row/gutter combination cannot fit on at least one artboard. Horizontal gutter is being reset to the default.");
                horGutterOverride[abIndices[i]] = defaults.HorGutter;
                warnedRow = true;
            }
        }

        // --- Always merge guides into single layers ---
        if (applyAllChk.value) {
            deleteLayerByExactName(doc, "Margins and Columns");
            deleteLayerByExactName(doc, "Baseline");
        }
        var marginsLayer = findLayer(doc, "Margins and Columns");
        if (!marginsLayer) { marginsLayer = doc.layers.add(); marginsLayer.name = "Margins and Columns"; }
        marginsLayer.locked = false; marginsLayer.visible = true;
        var baselineLayer = findLayer(doc, "Baseline");
        if (!baselineLayer) { baselineLayer = doc.layers.add(); baselineLayer.name = "Baseline"; }
        baselineLayer.locked = false; baselineLayer.visible = true;

        for (var k = 0; k < abIndices.length; k++) {
            var idx = abIndices[k];
            var thisVertGutter = vertGutterOverride[idx] !== undefined ? vertGutterOverride[idx] : vertGutter;
            var thisHorGutter = horGutterOverride[idx] !== undefined ? horGutterOverride[idx] : horGutter;
            var ab = doc.artboards[idx];
            if (applyAllChk.value) {
                // Already wiped, so no selective deletion required
            } else {
                removePathsForArtboard(marginsLayer, idx);
                removePathsForArtboard(baselineLayer, idx);
            }
            addMarginsAndColumnsToLayer(marginsLayer, ab, idx, top, bottom, leftVal, rightVal, cols, thisVertGutter, rows, thisHorGutter);
            addBaselineGridToLayer(baselineLayer, ab, idx, top, bottom, leftVal, rightVal, baselineStart, baselineInc);
            saveSettingsToXML(settingsXMLPath, idx, {
                Top: top, Bottom: bottom, Left: leftVal, Right: rightVal,
                Columns: cols, Rows: rows, VertGutter: thisVertGutter, HorGutter: thisHorGutter,
                BaselineStart: baselineStart, BaselineInc: baselineInc
            });
        }
        marginsLayer.locked = true; baselineLayer.locked = true;

        w.close();
    };
    w.center(); w.show();
}

showMarginsDialog();
TOPICS
How-to , Scripting , SDK , Tools
103
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe
Community Expert ,
Jul 25, 2025 Jul 25, 2025

UnitValue can be used for unit conversion. For detailed usage, refer to JavaScript Tools Guide.pdf.

var ptValue = new UnitValue(10, 'mm').as('pt') ;
alert(ptValue) ;

 

This may be unnecessary advice, but ExtendScript can parse XML and search with XPath, so it may be easier than using regular expressions.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Jul 26, 2025 Jul 26, 2025
LATEST

Ah okay, 

I ended up getting to here 

/**
 * Illustrator Margins, Grid & Baseline Script [Mac-Ready]
 * No ES6 code—fully ExtendScript compatible. Tested on Mac.
 */

var settingsXMLPath = Folder.myDocuments + "/IllustratorGridSettings.xml";

// --- XML functions (Modified to handle raw string values) ---
function loadSettingsFromXML(xmlFilePath, abIdx, defaults) {
    // Helper to convert default numbers to strings with units
    function stringifyDefaults(defaultsObj) {
        var stringDefaults = {};
        for (var key in defaultsObj) {
            if (defaultsObj.hasOwnProperty(key)) {
                if (key === "BaselineInc") {
                    // Baseline increment defaults to points
                    stringDefaults[key] = defaultsObj[key].toString() + "pt";
                } else if (key === "Columns" || key === "Rows") {
                    // Numbers only, no units
                    stringDefaults[key] = defaultsObj[key].toString();
                } else {
                    // Everything else defaults to mm
                    stringDefaults[key] = defaultsObj[key].toString() + "mm";
                }
            }
        }
        return stringDefaults;
    }

    var file = new File(xmlFilePath);
    if (!file.exists) return stringifyDefaults(defaults);
    
    file.open('r');
    var content = file.read();
    file.close();
    
    var abTag = new RegExp('<Artboard index="' + abIdx + '">([\\s\\S]*?)<\\/Artboard>');
    var found = content.match(abTag);
    
    if (found) {
        var vals = {};
        var tags = ["Top","Bottom","Left","Right","Columns","Rows","VertGutter","HorGutter","BaselineStart","BaselineInc"];
        for (var i = 0; i < tags.length; i++) {
            var tagName = tags[i];
            var tagRegex = new RegExp('<' + tagName + '>([\\s\\S]*?)<\\/' + tagName + '>');
            var tagMatch = found[1].match(tagRegex);
            
            if (tagMatch) {
                vals[tagName] = tagMatch[1];
            } else {
                // Apply default with appropriate unit
                if (tagName === "BaselineInc") {
                    vals[tagName] = defaults[tagName].toString() + "pt";
                } else if (tagName === "Columns" || tagName === "Rows") {
                    vals[tagName] = defaults[tagName].toString();
                } else {
                    vals[tagName] = defaults[tagName].toString() + "mm";
                }
            }
        }
        return vals;
    }
    
    return stringifyDefaults(defaults);
}

function saveSettingsToXML(xmlFilePath, abIdx, newVals) {
    var file = new File(xmlFilePath);
    var xmlContent = '';
    if (file.exists) {
        file.open('r');
        xmlContent = file.read();
        file.close();
    }
    var abTag = new RegExp('<Artboard index="' + abIdx + '">([\\s\\S]*?)<\\/Artboard>','g');
    xmlContent = xmlContent.replace(abTag, '');
    var abXML = '  <Artboard index="' + abIdx + '">\n';
    for (var key in newVals) {
        var value = newVals[key].toString().replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
        abXML += '    <' + key + '>' + value + '</' + key + '>\n';
    }
    abXML += '  </Artboard>\n';
    if (xmlContent.indexOf('<GridSettings>') === -1) {
        xmlContent = '<GridSettings>\n' + abXML + '</GridSettings>';
    } else {
        xmlContent = xmlContent.replace('</GridSettings>', abXML + '</GridSettings>');
    }
    file.open('w');
    file.write(xmlContent);
    file.close();
}

// --- Helpers ---
function mmToPt(mm) { return mm * 2.83465; }
function ptToMm(pt) { return pt / 2.83465; }
function makeRGB(r, g, b) {
    var color = new RGBColor();
    color.red = r; color.green = g; color.blue = b;
    return color;
}
function findLayer(doc, name) {
    for (var i = 0; i < doc.layers.length; i++) {
        if (doc.layers[i].name === name) return doc.layers[i];
    }
    return null;
}
function deleteLayerByExactName(doc, name) {
    for (var i = doc.layers.length - 1; i >= 0; i--) {
        if (doc.layers[i].name === name) {
            doc.layers[i].locked = false;
            doc.layers[i].visible = true;
            doc.layers[i].remove();
        }
    }
}
function removePathsForArtboard(layer, abIdx) {
    var abTag = " ab" + abIdx + " ";
    for (var i = layer.pathItems.length - 1; i >= 0; i--) {
        var p = layer.pathItems[i];
        if (p.note && p.note.indexOf(abTag) === 0) p.remove();
    }
}

// --- Enhanced Unit Parsing Functions with Default Unit Addition ---
function parseValueWithUnit(inputStr) {
    if (typeof inputStr !== 'string') {
        inputStr = String(inputStr);
    }
    
    inputStr = inputStr.replace(/\s/g, '');
    var match = inputStr.match(/^(-?\d*\.?\d+)([a-zA-Z%]*)$/);
    
    if (!match) {
        var num = parseFloat(inputStr);
        return isNaN(num) ? NaN : mmToPt(num);
    }
    
    var value = parseFloat(match[1]);
    var unit = match[2].toLowerCase();
    
    if (isNaN(value)) return NaN;
    
    switch (unit) {
        case '':
        case 'mm':
            return mmToPt(value);
        case 'cm':
            return mmToPt(value * 10);
        case 'in':
        case 'inch':
            return value * 72;
        case 'pt':
        case 'point':
            return value;
        case 'px':
        case 'pixel':
            var doc = app.activeDocument;
            if (doc.rulerUnits == RulerUnits.Pixels) {
                return value;
            } else {
                return value * 0.75;
            }
        case 'pc':
        case 'pica':
            return value * 12;
        default:
            return mmToPt(value);
    }
}

function parseValueWithUnitPt(inputStr) {
    return parseValueWithUnit(inputStr);
}

// --- Helper function to add default units to user input ---
function addDefaultUnit(inputStr, fieldType) {
    if (typeof inputStr !== 'string') {
        inputStr = String(inputStr);
    }
    
    inputStr = inputStr.replace(/\s/g, '');
    var match = inputStr.match(/^(-?\d*\.?\d+)([a-zA-Z%]*)$/);
    
    if (match && match[2] === '') {
        // No unit specified, add appropriate default
        if (fieldType === "baseline") {
            return inputStr + "pt";
        } else if (fieldType === "number") {
            return inputStr; // Columns/Rows don't need units
        } else {
            return inputStr + "mm"; // Default for margins and gutters
        }
    }
    
    // Unit already specified or invalid input, return as-is
    return inputStr;
}

// --- Guide Creation (works directly with points) ---
function addMarginsAndColumnsToLayer(layer, ab, abIdx, top_pt, bottom_pt, left_pt, right_pt, columns, vertGutter_pt, rows, horGutter_pt) {
    var rect = ab.artboardRect;
    var left = rect[0], top = rect[1], right = rect[2], bottom = rect[3];
    var width = right - left, height = top - bottom;
    var abTag = " ab" + abIdx + " ";
    
    var path = layer.pathItems.rectangle(top - top_pt, left + left_pt, width - left_pt - right_pt, height - top_pt - bottom_pt);
    path.stroked = true; path.strokeColor = makeRGB(224, 0, 255); path.strokeWidth = 0.1; path.filled = false; path.guides = true; path.note = abTag + "margin";
    
    var gridColor = makeRGB(0, 176, 255);
    if (columns > 1) {
        var usableWidth = width - left_pt - right_pt;
        var colWidth = (usableWidth - (columns - 1) * vertGutter_pt) / columns;
        var x = left + left_pt;
        for (var c = 0; c < columns - 1; c++) {
            x += colWidth;
            var vGuide1 = layer.pathItems.add();
            vGuide1.setEntirePath([[x, top - top_pt], [x, bottom + bottom_pt]]);
            vGuide1.stroked = true; vGuide1.strokeColor = gridColor; vGuide1.strokeWidth = 0.1; vGuide1.filled = false; vGuide1.guides = true; vGuide1.note = abTag + "colg";
            x += vertGutter_pt;
            var vGuide2 = layer.pathItems.add();
            vGuide2.setEntirePath([[x, top - top_pt], [x, bottom + bottom_pt]]);
            vGuide2.stroked = true; vGuide2.strokeColor = gridColor; vGuide2.strokeWidth = 0.1; vGuide2.filled = false; vGuide2.guides = true; vGuide2.note = abTag + "colg";
        }
    }
    
    if (rows > 1) {
        var usableHeight = height - top_pt - bottom_pt;
        var rowHeight = (usableHeight - (rows - 1) * horGutter_pt) / rows;
        var y = top - top_pt;
        for (var r = 0; r < rows - 1; r++) {
            y -= rowHeight;
            var hGuide1 = layer.pathItems.add();
            hGuide1.setEntirePath([[left + left_pt, y], [right - right_pt, y]]);
            hGuide1.stroked = true; hGuide1.strokeColor = gridColor; hGuide1.strokeWidth = 0.1; hGuide1.filled = false; hGuide1.guides = true; hGuide1.note = abTag + "rowg";
            y -= horGutter_pt;
            var hGuide2 = layer.pathItems.add();
            hGuide2.setEntirePath([[left + left_pt, y], [right - right_pt, y]]);
            hGuide2.stroked = true; hGuide2.strokeColor = gridColor; hGuide2.strokeWidth = 0.1; hGuide2.filled = false; hGuide2.guides = true; hGuide2.note = abTag + "rowg";
        }
    }
}

// --- ENHANCED: Baseline Grid Creation (now handles 0 increment gracefully) ---
function addBaselineGridToLayer(layer, ab, abIdx, top_pt, bottom_pt, left_pt, right_pt, baselineStart_pt, baselineInc_pt) {
    // If baseline increment is 0, skip creating baseline guides entirely
    if (baselineInc_pt <= 0) {
        return; // Exit function without creating any baseline guides
    }
    
    var rect = ab.artboardRect;
    var left = rect[0], top = rect[1], right = rect[2], bottom = rect[3];
    var abTag = " ab" + abIdx + " ";
    var y0 = top - top_pt - baselineStart_pt;
    var yEnd = bottom + bottom_pt;
    while (y0 > yEnd) {
        var line = layer.pathItems.add();
        line.setEntirePath([[left + left_pt, y0], [right - right_pt, y0]]);
        line.stroked = true; line.strokeColor = makeRGB(80, 80, 80); line.strokeWidth = 0.05; line.filled = false; line.guides = true; line.note = abTag + "baseline";
        y0 -= baselineInc_pt;
    }
}

// --- UI Dialog (IMPROVED LAYOUT) ---
function showMarginsDialog() {
    if (app.documents.length === 0) { alert("Please open a document first."); return; }
    var doc = app.activeDocument;
    var abIdx = doc.artboards.getActiveArtboardIndex();
    var defaults = {
        Top:12.7, Bottom:12.7, Left:12.7, Right:12.7, Columns:2, Rows:2,
        VertGutter:4.233, HorGutter:4.233, BaselineStart:0, BaselineInc:12
    };
    var settings = loadSettingsFromXML(settingsXMLPath, abIdx, defaults);

    // Create a wider dialog with better spacing
    var w = new Window("dialog", "Margins, Grid & Baseline (for Artboard " + (abIdx + 1) + ")");
    w.orientation = "column";
    w.alignChildren = "fill";
    w.spacing = 15;
    w.margins = 20;

    // MARGINS PANEL - wider layout with more space
    var marginPanel = w.add("panel", undefined, "Margins");
    marginPanel.orientation = "column";
    marginPanel.alignChildren = "fill";
    marginPanel.margins = 15;
    marginPanel.spacing = 10;
    
    var mRow1 = marginPanel.add("group");
    mRow1.orientation = "row";
    mRow1.alignChildren = "center";
    mRow1.spacing = 10;
    mRow1.add("statictext", undefined, "Top:").preferredSize.width = 60;
    var topInput = mRow1.add("edittext", undefined, settings.Top);
    topInput.preferredSize.width = 80;
    mRow1.add("statictext", undefined, "Left:").preferredSize.width = 60;
    var leftInput = mRow1.add("edittext", undefined, settings.Left);
    leftInput.preferredSize.width = 80;
    
    var mRow2 = marginPanel.add("group");
    mRow2.orientation = "row";
    mRow2.alignChildren = "center";
    mRow2.spacing = 10;
    mRow2.add("statictext", undefined, "Bottom:").preferredSize.width = 60;
    var bottomInput = mRow2.add("edittext", undefined, settings.Bottom);
    bottomInput.preferredSize.width = 80;
    mRow2.add("statictext", undefined, "Right:").preferredSize.width = 60;
    var rightInput = mRow2.add("edittext", undefined, settings.Right);
    rightInput.preferredSize.width = 80;

    // GRID PANEL - better organized layout
    var gridPanel = w.add("panel", undefined, "Grid");
    gridPanel.orientation = "column";
    gridPanel.alignChildren = "fill";
    gridPanel.margins = 15;
    gridPanel.spacing = 10;
    
    var gRow1 = gridPanel.add("group");
    gRow1.orientation = "row";
    gRow1.alignChildren = "center";
    gRow1.spacing = 10;
    gRow1.add("statictext", undefined, "Columns:").preferredSize.width = 80;
    var colInput = gRow1.add("edittext", undefined, settings.Columns);
    colInput.preferredSize.width = 60;
    gRow1.add("statictext", undefined, "Vertical Gutter:").preferredSize.width = 100;
    var vertGutterInput = gRow1.add("edittext", undefined, settings.VertGutter);
    vertGutterInput.preferredSize.width = 80;
    
    var gRow2 = gridPanel.add("group");
    gRow2.orientation = "row";
    gRow2.alignChildren = "center";
    gRow2.spacing = 10;
    gRow2.add("statictext", undefined, "Rows:").preferredSize.width = 80;
    var rowInput = gRow2.add("edittext", undefined, settings.Rows);
    rowInput.preferredSize.width = 60;
    gRow2.add("statictext", undefined, "Horizontal Gutter:").preferredSize.width = 100;
    var horGutterInput = gRow2.add("edittext", undefined, settings.HorGutter);
    horGutterInput.preferredSize.width = 80;

    // BASELINE PANEL - cleaner layout
    var basePanel = w.add("panel", undefined, "Baseline Grid");
    basePanel.orientation = "row";
    basePanel.alignChildren = "center";
    basePanel.margins = 15;
    basePanel.spacing = 10;
    basePanel.add("statictext", undefined, "Start:").preferredSize.width = 80;
    var baselineStartInput = basePanel.add("edittext", undefined, settings.BaselineStart);
    baselineStartInput.preferredSize.width = 80;
    basePanel.add("statictext", undefined, "Increment:").preferredSize.width = 80;
    var baselineIncInput = basePanel.add("edittext", undefined, settings.BaselineInc);
    baselineIncInput.preferredSize.width = 80;

    // APPLY TO ALL CHECKBOX
    var applyAllGroup = w.add("group");
    applyAllGroup.margins = [0, 10, 0, 0];
    var applyAllChk = applyAllGroup.add("checkbox", undefined, "Apply to all artboards");
    applyAllChk.value = (doc.artboards.length === 1);

    // BUTTONS - better spacing
    var btnGroup = w.add("group");
    btnGroup.orientation = "row";
    btnGroup.alignment = "right";
    btnGroup.spacing = 10;
    btnGroup.margins = [0, 15, 0, 0];
    var cancelBtn = btnGroup.add("button", undefined, "Cancel", { name: "cancel" });
    var okBtn = btnGroup.add("button", undefined, "OK", { name: "ok" });
    cancelBtn.preferredSize.width = 80;
    okBtn.preferredSize.width = 80;

    okBtn.onClick = function() {
        var top_pt = parseValueWithUnit(topInput.text), bottom_pt = parseValueWithUnit(bottomInput.text),
            left_pt = parseValueWithUnit(leftInput.text), right_pt = parseValueWithUnit(rightInput.text),
            cols = parseInt(colInput.text), rows = parseInt(rowInput.text),
            vertGutter_pt = parseValueWithUnit(vertGutterInput.text), horGutter_pt = parseValueWithUnit(horGutterInput.text),
            baselineStart_pt = parseValueWithUnit(baselineStartInput.text), baselineInc_pt = parseValueWithUnitPt(baselineIncInput.text);

        // --- ENHANCED VALIDATION: Allow 0 for baseline increment ---
        if (isNaN(top_pt) || isNaN(bottom_pt) || isNaN(left_pt) || isNaN(right_pt) || 
            isNaN(cols) || isNaN(rows) || 
            isNaN(vertGutter_pt) || isNaN(horGutter_pt) || 
            isNaN(baselineStart_pt) || isNaN(baselineInc_pt) ||
            top_pt < 0 || bottom_pt < 0 || left_pt < 0 || right_pt < 0 ||
            cols < 0 || rows < 0 ||
            vertGutter_pt < 0 || horGutter_pt < 0 ||
            baselineInc_pt < 0) { // Changed from baselineInc_pt <= 0 to baselineInc_pt < 0
            alert("Please enter valid (non-negative) numbers. Baseline increment can be 0 to skip baseline grid.");
            return;
        }
        
        function checkGridFit(abIdx, t_pt, b_pt, l_pt, r_pt, c, r, vg_pt, hg_pt) {
            var rect = doc.artboards[abIdx].artboardRect;
            var artW = rect[2] - rect[0] - l_pt - r_pt; var artH = rect[1] - rect[3] - t_pt - b_pt;
            var minW = mmToPt(1), minH = mmToPt(1);
            var failCol = false, failRow = false;
            if (c > 1) {
                var gutW = (c - 1) * vg_pt;
                if (artW <= gutW || (artW - gutW)/c < minW) { failCol = true; }
            }
            if (r > 1) {
                var gutH = (r - 1) * hg_pt;
                if (artH <= gutH || (artH - gutH)/r < minH) { failRow = true; }
            }
            return {failCol: failCol, failRow: failRow};
        }
        
        var abIndices = [];
        if (applyAllChk.value) { for (var i = 0; i < doc.artboards.length; i++) abIndices.push(i); } else { abIndices.push(abIdx); }
        
        var vertGutterOverride = {}, horGutterOverride = {};
        var warnedCol = false, warnedRow = false;
        for (var i = 0; i < abIndices.length; i++) {
            var check = checkGridFit(abIndices[i], top_pt, bottom_pt, left_pt, right_pt, cols, rows, vertGutter_pt, horGutter_pt);
            if (check.failCol) {
                if (!warnedCol) { alert("Column/gutter combination doesn't fit on at least one artboard. Vertical gutter is reset to default."); warnedCol = true; }
                vertGutterOverride[abIndices[i]] = mmToPt(defaults.VertGutter);
            }
            if (check.failRow) {
                if (!warnedRow) { alert("Row/gutter combination doesn't fit on at least one artboard. Horizontal gutter is reset to default."); warnedRow = true; }
                horGutterOverride[abIndices[i]] = mmToPt(defaults.HorGutter);
            }
        }

        // --- ENHANCED LAYER MANAGEMENT: Handle baseline layer conditionally ---
        if (applyAllChk.value) {
            deleteLayerByExactName(doc, "Margins and Columns");
            // Only delete baseline layer if we're going to recreate it
            if (baselineInc_pt > 0) {
                deleteLayerByExactName(doc, "Baseline");
            }
        }
        
        var marginsLayer = findLayer(doc, "Margins and Columns");
        if (!marginsLayer) { marginsLayer = doc.layers.add(); marginsLayer.name = "Margins and Columns"; }
        marginsLayer.locked = false; marginsLayer.visible = true;
        
        // Only create baseline layer if we need it (baselineInc_pt > 0)
        var baselineLayer = null;
        if (baselineInc_pt > 0) {
            baselineLayer = findLayer(doc, "Baseline");
            if (!baselineLayer) { baselineLayer = doc.layers.add(); baselineLayer.name = "Baseline"; }
            baselineLayer.locked = false; baselineLayer.visible = true;
        }

        for (var k = 0; k < abIndices.length; k++) {
            var idx = abIndices[k];
            var thisVertGutter_pt = vertGutterOverride[idx] !== undefined ? vertGutterOverride[idx] : vertGutter_pt;
            var thisHorGutter_pt = horGutterOverride[idx] !== undefined ? horGutterOverride[idx] : horGutter_pt;
            var ab = doc.artboards[idx];
            
            if (!applyAllChk.value) {
                removePathsForArtboard(marginsLayer, idx);
                // Only clean baseline layer if it exists
                if (baselineLayer) {
                    removePathsForArtboard(baselineLayer, idx);
                }
            }
            
            // Always create margins and columns
            addMarginsAndColumnsToLayer(marginsLayer, ab, idx, top_pt, bottom_pt, left_pt, right_pt, cols, thisVertGutter_pt, rows, thisHorGutter_pt);
            
            // Only create baseline grid if increment > 0 and layer exists
            if (baselineInc_pt > 0 && baselineLayer) {
                addBaselineGridToLayer(baselineLayer, ab, idx, top_pt, bottom_pt, left_pt, right_pt, baselineStart_pt, baselineInc_pt);
            }
            
            // --- SAVE LOGIC REMAINS THE SAME ---
            var valuesToSave = {
                Top: addDefaultUnit(topInput.text, "margin"),
                Bottom: addDefaultUnit(bottomInput.text, "margin"),
                Left: addDefaultUnit(leftInput.text, "margin"),
                Right: addDefaultUnit(rightInput.text, "margin"),
                Columns: addDefaultUnit(colInput.text, "number"),
                Rows: addDefaultUnit(rowInput.text, "number"),
                VertGutter: addDefaultUnit(vertGutterInput.text, "gutter"),
                HorGutter: addDefaultUnit(horGutterInput.text, "gutter"),
                BaselineStart: addDefaultUnit(baselineStartInput.text, "margin"),
                BaselineInc: addDefaultUnit(baselineIncInput.text, "baseline")
            };
            
            if (vertGutterOverride[idx] !== undefined) { 
                valuesToSave.VertGutter = defaults.VertGutter.toString() + "mm"; 
            }
            if (horGutterOverride[idx] !== undefined) { 
                valuesToSave.HorGutter = defaults.HorGutter.toString() + "mm"; 
            }
            
            saveSettingsToXML(settingsXMLPath, idx, valuesToSave);
        }
        
        // Lock layers that were created
        marginsLayer.locked = true;
        if (baselineLayer) {
            baselineLayer.locked = true;
        }
        
        w.close();
    };
    
    w.center(); 
    w.show();
}

showMarginsDialog();
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines