Skip to main content
Known Participant
July 26, 2025
Question

Guide maker to support range of units help

  • July 26, 2025
  • 1 reply
  • 159 views

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 


 

 

 

/**
 * 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();

1 reply

Legend
July 26, 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.

Known Participant
July 26, 2025

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();