Position cards for printing

I wrote this Photoshop script to layout layers in an array for easy printing of game cards on pages.

During the development of Doomsday I had to position the 96 cards on A3 sheets for printing, and while its easy to use the guides, I found it was not always accurate particularly when zoomed out or in, therefore I wrote this Photoshop script to layout layers in an array for easy printing of game cards on pages.

Copy the code and paste it into a new .jsx file on your computer remembering to change the variables on lines 30-36 to the values that suit your desired print output.

Read the “change this value” comments in the code.

  • leftMargin
  • topMargin
  • cardWidth
  • cardHeight
  • cardsInARow
  • cardsInAColumn
  • groupPages

Then load all the images as layers using the “Load files into stack” script that comes with Photoshop. Next resize your canvas to the page size of your printer (A4, A3 etc..).

Lastly run my script using file->scripts->browse and locate the saved jsx file.

I could make it real simple and make a dialog for this but using a hard coded script you only need to change the code once and a dialog would require you enter the values every time you set up your cards.

Oh don’t forget to add your cut lines as the top most layer, you can do this before and then lock the layer and the code will ignore the locked layers.

#target photoshop;  

app.bringToFront();    
var doc = app.activeDocument;


function positionLayer( lyr, x, y )
{   // layerObject, Number, Number  
    // if can not move because its locked return  false
    if(lyr.isBackgroundLayer||lyr.positionLocked) 
    {
        return false; 
    }
    // get the layer bounds  
    var layerBounds = lyr.bounds;  
    // get top left position  
    var layerX = layerBounds[0].value;  
    var layerY = layerBounds[1].value;  
    // the difference between where layer needs to be and is now  
    var deltaX = x-layerX;  
    var deltaY = y-layerY;  
    // move the layer into position  
    lyr.translate (deltaX, deltaY);  
    return true;
}

function    moveLayers()
{   
    
    var leftMargin      = 83;  // change this value for the left margin
    var topMargin       = 51;  // change this value for the top margin
    var cardWidth       = 746; // change this value for width of the card
    var cardHeight      = 1038;// change this value for height of the card
    var cardsInARow     = 5    // change this value for number of cards across page
    var cardsInAColumn  = 5    // change this value for number of rows of cards down page
    var groupPages      =   true; // set to false if you dont want the cards in groups 
    var pageX           = leftMargin;                               
    var pageY           = topMargin;  
    var cardsXCount     =   1;         
    var cardsYCount     =   1;  
    if(groupPages==true)                       
    {
        var theGroup = app.activeDocument.layerSets.add()
    }
    var length = doc.artLayers.length-1;
    for (var layer = length; layer >= 0 ; layer--)
    {
        var thisLayer = doc.artLayers[layer];
        if(positionLayer(thisLayer, pageX,pageY)==true)
        {    
            if(groupPages==true)                       
            {        
                thisLayer.move(theGroup, ElementPlacement.INSIDE); 
            }
            pageX           +=    cardWidth;                      // width in pixels of you cards
            cardsXCount     ++;
            if(cardsXCount>cardsInARow)
            {
                cardsXCount     =   1;
                pageX           =   leftMargin;                 // reset to the left margin
                pageY           +=  cardHeight;
                cardsYCount     ++;
                if(cardsYCount>cardsInAColumn)
                {
                    pageY           = topMargin;
                    cardsYCount     =   1;
                    if(groupPages==true)                       
                    {
                        theGroup = app.activeDocument.layerSets.add();
                    }
                }
            }
        }
    }
}
moveLayers();

After developing the above code I added some more featurs to it, in this version it does more of the work for you. You just need to set the card width and height, and it reads the page size from photoshop and then it ads the cut marks and centers the cards on the page.

Hope this works better for you.

#target photoshop;  

