Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

请教关于复制图层或组到另一个psd #164

Open
Upfunny opened this issue Jul 11, 2022 · 19 comments
Open

请教关于复制图层或组到另一个psd #164

Upfunny opened this issue Jul 11, 2022 · 19 comments

Comments

@Upfunny
Copy link

Upfunny commented Jul 11, 2022

Hi~ 发现你base 深圳 很高兴可以使用中文沟通 有个问题想请教一下~
我几个psd 想从这几个psd中,分别复制 图层 或者 组 到一个新的psd内
想请教一下 怎么跨psd复制图层或组

@loonghao
Copy link
Owner

@Upfunny 这个之前没尝试过,我到时候帮你测试一下

@Upfunny
Copy link
Author

Upfunny commented Jul 12, 2022

@Upfunny 这个之前没尝试过,我到时候帮你测试一下

感谢~! 另外想到一个曲线救国的方式 把psd打开 需要的保留 不需要的全删 存储为psd
再新建一个psd 将这个删减后的psd导入 但会有一个问题
psd导入后,是smart object,如何转换为图层?

@TsXor
Copy link

TsXor commented Jul 16, 2022

单凭ps api可能有点想不到,但是我们可以借助另一个模块:psd-tools
这是psd-tools的Getting started使用例:(将图层保存为图片)

from psd_tools import PSDImage

psd = PSDImage.open('example.psd')
psd.composite().save('example.png')

for layer in psd:
    print(layer)
    layer_image = layer.composite()
    layer_image.save('%s.png' % layer.name)

然后,这是ps api的Import Image As Layer(将图片导入为图层)用例:

"""Import a image as a artLayer."""

# Import local modules
from photoshop import Session


with Session(action="new_document") as ps:
    desc = ps.ActionDescriptor
    desc.putPath(ps.app.charIDToTypeID("null"), "your/image/path")
    event_id = ps.app.charIDToTypeID("Plc ")  # `Plc` need one space in here.
    ps.app.executeAction(ps.app.charIDToTypeID("Plc "), desc)

我们只要拼接上面两段代码即可:将要复制的psd存为临时文件(用tempfiles模块),从这个psd中读出图层并存为临时文件,在ps中切换到目标psd,然后将前述临时图片导入。

@TsXor
Copy link

TsXor commented Jul 16, 2022

顺便捉个虫:https://loonghao.github.io/photoshop-python-api/examples/#operation-layer-set
标题应为Operate Layer Set
第一行应为"""An example to show you how to operate layerSet."""

@Upfunny
Copy link
Author

Upfunny commented Jul 16, 2022

单凭ps api可能有点想不到,但是我们可以借助另一个模块:psd-tools 这是psd-tools的Getting started使用例:(将图层保存为图片)

from psd_tools import PSDImage

psd = PSDImage.open('example.psd')
psd.composite().save('example.png')

for layer in psd:
    print(layer)
    layer_image = layer.composite()
    layer_image.save('%s.png' % layer.name)

然后,这是ps api的Import Image As Layer(将图片导入为图层)用例:

"""Import a image as a artLayer."""

# Import local modules
from photoshop import Session


with Session(action="new_document") as ps:
    desc = ps.ActionDescriptor
    desc.putPath(ps.app.charIDToTypeID("null"), "your/image/path")
    event_id = ps.app.charIDToTypeID("Plc ")  # `Plc` need one space in here.
    ps.app.executeAction(ps.app.charIDToTypeID("Plc "), desc)

我们只要拼接上面两段代码即可:将要复制的psd存为临时文件(用tempfiles模块),从这个psd中读出图层并存为临时文件,在ps中切换到目标psd,然后将前述临时图片导入。

实际上不使用psd-tools 使用photoshop-python-api 也是可以实现的 筛选下图层名 不需要的隐藏掉 输出png就可以
但是我的需求中还需要对合成的psd进行二次编辑,所以直接保存为png肯定是不行的,目前是保存为psd,再在新的psd中导入之前导出的元素psd,缺点是导入后为智能对象,需要点击一下右键转为图层。目前在考虑是否结合PS里的动作进行处理

@TsXor
Copy link

TsXor commented Jul 16, 2022

