Assistor PS 分析

微信截图_20190806163246.png

Assistor PS  :Save Your Valuable Time (节省你的宝贵时间)

初看Assistor PS让我觉得惊为天人,不需要以插件的形式存在就可以进行读取图层,并且为每个图层进行标注,裁剪,距离计算等等一系列比较黑科技的功能。于是安奈不住分析一下。

微信截图_20190806163744.png

界面做的很漂亮,然后有几个按钮,本来想直接找按钮底下的事件,发现不是太好找,于是换了个思路,查找热键对应的事件。

反编译找到如下代码

GlobalHotKey.Register(hwndSource.Handle, ModifierKeys.Alt, GlobalHotKey.Keys.D1).Pressed = OnHotKeyPressed;
GlobalHotKey.Register(hwndSource.Handle, ModifierKeys.Alt, GlobalHotKey.Keys.D2).Pressed = OnHotKeyPressed;
GlobalHotKey.Register(hwndSource.Handle, ModifierKeys.Alt, GlobalHotKey.Keys.D3).Pressed = OnHotKeyPressed;
GlobalHotKey.Register(hwndSource.Handle, ModifierKeys.Alt, GlobalHotKey.Keys.D4).Pressed = OnHotKeyPressed;
GlobalHotKey.Register(hwndSource.Handle, ModifierKeys.Alt, GlobalHotKey.Keys.Q).Pressed = OnHotKeyPressed;
GlobalHotKey.Register(hwndSource.Handle, ModifierKeys.Alt, GlobalHotKey.Keys.W).Pressed = OnHotKeyPressed;
GlobalHotKey.Register(hwndSource.Handle, ModifierKeys.Alt, GlobalHotKey.Keys.E).Pressed = OnHotKeyPressed;

然后找到回调事件

switch (e.Key)
{
    default:
        return;
    case GlobalHotKey.Keys.D1:
        Tool.ExecutePosition();
    break;
    case GlobalHotKey.Keys.D2:
        Tool.ExecuteSize();
    break;
    case GlobalHotKey.Keys.D3:
        Tool.ExecuteDistance();
    break;
    case GlobalHotKey.Keys.D4:
        Tool.ExecuteText();
    break;
    case GlobalHotKey.Keys.Q:
        Tool.ExecuteGuideBox();
    break;
    case GlobalHotKey.Keys.W:
        Tool.ExecuteSnips();
    break;
    case GlobalHotKey.Keys.A:
        Tool.ExecuteDescription();
    break;
}

发现作者是将一些操作封装了一下,查看这个Tool类。

public void ExecutePosition()
{
    this.AppendLog(LogType.Information, LogSubType.Execute, AssistorLogType.LayerDescriptor_Position, string.Empty, string.Empty);
    this.(ScriptProvider.Execute(ScriptMethodType.Position, this.()));
}
public void ExecuteSize()
{
    this.AppendLog(LogType.Information, LogSubType.Execute, AssistorLogType.LayerDescriptor_Size, string.Empty, string.Empty);
    this.(ScriptProvider.Execute(ScriptMethodType.Size, this.()));
}
public void ExecuteText()
{
    this.AppendLog(LogType.Information, LogSubType.Execute, AssistorLogType.LayerDescriptor_Text, string.Empty, string.Empty);
    this.(ScriptProvider.Execute(ScriptMethodType.Text, this.()));
}
public void ExecuteDistance()
{
    this.AppendLog(LogType.Information, LogSubType.Execute, AssistorLogType.LayerDescriptor_Distance, string.Empty, string.Empty);
    this.(ScriptProvider.Execute(ScriptMethodType.Distance, this.()));
}
public void ExecuteGuideBox()
{
    this.AppendLog(LogType.Information, LogSubType.Execute, AssistorLogType.LayerDescriptor_GuideBox, string.Empty, string.Empty);
    this.(ScriptProvider.Execute(ScriptMethodType.GuideBox, this.()));
}
public void ExecuteSnips()
{
    this.AppendLog(LogType.Information, LogSubType.Execute, AssistorLogType.LayerDescriptor_Snips, string.Empty, string.Empty);
    this.(ScriptProvider.Execute(ScriptMethodType.Snips, this.()));
}

