Photoshop Pythonite

#1

Hi folks, here is a few findings about python script controlling Photoshop.
I’m using python 2.7 x64, Photoshop CS6 (x64), on win7 pro x64.

I’ve found different stuff looking into it for a few tools here, and though about sharing it so other people can get it and enhance all that. Let’s share thinking, new function etc, seems there is lot that can be done.

First, like ever posted, here is how to ‘connect’ to Photoshop:
If Photoshop isn’t started, it will start it.


from win32com.client import Dispatch
psApp = Dispatch("Photoshop.Application")

open some files into Photoshop, then try this:


# Some index infos:
PsDocumentMode = ["none", "Grayscale", "RGB", "CMYK", "Lab", "Bitmap", "IndexedColor", "MultiChannel", "Duotone"]
PsLayerType = ["none", "psArtLayer", "psLayerSet"]
PsBlendMode = ["none", "psPassThrough", "psNormalBlend", "psDissolve", "psDarken", "psMultiply",
    "psColorBurn", "psLinearBurn", "psLighten", "psScreen", "psColorDodge", "psLinearDodge", "psOverlay",
    "psSoftLight", "psHardLight", "psVividLight", "psLinearLight", "psPinLight", "psDifference", "psExclusion",
    "psHue", "psSaturationBlend", "psColorBlend", "psLuminosity", "psHardMix", "psLighterColor", "psDarkerColor",
    "psSubtract", "psDivide"]
PsElementPlacement = ["none", "psPlaceInside", "psPlaceAtBeginning", "psPlaceAtEnd", "psPlaceBefore", "psPlaceAfter"]

# open documents:
openDocs = psApp.Application.Documents
print "Number of open docs:", openDocs.Count
print [openDocs[i].name.encode('cp1252') for i in range(0, openDocs.Count)]

# active document:
doc = psApp.Application.ActiveDocument
print "
 Active documents:", doc.name
print " Doc size:", int(doc.width), int(doc.height)
print " Doc path:", doc.path
print " Doc mode:", PsDocumentMode[doc.mode]
print " Doc BitsPerChannel:", doc.BitsPerChannel
print "ActiveChannels:", [channel.name for channel in doc.ActiveChannels]
try:
    profil = doc.ColorProfileName
except:
    profil = "Untagged"
print " Doc ColorProfileName:", profil
print " Doc Resolution: %sdpi" %doc.Resolution

# some properties:
print "
Some properties:"
myLayer = doc.ActiveLayer
print "Blend mode:", PsBlendMode[myLayer.BlendMode]
print "Bounds:", myLayer.Bounds # detect empty layers !!!
print "Lock:", myLayer.AllLocked
print "FillOpacity:", myLayer.FillOpacity
print "Opacity:", myLayer.Opacity
print "Visible:", myLayer.Visible
print "Type:", myLayer.typename


A few more stuff (some code delete and alter layers, so all is commented) :


#select entire aera:
#doc.Selection.SelectAll()

#copy it to clipboard:
#doc.Selection.Copy()

#remove selection:
#doc.Selection.Deselect()

#newLayer = doc.ActiveLayer.Duplicate()
#newLayer.Delete()

#New layer or Group:
#doc.artLayers.add
#doc.layerSets.add

# Move layers:
#doc.Layers("Layer 4").Move(doc.Layers("Layer 3"), 4) #3:top, #4:bottom

# flatten doc:
#doc.MergeVisibleLayers

# Close PSD without saving:
#doc.Close(2)

# Save:
#doc.Save()

Finally some resources :
http://www.adobe.com/devnet/photoshop/scripting.html
Get Photoshop CS6 VBScript Reference.pdf, there is all vbscript functions into.

Next part will be more fun …

#2

Ok, now you get the basis, and before you start writing heavy stuff … :cool:

Here we have used some COM code to do stuff. But not all is possible with COM functions. Sometimes we will need to use sort of javascript code transformed to python COM. example :

Open a doc with a few layers, select one, and run this :


from win32com.client import Dispatch
psApp = Dispatch("Photoshop.Application")

def _createLayerMask(): # create layer mask on active layer
    dialogMode = 3
    ref = Dispatch("Photoshop.ActionReference")
    desc = Dispatch("Photoshop.ActionDescriptor")
    desc.PutClass(psApp.CharIDToTypeID("Nw  "), psApp.CharIDToTypeID("Chnl"))
    ref.PutEnumerated(psApp.CharIDToTypeID("Chnl"), psApp.CharIDToTypeID("Chnl"), psApp.CharIDToTypeID("Msk "));
    desc.PutReference(psApp.CharIDToTypeID("At  "), ref );
    desc.PutEnumerated(psApp.CharIDToTypeID("Usng"), psApp.CharIDToTypeID("UsrM"), psApp.CharIDToTypeID("RvlA"));

    psApp.ExecuteAction(psApp.CharIDToTypeID("Mk  "), desc, dialogMode);
#


_createLayerMask()

Working on mask like that is not possible directly with COM functions. So we need to send commands to Photoshop so that it execute some referenced actions.

You can list all Photoshop do when doing stuff into it, then reading it and coding it back into python.
You will need ScriptListener for that : http://download.adobe.com/pub/adobe/photoshop/win/13.x/Win_Scripting_Plug-In.zip
You will also get some pdf doc with it. Put the ScriptListener.8li file into you x:\xxx\Adobe Photoshop CS6 (64 Bit)\Plug-ins\Automate, start Photoshop and do some stuff in it. Logs will appears on your desktop.

Some general stuff and problematic in next part …

#3