单凭ps api可能有点想不到,但是我们可以借助另一个模块:psd-tools 这是psd-tools的Getting started使用例:(将图层保存为图片)

from psd_tools import PSDImage

psd = PSDImage.open('example.psd')
psd.composite().save('example.png')

for layer in psd:
    print(layer)
    layer_image = layer.composite()
    layer_image.save('%s.png' % layer.name)

然后,这是ps api的Import Image As Layer(将图片导入为图层)用例:

"""Import a image as a artLayer."""

# Import local modules
from photoshop import Session


with Session(action="new_document") as ps:
    desc = ps.ActionDescriptor
    desc.putPath(ps.app.charIDToTypeID("null"), "your/image/path")
    event_id = ps.app.charIDToTypeID("Plc ")  # `Plc` need one space in here.
    ps.app.executeAction(ps.app.charIDToTypeID("Plc "), desc)

我们只要拼接上面两段代码即可:将要复制的psd存为临时文件(用tempfiles模块),从这个psd中读出图层并存为临时文件,在ps中切换到目标psd,然后将前述临时图片导入。

实际上不使用psd-tools 使用photoshop-python-api 也是可以实现的 筛选下图层名 不需要的隐藏掉 输出png就可以 但是我的需求中还需要对合成的psd进行二次编辑,所以直接保存为png肯定是不行的,目前是保存为psd,再在新的psd中导入之前导出的元素psd,缺点是导入后为智能对象,需要点击一下右键转为图层。目前在考虑是否结合PS里的动作进行处理

请您仔细读,psd-tools可以分别存每一个图层。把一个psd中的一个图层存为png再导入另一个psd,和直接复制一个psd中的一个图层到另一个psd,这两个操作是等效的。

for layer in psd:
    print(layer)
    layer_image = layer.composite()
    layer_image.save('%s.png' % layer.name)

psd-tools示例中的这段代码已经明显的不能再明显了

补充一下,对于一个文字图层,直接读取它的所有属性然后在另一个psd中创建一个一样的即可。

@Upfunny
Copy link
Author

Upfunny commented Jul 16, 2022

单凭ps api可能有点想不到,但是我们可以借助另一个模块:psd-tools 这是psd-tools的Getting started使用例:(将图层保存为图片)

from psd_tools import PSDImage

psd = PSDImage.open('example.psd')
psd.composite().save('example.png')

for layer in psd:
    print(layer)
    layer_image = layer.composite()
    layer_image.save('%s.png' % layer.name)

然后,这是ps api的Import Image As Layer(将图片导入为图层)用例:

"""Import a image as a artLayer."""

# Import local modules
from photoshop import Session


with Session(action="new_document") as ps:
    desc = ps.ActionDescriptor
    desc.putPath(ps.app.charIDToTypeID("null"), "your/image/path")
    event_id = ps.app.charIDToTypeID("Plc ")  # `Plc` need one space in here.
    ps.app.executeAction(ps.app.charIDToTypeID("Plc "), desc)

我们只要拼接上面两段代码即可:将要复制的psd存为临时文件(用tempfiles模块),从这个psd中读出图层并存为临时文件,在ps中切换到目标psd,然后将前述临时图片导入。

实际上不使用psd-tools 使用photoshop-python-api 也是可以实现的 筛选下图层名 不需要的隐藏掉 输出png就可以 但是我的需求中还需要对合成的psd进行二次编辑,所以直接保存为png肯定是不行的,目前是保存为psd,再在新的psd中导入之前导出的元素psd,缺点是导入后为智能对象,需要点击一下右键转为图层。目前在考虑是否结合PS里的动作进行处理

请您仔细读,psd-tools可以分别存每一个图层。把一个psd中的一个图层存为png再导入另一个psd,和直接复制一个psd中的一个图层到另一个psd,这两个操作是等效的。

图层类型很多 文字图层 形状图层 智能对象等等,每个图层也可能包含各种效果,单纯的导入PNG和复制图层可不是等效

@TsXor
Copy link

TsXor commented Jul 16, 2022

图层类型很多 文字图层 形状图层 智能对象等等,每个图层也可能包含各种效果,单纯的导入PNG和复制图层可不是等效