然后有一个ScriptProvider,这里面有一些比较好的进程之间的写法,总的来说,通过ScriptMethodType判断你的执行操作类型,然后执行Photoshop的JSX脚本。

ScriptMethodType类型

public enum ScriptMethodType
{
    Position,
    Size,
    Text,
    Distance,
    GuideBox,
    Snips,
    Description,
    Document,
    Guides,
    GuidesHCenter,
    GuidesVCenter,
    GuidesClear,
    Rounder,
    Tiler,
    Capture
}

JSX脚本

  1.     JSX.WIT.Photoshop.Application.jsx

  2.     JSX.WIT.Photoshop.Common.jsx

  3.     JSX.WIT.Photoshop.Document.jsx

  4.     JSX.WIT.Photoshop.DrawingContext.jsx

  5.     JSX.WIT.Photoshop.JSON.jsx

  6.     JSX.WIT.Photoshop.Parameter.jsx

  7.     JSX.WIT.Photoshop.Parser.jsx

  8.     JSX.WIT.Photoshop.Polygons.jsx

  9.     JSX.WIT.Photoshop.Processor.jsx

  10.     JSX.WIT.Photoshop.Script.Capture.jsx

  11.     JSX.WIT.Photoshop.Script.Distance.jsx

  12.     JSX.WIT.Photoshop.Script.Export.jsx

  13.     JSX.WIT.Photoshop.Script.GuideBox.jsx

  14.     JSX.WIT.Photoshop.Script.Guides.jsx

  15.     JSX.WIT.Photoshop.Script.GuidesClear.jsx

  16.     JSX.WIT.Photoshop.Script.GuidesHCenter.jsx

  17.     JSX.WIT.Photoshop.Script.GuidesVCenter.jsx

  18.     JSX.WIT.Photoshop.Script.jsx

  19.     JSX.WIT.Photoshop.Script.Position.jsx

  20.     JSX.WIT.Photoshop.Script.Rounder.jsx

  21.     JSX.WIT.Photoshop.Script.Size.jsx

  22.     JSX.WIT.Photoshop.Script.Snips.jsx

  23.     JSX.WIT.Photoshop.Script.Text.jsx

  24.     JSX.WIT.Photoshop.Script.Tiler.jsx

可以发现名字和ScriptMethodType自定义类型是一致的。

部分脚本源码

function Photoshop() {
    
    
    var desc = new ActionDescriptor();
    var ref = new ActionReference();
    ref.putProperty(typeID("Prpr"), typeID("PbkO"));
    ref.putEnumerated(typeID("capp"), typeID("Ordn"), typeID("Trgt"));
    desc.putReference(typeID("null"), ref );
    
    var pdesc = new ActionDescriptor();
    pdesc.putEnumerated(typeID("performance"), typeID("performance"), typeID("accelerated"));    
    desc.putObject(typeID("T   "), typeID("PbkO"), pdesc );
    executeAction(typeID("setd"), desc, DialogModes.NO);
    
}
Photoshop.prototype.SetPanelVisibility = function(panelName, visible)
{
    try {
        var desc = new ActionDescriptor();
        var ref = new ActionReference(); 
        ref.putName( stringIDToTypeID( "classPanel" ), panelName ); 
        desc.putReference( charIDToTypeID( "null" ), ref ); 
        executeAction( stringIDToTypeID( visible ? "show" : "hide"), desc, DialogModes.NO );  
    }
    catch(ex)
    {
    }
        
 
}
Photoshop.prototype.SetLayersPanelVisibility = function(visible)
{
    this.SetPanelVisibility('panelid.static.layers', visible);    
}
Photoshop.prototype.GetActiveDocument = function(){
    return new Document(this, this.GetActiveDocumentId());        
    
};
Photoshop.prototype.GetActiveDocumentId = function(){
    try {        
        var documentReference = new ActionReference();
        documentReference.putProperty(typeID("Prpr"), typeID("DocI"));
        documentReference.putEnumerated(typeID("Dcmn"), typeID("Ordn"), typeID("Trgt"));
        var documentDescriptor = executeActionGet(documentReference);
        return documentDescriptor.getInteger(typeID("DocI"));    
    }
    catch(ex) {
        return -1;        
    }
};
Photoshop.prototype.SetActiveDocument = function(document) {
    
    this.SetActiveDocumentFromId(document.DocumentId);
};
Photoshop.prototype.SetActiveDocumentFromId = function(documentId) {
    
    var documentDescriptor = new ActionDescriptor();
    var documentReference = new ActionReference();
    documentReference.putIdentifier(typeID("Dcmn"), documentId);
    documentDescriptor.putReference(typeID("null"), documentReference);
    executeAction(typeID("slct"), documentDescriptor, DialogModes.NO);
};
Photoshop.prototype.GetRulerUnits = function() {
    return preferences.rulerUnits; 
};
Photoshop.prototype.SetRulerUnits = function (rulerUnits) {
    preferences.rulerUnits = rulerUnits;
};
Photoshop.prototype.GetTypeUnits = function () {
    return preferences.typeUnits;
};
Photoshop.prototype.SetTypeUnits = function (typeUnits) {
    preferences.typeUnits = typeUnits;
};