you can also do this (dDo uses this method) if you want to avoid COM hassles:
Do a subprocess.Popen on “c:\program files\Adobe\Photoshop\Photoshop.exe %temp%\myscript.jsx”. Write the myscript.jsx dynamically from Python before you do the popen.
If photoshop isn’t started it will start and execute the script. If it’s started it will just execute the script. dDo communicates back to it’s partner program (in our case Python) via a temp file where it writes any return values to. But you could use other more complex methods too, like sockets, named pipes, etc.
Thanks to my colleague Akram for figuring out dDo’s magic :slight_smile:

#4

Hi Robert,
I don’t know dDO, do you have more informations ?

Anyway I’m writing a complete module for an internal tool that will get different functions inside a toolbox. Actually, all works like a charm.
I even wrote a python script to transform js log from scriptlistener to python code.

Just wanted to share this work so more people can get into. The more we will work on this, the better the knowledge for everyone. I don’t put myself as a guru, just someone who did a small path into this forest and want to share some light with others.

Actually, the main part of my module works with layer IDs, which is the only unique reference to layers.
Basically :

  • COM can get layer object so we have properties of the layer and can change it. But COM index of layers is only root based (it doesn’t get layers in layerset, and accessing them is cumbersome).
  • DOM index is full, and inverted (from bottom to top)
    Anyway, these indexes change for all layers when you move, delete, or add a layer or group.
    So I’m mainly working with unique IDs of layers, return by some DOM functions, to works on layers, with some COM functions to get layer object inside python when needed.

Dunno if we can get DOM functions to get layer object from Photoshop ? Would simplify a few stuff.

#5

Has anyone figured out how to work with Paths in photoshop using the com method?

1 Like
#6

Oh man, I wish I knew this 2 years ago! This is a gem!

1 Like
#7

Hey guys!

Thanks for this great reference ! It helped me a lot !

In any case I’m trying to write a script that would move objects in to a group usind ID(I believe) the problem I have is that the list is inverted as far as I can tell… how can I invert that list back up ?

Im using comtypes…

def MTG(From, To):
	dialogMode = 3
	idslct = ps.CharIDToTypeID( "slct" )
	ActSelection = ct.CreateObject( "Photoshop.ActionDescriptor" )
	holder = ps.CharIDToTypeID( "null" )
	name = ct.CreateObject( "Photoshop.ActionReference" )
	idLyr = ps.CharIDToTypeID( "Lyr " )
	name.PutName( idLyr, From )
	ActSelection.PutReference( holder, name )
	idMkVs = ps.CharIDToTypeID( "MkVs" )
	ActSelection.PutBoolean( idMkVs, False )
	ps.ExecuteAction( idslct, ActSelection, dialogMode )
	ref=ct.CreateObject( "Photoshop.ActionReference" )
	ref.putProperty( ps.charIDToTypeID("Prpr") , ps.charIDToTypeID( "LyrI" )); 
	ref.putEnumerated( ps.charIDToTypeID("Lyr "), ps.charIDToTypeID("Ordn"), ps.charIDToTypeID("Trgt") );
	IDFrom = ps.executeActionGet(ref).getInteger(ps.charIDToTypeID( "LyrI" ))

	idslct = ps.CharIDToTypeID( "slct" )
	ActSelection = ct.CreateObject( "Photoshop.ActionDescriptor" )
	holder = ps.CharIDToTypeID( "null" )
	name = ct.CreateObject( "Photoshop.ActionReference" )
	idLyr = ps.CharIDToTypeID( "Lyr " )
	name.PutName( idLyr, To )
	ActSelection.PutReference( holder, name )
	idMkVs = ps.CharIDToTypeID( "MkVs" )
	ActSelection.PutBoolean( idMkVs, False )
	ps.ExecuteAction( idslct, ActSelection, dialogMode )
	ref=ct.CreateObject( "Photoshop.ActionReference" )
	ref.putProperty( ps.charIDToTypeID("Prpr") , ps.charIDToTypeID( "LyrI" )); 
	ref.putEnumerated( ps.charIDToTypeID("Lyr "), ps.charIDToTypeID("Ordn"), ps.charIDToTypeID("Trgt") );
	IDTo = ps.executeActionGet(ref).getInteger(ps.charIDToTypeID( "LyrI" ))

	dialogMode = 3
	idmove = ps.CharIDToTypeID( "move" )
	desc312 =ct.CreateObject( "Photoshop.ActionDescriptor" )
	idnull = ps.CharIDToTypeID( "null" )
	ref347 =ct.CreateObject( "Photoshop.ActionReference" )
	idLyr = ps.CharIDToTypeID( "Lyr " )
	ref347.PutName( idLyr, From )
	desc312.PutReference( idnull, ref347 )
	idT = ps.CharIDToTypeID( "T   " )
	ref348 =ct.CreateObject( "Photoshop.ActionReference" )
	idLyr = ps.CharIDToTypeID( "Lyr " )
	ref348.PutIndex( idLyr, IDFrom )
	desc312.PutReference( idT, ref348 )
	idAdjs = ps.CharIDToTypeID( "Adjs" )
	desc312.PutBoolean( idAdjs, False )
	idVrsn = ps.CharIDToTypeID( "Vrsn" )
	desc312.PutInteger( idVrsn, 5 )
	ps.ExecuteAction( idmove, desc312, dialogMode )

MTG("LayerToMove","Group")

Also how can I len(doc) I got this error TypeError: object of type ‘Dispatch’ has no len()

#8

You can just pass a Path to your function. Can you be more specific on the case you need it?

#9

@wizofe you are replying to topics which are 4 - 6 years old…