兵来将挡,水来土掩呗。ps api是可以读取图层属性的,像文字图层,形状图层这样的图层只要读取所有信息并在目标psd中创建一个一样的即可。

@Upfunny
Copy link
Author

Upfunny commented Jul 16, 2022

那我请教一下 望不吝赐教
一个文字图层 多行文字 部分文字字号不同,颜色不同,可能加粗倾斜下划线
如何使用api读取信息并新建一个一模一样的

@TsXor
Copy link

TsXor commented Jul 16, 2022

你第一条回复的“目前在考虑是否结合PS里的动作进行处理”其实是个不错的思路。
我试了一下,录制了一个全选并复制的动作和一个粘贴的动作,尝试用这两个动作复制图层,可惜的是,复制的文字图层已经被自动转换成了图片,无法编辑。

@TsXor
Copy link

TsXor commented Jul 16, 2022

那我请教一下 望不吝赐教 一个文字图层 多行文字 部分文字字号不同,颜色不同,可能加粗倾斜下划线 如何使用api读取信息并新建一个一模一样的

读文档:https://loonghao.github.io/photoshop-python-api/reference/photoshop/api/text_item/
我其实不太明白为啥作者不写一份中文文档,明明他写英语都有语法错误(见上方捉虫)

@Upfunny
Copy link
Author

Upfunny commented Jul 16, 2022

那我请教一下 望不吝赐教 一个文字图层 多行文字 部分文字字号不同,颜色不同,可能加粗倾斜下划线 如何使用api读取信息并新建一个一模一样的

读文档:https://loonghao.github.io/photoshop-python-api/reference/photoshop/api/text_item/ 我其实不太明白为啥作者不写一份中文文档,明明他写英语都有语法错误(见上方捉虫)

英文更普适 语法无所谓 看的大差不差就行了 不必吹毛求疵
PS的api 有一些信息是获取不到的比如修改我说的这种复杂一些的文字图层,我是没有找到方法能读出更细的信息
基于图层的调整字号 调整文本是没问题 但调整第5-10个字的字号 或者文本 这种就不行了

@TsXor
Copy link

TsXor commented Jul 16, 2022

那我请教一下 望不吝赐教 一个文字图层 多行文字 部分文字字号不同,颜色不同,可能加粗倾斜下划线 如何使用api读取信息并新建一个一模一样的

读文档:https://loonghao.github.io/photoshop-python-api/reference/photoshop/api/text_item/ 我其实不太明白为啥作者不写一份中文文档,明明他写英语都有语法错误(见上方捉虫)

英文更普适 语法无所谓 看的大差不差就行了 不必吹毛求疵 PS的api 有一些信息是获取不到的比如修改我说的这种复杂一些的文字图层,我是没有找到方法能读出更细的信息 基于图层的调整字号 调整文本是没问题 但调整第5-10个字的字号 或者文本 这种就不行了

这点你说的挺对,ps api确实没这种功能。
但是我发现ps的动作可以实现这种精确度的操作。
因此我有了这种思路:我可以先生成一个单一字体的文本,然后生成一个临时动作,再执行这个动作即可。
我试试能不能实现

@loonghao
Copy link
Owner

@Upfunny 这个之前没尝试过,我到时候帮你测试一下

感谢~! 另外想到一个曲线救国的方式 把psd打开 需要的保留 不需要的全删 存储为psd 再新建一个psd 将这个删减后的psd导入 但会有一个问题 psd导入后,是smart object,如何转换为图层?

下面代码会把当前活动层的智能对象转换成普通层

# example 1
with Session() as ps:
    js = """
var idplacedLayerConvertToLayers = stringIDToTypeID( "placedLayerConvertToLayers" );
executeAction( idplacedLayerConvertToLayers, undefined, DialogModes.NO );    
"""
    ps.app.doJavaScript(js)

# example 2
with Session() as ps:
    descriptor = ps.ActionDescriptor
    idplacedLayerConvertToLayers = ps.app.stringIDToTypeID("placedLayerConvertToLayers")
    ps.app.executeAction(idplacedLayerConvertToLayers, descriptor)

@TsXor
Copy link

