Skip to content

Instantly share code, notes, and snippets.

@yorb
Last active December 14, 2015 15:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save yorb/5112285 to your computer and use it in GitHub Desktop.
Save yorb/5112285 to your computer and use it in GitHub Desktop.
Photoshop script for adding rectangle measurements to your mockups
/*
Photoshop script for adding measurements to your mockups,
based on Pixel Measure v0.04 by Nikolaj Selvik (https://code.google.com/p/pixelmeasure/)
To install: Place in /Applications/Adobe Photoshop CC/Presets/Scripts/ and restart Photoshop.
To use: Draw a rectangle marquee and invoke from File > Scripts > Pixel Measure - Asset.
Tips:
- Set your foreground color to something visible (e.g., red).
- Set your Measurement Scale (Image > Analysis > Set Measurement Scale).
For iOS retina, you might use the unit "pt" and set the pixel length to 2 (2px = 1pt @2x).
- Set your DPI scale (Image > Image Size). In the retina example, if 72 is your normal dpi,
use 144 (72 x 2 = 144).
- Set your ruler to use points.
- Set a keyboard shortcut to invoke the script.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// temporarily change ruler units to points
var originalUnit = preferences.rulerUnits;
preferences.rulerUnits = Units.POINTS;
// set up vars
var assetName, docRef, ratio, docRes;
var isTextLabel = false;
app.displayDialogs = DialogModes.NO;
if(validateState())
{
app.activeDocument.suspendHistory("Create " + (isTextLabel ? "text spec" : "asset measurement"), "createMeasure();");
}
function validateState()
{
if (app.documents.length === 0)
{
alert("No document open");
return false;
} else {
docRef = app.activeDocument;
ratio = docRef.measurementScale.pixelLength;
docRes = docRef.resolution;
}
if(!hasSelection(app.activeDocument))
{
alert("Please make a selection to measure");
return false;
}
assetName = prompt("Asset Name\r(prefix with \"t \" for text styles; leave blank for dimensions only)", "");
if ((assetName) === null) {
return false;
}
if (assetName.indexOf("t ") === 0) {
isTextLabel = true;
assetName = assetName.substr(2);
}
if (docRes / ratio !== 72) {
if (!confirm("Measurement Scale does not match document resolution\rContinue anyway?")) {
return false;
}
}
try {
var colorTest = app.foregroundColor;
} catch(e) {
alert("Invalid foreground color\r" + e);
return false;
}
return true;
}
function createMeasure()
{
var selRef = docRef.selection;
var mainLayerSet;
// set density-independent units (dp, pt)
var measureUnits = docRef.measurementScale.logicalUnits;
if(measureUnits == "pixels")
{
measureUnits = "px";
}
else if(measureUnits == "points")
{
measureUnits = "pt";
}
// =======================================================
// Create "Pixel Measures" LayerSet if it doesn't already exist
// =======================================================
try
{
mainLayerSet = docRef.layerSets.getByName("Pixel Measures");
}
catch(error)
{
mainLayerSet = docRef.layerSets.add();
mainLayerSet.name = "Pixel Measures";
}
// =======================================================
// Create Measurement LayerSet
// =======================================================
var layerSetRef = mainLayerSet.layerSets.add();
var boxLayerRef = layerSetRef.artLayers.add();
// get average color
var x1 = selRef.bounds[0].value;
var y1 = selRef.bounds[1].value;
var x2 = selRef.bounds[2].value;
var y2 = selRef.bounds[3].value;
selRef.copy(true);
var chanRef = docRef.channels.add();
chanRef.name = "TMP";
chanRef.kind = ChannelType.SELECTEDAREA;
selRef.store(docRef.channels.getByName("TMP"), SelectionType.EXTEND);
docRef.paste();
var tmpLayer = docRef.activeLayer;
selRef.load(docRef.channels.getByName("TMP"));
tmpLayer.applyAverage();
chanRef.remove();
selRef.deselect();
var width = x2 - x1;
var height = y2 - y1;
var tmpSampler = docRef.colorSamplers.add(Array(x1 + width/2, y1 + height/2));
var bgColor = new SolidColor();
bgColor = tmpSampler.color;
tmpSampler.remove();
//~ app.foregroundColor = bgColor;
// =======================================================
// Draw Lines
// =======================================================
drawBox(x1,y1,x2,y2);
// =======================================================
// Draw Text
// =======================================================
var textLayerRef = layerSetRef.artLayers.add();
textLayerRef.kind = LayerKind.TEXT;
var textItemRef = textLayerRef.textItem;
var textColor = new SolidColor();
textItemRef.size = new UnitValue(5, "pt");
// temporary workaround for text size bug
if (textItemRef.size.as("pt") != 5) {
changeTextSize(5*ratio);
}
textItemRef.font = "Monaco";
textItemRef.antiAliasMethod = AntiAlias.NONE;
textItemRef.contents = assetName + (isTextLabel ? "" : (assetName ? "\r" : "") + width + "\u00D7" + height + " " + measureUnits);
textItemRef.justification = Justification.CENTER;
var textWidth = textLayerRef.bounds[2].value - textLayerRef.bounds[0].value;
var textHeight = textLayerRef.bounds[3].value - textLayerRef.bounds[1].value;
if(textWidth < width && textHeight < height)
{
textItemRef.position = Array(x1 + width/2, y1 + (height/2) + (assetName && !isTextLabel ? -2/ratio : 4/ratio));
var fgColorValue = (app.foregroundColor.rgb.red + app.foregroundColor.rgb.green + app.foregroundColor.rgb.blue) / 3;
if(isTextLabel || fgColorValue < 128)
{
textColor.rgb.hexValue = "ffffff";
}
else
{
textColor.rgb.hexValue = "000000";
}
}
else
{
// test position of text and find good offsets
var textX = x1 + width/2;
if (textX - textWidth/2 < 0) {
textX = 1;
textItemRef.justification = Justification.LEFT;
} else if (textX + textWidth/2 > docRef.width.value) {
textX = docRef.width.value - 1;
textItemRef.justification = Justification.RIGHT;
}
var textY = y2 + 15/ratio;
if (textY + 2 > docRef.height.value) {
textY = y1 - (assetName && !isTextLabel ? 16/ratio : 4/ratio);
}
textItemRef.position = Array(textX, textY);
if (isTextLabel) {
textColor.rgb.hexValue = "00aeef";
} else {
textColor = app.foregroundColor;
}
}
if (assetName) {
layerSetRef.name = assetName;
} else {
layerSetRef.name = textItemRef.contents;
}
textItemRef.color = textColor;
// =======================================================
// Reset
// =======================================================
app.preferences.rulerUnits = originalUnit;
}
function drawBox(x1,y1,x2,y2)
{
// Factor in ruler offset, from http://forums.adobe.com/message/2866222#2866222
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var desc = executeActionGet(ref);
var xOffSet = desc.getInteger(stringIDToTypeID('rulerOriginH')) / 65536;
var yOffSet = desc.getInteger(stringIDToTypeID('rulerOriginV')) / 65536;
x1 -= xOffSet/ratio;
x2 -= xOffSet/ratio;
y1 -= yOffSet/ratio;
y2 -= yOffSet/ratio;
var pointArray = [];
var pointA = new PathPointInfo();
pointA.kind = PointKind.CORNERPOINT;
pointA.anchor = Array(x1, y1);
pointA.leftDirection = pointA.anchor;
pointA.rightDirection = pointA.anchor;
pointArray.push(pointA);
var pointB = new PathPointInfo();
pointB.kind = PointKind.CORNERPOINT;
pointB.anchor = Array(x2, y1);
pointB.leftDirection = pointB.anchor;
pointB.rightDirection = pointB.anchor;
pointArray.push(pointB);
var pointC = new PathPointInfo();
pointC.kind = PointKind.CORNERPOINT;
pointC.anchor = Array(x2, y2);
pointC.leftDirection = pointC.anchor;
pointC.rightDirection = pointC.anchor;
pointArray.push(pointC);
var pointD = new PathPointInfo();
pointD.kind = PointKind.CORNERPOINT;
pointD.anchor = Array(x1, y2);
pointD.leftDirection = pointD.anchor;
pointD.rightDirection = pointD.anchor;
pointArray.push(pointD);
var box = new SubPathInfo();
box.operation = ShapeOperation.SHAPEXOR;
box.closed = true;
box.entireSubPath = pointArray;
var boxSubPathArray = [];
boxSubPathArray.push(box);
var fillColor;
if (isTextLabel) {
fillColor = new SolidColor();
fillColor.rgb.hexValue = "00aeef";
} else {
fillColor = app.foregroundColor;
}
var boxPath = app.activeDocument.pathItems.add("TempPath", boxSubPathArray);
boxPath.fillPath(fillColor, ColorBlendMode.NORMAL, 60, 0, false, false);
app.activeDocument.pathItems.removeAll();
}
function hasSelection(doc)
{
var res = false;
var as = doc.activeHistoryState;
doc.selection.deselect();
if (as != doc.activeHistoryState)
{
res = true;
doc.activeHistoryState = as;
}
return res;
}
// Temporary fix for the textItem.size bug: https://forums.adobe.com/message/6804450
// Adapted from https://forums.adobe.com/message/6814050#6814050
function changeTextSize(newSize) {
var idsetd = charIDToTypeID( "setd" );
var desc23 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref6 = new ActionReference();
var idPrpr = charIDToTypeID( "Prpr" );
var idTxtS = charIDToTypeID( "TxtS" );
ref6.putProperty( idPrpr, idTxtS );
var idTxLr = charIDToTypeID( "TxLr" );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref6.putEnumerated( idTxLr, idOrdn, idTrgt );
desc23.putReference( idnull, ref6 );
var idT = charIDToTypeID( "T " );
var desc24 = new ActionDescriptor();
var idtextOverrideFeatureName = stringIDToTypeID( "textOverrideFeatureName" );
desc24.putInteger( idtextOverrideFeatureName, 808465458 );
var idtypeStyleOperationType = stringIDToTypeID( "typeStyleOperationType" );
desc24.putInteger( idtypeStyleOperationType, 3 );
var idSz = charIDToTypeID( "Sz " );
var idPxl = charIDToTypeID( "#Pxl" );
desc24.putUnitDouble( idSz, idPxl, newSize );
var idTxtS = charIDToTypeID( "TxtS" );
desc23.putObject( idT, idTxtS, desc24 );
executeAction( idsetd, desc23, DialogModes.NO );
}
@mycort
Copy link

mycort commented Sep 12, 2013

I have a even better and more robust version of this script. Supports multiple measurements at one time, creates arrow points, and creates the font info of a selected text. email me at mycort@yahoo.com if you want it.

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