app.bringToFront();    
var doc = app.activeDocument;
//var debug = new File("f:\script.txt");
app.preferences.rulerUnits = Units.PIXELS;                  // Set the ruler units to PIXELS  
var w               =   doc.width;
var h               =   doc.height;
var groupPages      =   true;                            // set to false if you dont want the cards in groups 
var cardWidth       =   746;                                // change this value for width of the card
var cardHeight      =   1038;                               // change this value for height of the card
var cardsInARow     =   Math.floor(w/cardWidth);            // change this value for number of cards across page
var cardsInAColumn  =   Math.floor(h/cardHeight);           // change this value for number of rows of cards down page
var leftMargin      =   (w-(cardsInARow*cardWidth))/2;      // change this value for the left margin (83)
var topMargin       =   (h-(cardsInAColumn*cardHeight))/2;  // change this value for the top margin (51)    


function positionLayer( lyr, x, y )
{   // layerObject, Number, Number  
    // if can not move because its locked return  false
    if(lyr.isBackgroundLayer||lyr.positionLocked) 
    {
        return false; 
    }
    // get the layer bounds  
    var layerBounds = lyr.bounds;  
    // get top left position  
    var layerX = layerBounds[0].value;  
    var layerY = layerBounds[1].value;  
    // the difference between where layer needs to be and is now  
    var deltaX = x-layerX;  
    var deltaY = y-layerY;  
  //  debug.open('a');
  //  debug.writeln("x:"+x+" y:"+y+" layerX:"+layerX+"  layerY:"+layerY+" deltaX:"+deltaX+"  deltaY:"+deltaY);   
  //  debug.close();

    // move the layer into position  
    lyr.translate (deltaX, deltaY);  
    return true;
}

function toDPI(valu)
{
    var     defaultDPI  =   72;
    var     docDPI      =   300;
    var     dpiRation   =   docDPI/defaultDPI;
    return  Math.floor(valu/dpiRation);
}


// exported from photoshop listener
function DrawShape() 
{

    var doc = app.activeDocument;
    var y = arguments.length;
    var i = 0;
    var rectArray = [];
    for (i = 0; i < y; i++)
     {
        rectArray[i] = new PathPointInfo;
        rectArray[i].kind = PointKind.CORNERPOINT;
        rectArray[i].anchor = arguments[i];

        rectArray[i].leftDirection = rectArray[i].anchor;
        rectArray[i].rightDirection = rectArray[i].anchor;
      
    }
    var lineSubPathArray = new SubPathInfo();
    lineSubPathArray.closed = true;
    lineSubPathArray.operation = ShapeOperation.SHAPEADD;
    lineSubPathArray.entireSubPath = rectArray;
    
    var myPathItem = doc.pathItems.add("myPath", [lineSubPathArray]);
    var actionDescript = new ActionDescriptor();
    var contentLayer = new ActionReference();
    
    contentLayer.putClass(stringIDToTypeID("contentLayer"));
    actionDescript.putReference(charIDToTypeID("null"), contentLayer);
    var actionType = new ActionDescriptor();
    var actionColor = new ActionDescriptor();
    var color = new ActionDescriptor();
    color.putDouble(charIDToTypeID("Rd  "), 0.0); // R
    color.putDouble(charIDToTypeID("Grn "), 0.0); // G
    color.putDouble(charIDToTypeID("Bl  "), 0.0); // B
    var rgbCoverted = charIDToTypeID("RGBC");
    actionColor.putObject(charIDToTypeID("Clr "), rgbCoverted, color);
    actionType.putObject(charIDToTypeID("Type"), stringIDToTypeID("solidColorLayer"), actionColor);
    actionDescript.putObject(charIDToTypeID("Usng"), stringIDToTypeID("contentLayer"), actionType);
    executeAction(charIDToTypeID("Mk  "), actionDescript, DialogModes.NO);
    myPathItem.remove();
}
// exported from photoshop listener
function    addShape()
{
    var idAddT = charIDToTypeID( "AddT" );
    var desc16 = new ActionDescriptor();
    var idnull = charIDToTypeID( "null" );
        var ref5 = new ActionReference();
        var idPath = charIDToTypeID( "Path" );
        var idOrdn = charIDToTypeID( "Ordn" );
        var idTrgt = charIDToTypeID( "Trgt" );
        ref5.putEnumerated( idPath, idOrdn, idTrgt );
    desc16.putReference( idnull, ref5 );
    var idT = charIDToTypeID( "T   " );
        var desc17 = new ActionDescriptor();
        var idTop = charIDToTypeID( "Top " );
        var idPxl = charIDToTypeID( "#Pxl" );
        desc17.putUnitDouble( idTop, idPxl,  arguments[0] );
        var idLeft = charIDToTypeID( "Left" );
        var idPxl = charIDToTypeID( "#Pxl" );
        desc17.putUnitDouble( idLeft, idPxl, arguments[1] );
        var idBtom = charIDToTypeID( "Btom" );
        var idPxl = charIDToTypeID( "#Pxl" );
        desc17.putUnitDouble( idBtom, idPxl,arguments[2] );
        var idRght = charIDToTypeID( "Rght" );
        var idPxl = charIDToTypeID( "#Pxl" );
        desc17.putUnitDouble( idRght, idPxl, arguments[3] );
    var idRctn = charIDToTypeID( "Rctn" );
    desc16.putObject( idT, idRctn, desc17 );
    executeAction( idAddT, desc16, DialogModes.NO );
}