TsXor commented Jul 17, 2022

结合动作做了一下解读,缺很多属性,有时间(指下周)再补

import photoshop.api as ps
from psaction import *

psapp = ps.Application()

xyt = psActionDescriptor(psapp, [ \ #作用未知
  ('putDouble', (psstr('xx'), 1.000000)), \
  ('putDouble', (psstr('xy'), 0.000000)), \
  ('putDouble', (psstr('yx'), 0.000000)), \
  ('putDouble', (psstr('yy'), 1.000000)), \
  ('putDouble', (psstr('tx'), 0.000000)), \
  ('putDouble', (psstr('ty'), 0.000000)), \
])
basexy = psActionDescriptor(psapp, [ \ #基底的xy坐标
  ('putDouble', (pschr('Hrzn'), 0.000000)), \
  ('putDouble', (pschr('Vrtc'), 0.000000)), \
])
blockstyle1 = psActionDescriptor(psapp, [ \
  ('putBoolean', (psstr('styleSheetHasParent'), True)), \
  ('putString', (psstr('fontPostScriptName'), """STKaiti""")), \ #字体PostScript名称
  ('putString', (pschr('FntN'), """STKaiti""")), \ #字体名称
  ('putString', (pschr('FntS'), """Regular""")), \ #字体样式
  ('putInteger', (pschr('Scrp'), 25)), \ #脚本,大部分字体的尺寸
  ('putInteger', (pschr('FntT'), 1)), \ #字体技术
  ('putUnitDouble', (pschr('Sz'), pschr('#Pnt'), 25.000000)), \ #本区域的字体尺寸
  ('putBoolean', (psstr('syntheticBold'), True)), \ #仿粗体
  ('putBoolean', (psstr('autoLeading'), False)), \ #自动行距
  ('putUnitDouble', (pschr('Ldng'), pschr('#Pnt'), 32.000000)), \ #行距
])
blockparastyle1 = psActionDescriptor(psapp, [ \
  ('putBoolean', (psstr('styleSheetHasParent'), True)), \
])


textShape = psActionDescriptor(psapp, [ \
  ('putEnumerated', (pschr('TEXT'), pschr('TEXT'), pschr('Pnt'))), \ #文本形状类型:点
  ('putEnumerated', (pschr('Ornt'), pschr('Ornt'), pschr('Hrzn'))), \ #文本方向:水平
  ('putObject', (pschr('Trnf'), pschr('Trnf'), xyt)), \ #调用上方xyt
  ('putInteger', (psstr('rowCount'), 1)), \ #列数
  ('putInteger', (psstr('columnCount'), 1)), \ #行数
  ('putBoolean', (psstr('rowMajorOrder'), True)), \ #是否带“使用行主顺序”
  ('putUnitDouble', (psstr('rowGutter'), pschr('#Pnt'), 0.000000)), \ #行装订线点数
  ('putUnitDouble', (psstr('columnGutter'), pschr('#Pnt'), 0.000000 )), \ #列装订线点数
  ('putUnitDouble', (pschr('Spcn'), pschr('#Pnt'), 0.000000 )), \ #列间距
  ('putEnumerated', (psstr('frameBaselineAlignment'), psstr('frameBaselineAlignment'), psstr('alignByAscent'))), \ #首行对齐:字母上缘
  ('putUnitDouble', (psstr('firstBaselineMinimum'), pschr('#Pnt'), 0.000000)), \ #首行最小高度点数
  ('putObject', (psstr('base'), pschr('Pnt'), basexy)), \ #关联基底配置
])
blockstyle1_range = psActionDescriptor(psapp, [ \
  ('putInteger', (pschr('From'), 0)), \ #从0起
  ('putInteger', (pschr('T'), 48)), \ #到48
  ('putObject', (pschr('TxtS'), pschr('TxtS'), blockstyle1)), \
])
blockparastyle1_range = psActionDescriptor(psapp, [ \
  ('putInteger', (pschr('From'), 0)), \
  ('putInteger', (pschr('T'), 48)), \
  ('putObject', (psstr('paragraphStyle'), psstr('paragraphStyle'), blockparastyle1)), \
])


