


#target photoshop
function generateDimensionsWithMarqueeSelection() {
if (!app.documents.length) {
alert("សូមបើក Document ជាមុនសិន!");
return;
}
var doc = app.activeDocument;
var startRulerUnits = app.preferences.rulerUnits;
try {
var dpi = doc.resolution;
// Helper: បំប្លែង Point (1/72 inch) ទៅ Canvas Pixel
function getCanvasPixel(anchorVal) {
var num = 0;
if (typeof anchorVal === "object" && anchorVal !== null) {
if (typeof anchorVal.as === "function") return anchorVal.as('px');
if ("value" in anchorVal) num = Number(anchorVal.value);
} else {
num = Number(anchorVal);
}
return (num / 72.0) * dpi;
}
// ១. ពិនិត្យមើល Rectangular Marquee Selection (បើមាន)
var hasSelection = false;
var selBounds = null; // [left, top, right, bottom] ជា pixels
try {
var sb = doc.selection.bounds;
selBounds = [sb[0].as('px'), sb[1].as('px'), sb[2].as('px'), sb[3].as('px')];
hasSelection = true;
} catch (e) {
hasSelection = false;
}
// Helper: ពិនិត្យមើលថាតើចំណុចកណ្ដាលនៃខ្សែបន្ទាត់ស្ថិតក្នុង Selection ឬទេ
function isPointInSelection(px, py, bounds) {
if (!bounds) return true;
return (px >= bounds[0] && px <= bounds[2] && py >= bounds[1] && py <= bounds[3]);
}
// ២. ស្វែងរក Path/Line ទាំងអស់ក្នុង Document
var targetPaths = [];
try {
if (doc.activePathItem) {
targetPaths.push(doc.activePathItem);
}
} catch (e) {}
if (targetPaths.length === 0 && doc.pathItems.length > 0) {
for (var p = 0; p < doc.pathItems.length; p++) {
targetPaths.push(doc.pathItems[p]);
}
}
if (targetPaths.length === 0) {
return;
}
// ៣. ស្រង់ទិន្នន័យពី Path និង Filter តាម Marquee Selection
var rawSegments = [];
for (var tp = 0; tp < targetPaths.length; tp++) {
var pathItem = targetPaths[tp];
if (!pathItem.subPathItems) continue;
for (var s = 0; s < pathItem.subPathItems.length; s++) {
var subPath = pathItem.subPathItems[s];
var pts = subPath.pathPoints;
if (!pts || pts.length < 2) continue;
var count = subPath.closed ? pts.length : pts.length - 1;
for (var i = 0; i < count; i++) {
var p1 = pts[i].anchor;
var p2 = pts[(i + 1) % pts.length].anchor;
var x1 = getCanvasPixel(p1[0]);
var y1 = getCanvasPixel(p1[1]);
var x2 = getCanvasPixel(p2[0]);
var y2 = getCanvasPixel(p2[1]);
var midX = (x1 + x2) / 2;
var midY = (y1 + y2) / 2;
// បើមាន Selection ត្រូវពិនិត្យថាតើខ្សែស្ថិតក្នុង Selection ដែរឬទេ
if (hasSelection && !isPointInSelection(midX, midY, selBounds)) {
continue;
}
var dx = x2 - x1;
var dy = y2 - y1;
var distPx = Math.sqrt(dx * dx + dy * dy);
// រំលងជ្រុងខ្លីៗ (< 15px)
if (distPx < 15) continue;
rawSegments.push({ x1: x1, y1: y1, x2: x2, y2: y2, distPx: distPx });
}
}
}
// ៤. ប្រព័ន្ធលុបខ្សែស្របស្ទួន (Deduplication) ធានាចេញអក្សរតែ ១ គត់
var cleanSegments = [];
function isDuplicate(newSeg, existingList) {
var midX1 = (newSeg.x1 + newSeg.x2) / 2;
var midY1 = (newSeg.y1 + newSeg.y2) / 2;
var ang1 = Math.atan2(newSeg.y2 - newSeg.y1, newSeg.x2 - newSeg.x1) * (180 / Math.PI);
if (ang1 < 0) ang1 += 180;
for (var e = 0; e < existingList.length; e++) {
var ex = existingList[e];
var midX2 = (ex.x1 + ex.x2) / 2;
var midY2 = (ex.y1 + ex.y2) / 2;
var ang2 = Math.atan2(ex.y2 - ex.y1, ex.x2 - ex.x1) * (180 / Math.PI);
if (ang2 < 0) ang2 += 180;
var dMid = Math.sqrt((midX1 - midX2)*(midX1 - midX2) + (midY1 - midY2)*(midY1 - midY2));
var dAng = Math.abs(ang1 - ang2);
if (dAng > 90) dAng = 180 - dAng;
var lenDiff = Math.abs(newSeg.distPx - ex.distPx);
if (dMid < 35 && dAng < 10 && lenDiff < 30) {
return true;
}
}
return false;
}
for (var r = 0; r < rawSegments.length; r++) {
if (!isDuplicate(rawSegments[r], cleanSegments)) {
cleanSegments.push(rawSegments[r]);
}
}
if (cleanSegments.length === 0) {
alert(hasSelection ? "មិនមានខ្សែបន្ទាត់ នៅក្នុងតំបន់ Selection ទេ!" : "រកមិនឃើញ ខ្សែបន្ទាត់ ត្រឹមត្រូវទេ!");
return;
}
// ៥. បង្កើត UI Dialog ជាមួយ ពុម្ពអក្សរ Khmer OS
var titleText = hasSelection ? "Marquee Selected Dimensions Tool" : "Single Text Dimension Tool";
var win = new Window("dialog", titleText, undefined, {resizeable: false});
win.orientation = "column";
win.alignChildren = ["fill", "top"];
win.spacing = 10;
win.margins = 12;
var scaleGroup = win.add("group");
scaleGroup.add("statictext", undefined, "Scale:");
var scaleDropdown = scaleGroup.add("dropdownlist", undefined, ["1:1", "1:10", "1:100"]);
scaleDropdown.selection = 0;
var decGroup = win.add("group");
decGroup.add("statictext", undefined, "ខ្ទង់ក្បៀស:");
var decDropdown = decGroup.add("dropdownlist", undefined, ["00.0 cm", "00.00 cm"]);
decDropdown.selection = 1;
var chkShowAngle = win.add("checkbox", undefined, "បង្ហាញ មុំ Angle (°)");
chkShowAngle.value = true;
var fontGroup = win.add("group");
fontGroup.add("statictext", undefined, "Font Size (pt):");
var txtFontSize = fontGroup.add("edittext", undefined, "42");
txtFontSize.characters = 5;
var offsetGroup = win.add("group");
offsetGroup.add("statictext", undefined, "Offset Gap (px):");
var txtOffset = offsetGroup.add("edittext", undefined, "120");
txtOffset.characters = 5;
var btnAction = win.add("button", undefined, "បង្កើតខ្នាត (" + cleanSegments.length + " ខ្សែ)");
btnAction.size = [240, 35];
var btnClose = win.add("button", undefined, "Close");
// -------------------------------------------------------------
// កំណត់ពុម្ពអក្សរ Khmer OS សម្រាប់ UI Dialog
// -------------------------------------------------------------
var khmerOSFont;
try {
khmerOSFont = ScriptUI.newFont("Khmer OS Siemreap", "REGULAR", 12);
} catch (f1) {
try {
khmerOSFont = ScriptUI.newFont("Khmer OS", "REGULAR", 12);
} catch (f2) {
khmerOSFont = ScriptUI.newFont("Khmer OS System", "REGULAR", 12);
}
}
function applyKhmerFont(container) {
if (container.graphics && khmerOSFont) {
container.graphics.font = khmerOSFont;
}
if (container.children) {
for (var c = 0; c < container.children.length; c++) {
applyKhmerFont(container.children[c]);
}
}
}
applyKhmerFont(win);
// -------------------------------------------------------------
var confirmed = false;
var opts = {};
btnAction.onClick = function() {
opts.scaleMult = (scaleDropdown.selection.text === "1:10") ? 10 : ((scaleDropdown.selection.text === "1:100") ? 100 : 1);
opts.decimals = (decDropdown.selection.index === 0) ? 1 : 2;
opts.showAngle = chkShowAngle.value;
opts.fontSize = parseInt(txtFontSize.text) || 42;
opts.offsetPx = parseInt(txtOffset.text) || 120;
confirmed = true;
win.close();
};
btnClose.onClick = function() { win.close(); };
win.center();
win.show();
if (!confirmed) return;
// ៦. Deselect Marquee Selection រួចបង្កើត Text Layer
if (hasSelection) {
doc.selection.deselect();
}
app.preferences.rulerUnits = Units.PIXELS;
var dimGroup = doc.layerSets.add();
dimGroup.name = "Marquee_Text_Dimensions_Group";
var redColor = new SolidColor();
redColor.rgb.red = 255;
redColor.rgb.green = 0;
redColor.rgb.blue = 0;
for (var j = 0; j < cleanSegments.length; j++) {
var seg = cleanSegments[j];
var x1 = seg.x1, y1 = seg.y1, x2 = seg.x2, y2 = seg.y2;
var distPx = seg.distPx;
if (x1 > x2 || (Math.abs(x1 - x2) < 0.1 && y1 > y2)) {
var tempX = x1; x1 = x2; x2 = tempX;
var tempY = y1; y1 = y2; y2 = tempY;
}
var dx = x2 - x1;
var dy = y2 - y1;
var angleRad = Math.atan2(dy, dx);
var angleDeg = angleRad * (180 / Math.PI);
var distCm = (distPx / dpi) * 2.54;
var actualCm = distCm * opts.scaleMult;
var textVal = actualCm.toFixed(opts.decimals) + " cm";
if (opts.showAngle) {
textVal += " ( " + angleDeg.toFixed(2) + "° )";
}
// Vector កែងរំកិលឡើងលើ
var ux = dx / distPx;
var uy = dy / distPx;
var nx = -uy;
var ny = ux;
// ទីតាំងអក្សរ
var midX = (x1 + x2) / 2;
var midY = (y1 + y2) / 2;
var targetX = midX + nx * opts.offsetPx;
var targetY = midY + ny * opts.offsetPx;
// បង្កើត Text Layer
var textLayer = dimGroup.artLayers.add();
textLayer.kind = LayerKind.TEXT;
textLayer.name = "Dim_" + textVal;
var textItem = textLayer.textItem;
textItem.contents = textVal;
textItem.justification = Justification.CENTER;
textItem.size = opts.fontSize;
textItem.color = redColor;
textItem.position = [targetX, targetY];
textLayer.rotate(angleDeg, AnchorPosition.MIDDLECENTER);
// តម្រឹម Bounding Box
var b = textLayer.bounds;
var curCenterX = (b[0].value + b[2].value) / 2;
var curCenterY = (b[1].value + b[3].value) / 2;
var shiftX = targetX - curCenterX;
var shiftY = targetY - curCenterY;
textLayer.translate(shiftX, shiftY);
}
} catch (err) {
alert("Error: " + err);
} finally {
app.preferences.rulerUnits = startRulerUnits;
}
}
generateDimensionsWithMarqueeSelection();