PHP 角色权限控制

所有的web都是通过资源、css、描述语句、js等展现的一个页面、按钮、链接等等,而这些页面、按钮、链接、只有通过触发才可以进行激活,所以可以看待为是一个面向对象的程序、这个按钮底部实现的逻辑可以是后端、前端来实现、如何控制前端、后端逻辑需要控制模型。常用的控制权限有两种模型可以用:RBAC、AUTH。前者在百度百科上有完整的模型概念,后者是没有的。我也在网络上搜索了一下AUTH,看到有人博客写AUTH2.0,我觉得如果你不理解概念,那就不要去写。容易误导一些没有概念的人,然后这些人又很有理的说给下一个人,久而久之错误的概念会像瘟疫一样散播。搜索AUTH除了看到OAUTH2.0,其余的没有看到。而AUTH和OAUTH2.0是两种完全不同的东西,如果你想了解OAUTH2.0,可以查看rfc6749

概念

RBAC:用于角色权限访问控制,基于判断节点。

AUTH:用于角色权限访问控制,基于规则判断节点。

它们的粒度是一致的,不同的是对节点的判断方式。

例子

例如:有一个类(class)类里有一个函数(function)。现在要限制用户A能够访问到function。建表如下

用户表

idnamenum
1用户A20
节点表

idnode

2class/function
关联表

iduser_idnode_idnode_rule
112num>10

RBAC 判断:首先获取用户A的id为1,然后查找关联表user_id为1的node_id,最后通过node_id来获取到node,获取到的node就是用户A能够访问的节点。有些节点你没有写进节点表,那就代表是公共的,可以随意使用的,所以判断的时候要判断当前节点是否在节点表里,如果不在,就没有必要判断节点了。

AUTH判断:首先获取用户A的num,然后查找关联表node_rule的规则,发现如果num>10则可以获取到node_id为2。最后通过node_id去获取节点表。一样的要判断公共的节点。

上述描述少了一个角色组和角色的分类描述,这些都无关紧要。

如何实现前端控制

MVC结构如果你了解过这种结构的实现方式的话,每一个V层的实现都是需要一个模板引擎的,就像smarty。更重要的是PHP支持HTML混编。要怎么实现一个自己的模板引擎 ?下面其实是一个正常的html。

<!DOCTYPE html>
    <html>
        <head>
            <title></title>
        </head>
        <body>
            {$echo}
        </body>
    </html>

在body节点有一个很特殊的“代码”{$echo},为什么有的模板标签非要有花括号,或者中括号,或者其它的开始结束标签,实际只有一个目的,方便正则替换。原理:读取html文件,php正则提取所有的模板标签,替换模板标签里的语句,或者变量。这也就是为什么有缓存这个模板概念,因为替换完以后是需要输出或者保存后再输出的。有了上述的概念以后,那么前端控制就比较容易了,TP或者微擎都支持自定义模板标签函数,完全可以自己定义一个函数,函数里读取用户的ID,然后获取当前的URL,通过判断当前的URL与你的节点库里的URL进行对比返回一个布尔值来确定是否展示,或者是否可触发即可。