warp = psActionDescriptor(psapp, [ \
  ('putEnumerated', (psstr('warpStyle'), psstr('warpStyle'), psstr('warpNone'))), \ #变形样式:无
  ('putDouble', (psstr('warpValue'), 0.000000)), \ #弯曲度
  ('putDouble', (psstr('warpPerspective'), 0.000000)), \ #垂直扭曲度
  ('putDouble', (psstr('warpPerspectiveOther'), 0.000000)), \ #水平扭曲度
  ('putEnumerated', (psstr('warpRotate'), pschr('Ornt'), pschr('Hrzn'))), \ #轴:水平
])
textShape_list = psActionList(psapp, [ \
  ('putObject', (psstr('textShape'), textShape)), \ #关联文本形状配置
])
blockstyle_list = psActionList(psapp, [ \
  ('putObject', (pschr('Txtt'), blockstyle1_range)), \ #关联样式范围配置
])
blockparastyle_list = psActionList(psapp, [ \
  ('putObject', (psstr('paragraphStyleRange'), blockparastyle1_range)), \ #关联段落样式范围配置
])
kerningrange_list = psActionList(psapp) #关联字距微调范围配置,遗憾的是本例无此配置


imgproperty = psActionDescriptor(psapp, [ \
  ('putString', (pschr('Txt'), """aaaaaaaaaaaaaaaakaaaa\raaaaaaaaaaaaaaaaaaasfgsdfg""")), \ #文本内容
  ('putObject', (psstr('warp'), psstr('warp'), warp)), \ #关联变形配置
  ('putEnumerated', (psstr('textGridding'), psstr('textGridding'), pschr('None'))), \ #文本网格:无
  ('putEnumerated', (pschr('Ornt'), pschr('Ornt'), pschr('Hrzn'))), \ #取向:水平
  ('putEnumerated', (pschr('AntA'), pschr('Annt'), pschr('AnSt'))), \ #消除锯齿:浑厚  
  ('putList', (psstr('textShape'), textShape_list)), \ #关联文本形状列表
  ('putList', (pschr('Txtt'), blockstyle_list)), \ #关联样式范围列表
  ('putList', (psstr('paragraphStyleRange'), blockparastyle_list)), \ #关联段落样式范围列表
  ('putList', (psstr('kerningRange'), kerningrange_list)), \ #关联字距微调范围列表
])


imgaction = psActionDescriptorTop(psapp, [ \
  ('putEnumerated',(pschr('TxLr'), pschr('Ordn'), pschr('Trgt'))), \
])
imgaction.add_commands([ \
  ('putObject', (pschr('T'), pschr('TxLr'), imgproperty)), \
])
imgaction.execute(pschr('setd'))

注:此段代码无法运行,原因是单行写多行似乎不能在中间注释,测试的话复制上面没注释的即可

@Upfunny
Copy link
Author

Upfunny commented Jul 18, 2022

另外,经测试,可以删去部分代码,只设置部分属性,如:

import photoshop.api as ps
from psaction import *

psapp = ps.Application()

blockstyle1 = psActionDescriptor(psapp, [ \
  ('putBoolean', (psstr('styleSheetHasParent'), True)), \
  ('putString', (psstr('fontPostScriptName'), """STKaiti""")), \
  ('putString', (pschr('FntN'), """STKaiti""")), \
  ('putString', (pschr('FntS'), """Regular""")), \
  ('putInteger', (pschr('Scrp'), 25)), \
  ('putInteger', (pschr('FntT'), 1)), \
  ('putUnitDouble', (pschr('Sz'), pschr('#Pnt'), 25.000000)), \
  ('putBoolean', (psstr('syntheticBold'), True)), \
  ('putBoolean', (psstr('autoLeading'), False)), \
  ('putUnitDouble', (pschr('Ldng'), pschr('#Pnt'), 32.000000)), \
])
blockparastyle1 = psActionDescriptor(psapp, [ \
  ('putBoolean', (psstr('styleSheetHasParent'), True)), \
])