function   addCutMarks()
{

    var     vCut    =       0    
    var     xT      =    Math.floor(toDPI(leftMargin));
    var     yT      =    Math.floor(toDPI(topMargin-50));
    var     yB      =    Math.floor(cardsInAColumn*cardHeight);
    var     sw      =    toDPI(1);
    var     sh      =    toDPI(yB+100);

    DrawShape([(xT),(yT)],[(xT+sw),(yT)],[(xT+sw),(yT+sh)],[(xT),(yT+sh)]); 
    xT  =   Math.floor(leftMargin);
    yT  =   Math.floor(topMargin-50);
    yB  =   Math.floor(yT+(cardsInAColumn*cardHeight)+100);
    // verticle
    for(var vCut=0;vCut<cardsInARow;vCut++)
    {
        xT  +=    cardWidth;
        addShape(yT,xT,yB,(xT+1));
    }
    xT  =   Math.floor(leftMargin-50)   
    yT  =   Math.floor(topMargin);    
    xB  =   Math.floor(xT+(cardsInARow*cardWidth)+100);
    addShape(yT,xT,yT,xB);
    // horizontal
    for(var hCut=0;hCut<cardsInAColumn;hCut++)
    {
            yT  +=  cardHeight;
            addShape(yT,xT,yT,xB);
    }
}

function    moveLayers()
{       
    var pageX           =   leftMargin;                               
    var pageY           =   topMargin;  
    var cardsXCount     =   1;         
    var cardsYCount     =   1;  
  
    if(groupPages==true)                       
    {
        var theGroup = app.activeDocument.layerSets.add()
    }
    var length = doc.artLayers.length-1;
    for (var layer = length; layer >= 0 ; layer--)
    {
        var thisLayer = doc.artLayers[layer];
        if(positionLayer(thisLayer, pageX,pageY)==true)
        {    
            if(groupPages==true)                       
            {        
                thisLayer.move(theGroup, ElementPlacement.INSIDE); 
            }
            pageX           +=    cardWidth;                      // width in pixels of you cards
            cardsXCount     ++;
            if(cardsXCount>cardsInARow)
            {
                cardsXCount     =   1;
                pageX           =   leftMargin;                 // reset to the left margin
                pageY           +=  cardHeight;
                cardsYCount     ++;
                if(cardsYCount>cardsInAColumn)
                {
                    pageY           = topMargin;
                    cardsYCount     =   1;
                    if(groupPages==true)                       
                    {
                        theGroup = app.activeDocument.layerSets.add();
                    }
                }
            }
        }
    }
}
moveLayers();
addCutMarks();