blockstyle1_range = psActionDescriptor(psapp, [ \
  ('putInteger', (pschr('From'), 0)), \
  ('putInteger', (pschr('T'), 48)), \
  ('putObject', (pschr('TxtS'), pschr('TxtS'), blockstyle1)), \
])
blockparastyle1_range = psActionDescriptor(psapp, [ \
  ('putInteger', (pschr('From'), 0)), \
  ('putInteger', (pschr('T'), 48)), \
  ('putObject', (psstr('paragraphStyle'), psstr('paragraphStyle'), blockparastyle1)), \
])


blockstyle_list = psActionList(psapp, [ \
  ('putObject', (pschr('Txtt'), blockstyle1_range)), \
])
blockparastyle_list = psActionList(psapp, [ \
  ('putObject', (psstr('paragraphStyleRange'), blockparastyle1_range)), \
])
kerningrange_list = psActionList(psapp)


imgproperty = psActionDescriptor(psapp, [ \
  ('putString', (pschr('Txt'), """aaaaaaaaaaaaaaaakaaaa\raaaaaaaaaaaaaa\raaaaasfgsdfg""")), \
  ('putEnumerated', (pschr('Ornt'), pschr('Ornt'), pschr('Hrzn'))), \
  ('putList', (pschr('Txtt'), blockstyle_list)), \
  ('putList', (psstr('paragraphStyleRange'), blockparastyle_list)), \
  ('putList', (psstr('kerningRange'), kerningrange_list)), \
])


imgaction = psActionDescriptorTop(psapp, [ ('putEnumerated',(pschr('TxLr'), pschr('Ordn'), pschr('Trgt'))) ])
imgaction.add_commands([ ('putObject', (pschr('T'), pschr('TxLr'), imgproperty)) ])
imgaction.execute(pschr('setd'))

感觉胜利近在眼前了 上述动作只能设置文本的高级属性,并不能读取,但是看psd-tools的文档好像可以读取这些属性

话说怎么录制jsx? 我录制出来的动作是atn

@TsXor
Copy link

TsXor commented Jul 18, 2022

话说怎么录制jsx? 我录制出来的动作是atn

安装ScriptingListener插件:https://helpx.adobe.com/photoshop/kb/downloadable-plugins-and-content.html#id_68969
注意:不用时请将它移出插件目录,以免录制的操作挤爆硬盘

@TsXor
Copy link

TsXor commented Jul 30, 2022

通过这几天对ActionManager的研究,我发现了解决此问题的通用方案:

  1. 设计一个ActionReference选择目标图层
  2. 使用app.executeActionGet方法获取到包含目标图层数据的ActionDescriptor
  3. 切换文件
  4. 在新文件中创建一个同名同类型图层
  5. 使用executeAction将第2步的ActionDescriptor中的数据全部注入新文件中的图层

@TsXor
Copy link

TsXor commented Aug 2, 2022

此段代码可以实现一个文字图层的全复制,将当前文档选中的图层复制到下一个文档选中的图层上(需要都是文字图层)

#参考:https://loonghao.github.io/photoshop-python-api/examples/#copy-and-paste

#导入模块,设置app
import photoshop.api as ps
import photoshop.api.action_manager as am
app = ps.Application()

def switch_doc(n):  #ActionManager实现,向前/后切换n个文档,正数向后,负数向前
  next_desc = ps.ActionDescriptor.load({'null': ['!ref', ReferenceKey('document', am.Offset+n)])
  app.executeAction(am.str2id('select'), next_desc)

startRulerUnits = app.preferences.rulerUnits
app.preferences.rulerUnits = ps.Units.Inches

#这个reference会选择当前文字图层
curlyr_ref = ps.ActionReference.load(['!ref', ReferenceKey('textLayer', am.Enumerated('ordinal', 'targetEnum')))
curlyr_desc = app.executeActionGet(curlyr_ref)
curlyr_data = curlyr_desc.uget('textKey')
switch_doc(1)  #切换到后一个文档
desc = ps.ActionDescriptor()
desc.uput('target', curlyr_ref)
desc.uput('to', curlyr_data)
app.executeAction(am.str2id('set'), desc)

if startRulerUnits != app.preferences.rulerUnits:
    app.preferences.rulerUnits = startRulerUnits

完全复制,大小样式全部保留的那种

@Upfunny

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants