• 一款批量修改AE模板的工具


     

     

    一、需求分析

         对于视频后期剪辑及相关从业人员来说,AE(After Effects)模板效果是一个不错的开始点。在模板效果的基础上,可以很快的做出各种炫酷的后期效果。但是在网上下载的模板工程中,往往包含了非常多的模板文字、图片、图形实体、AI资源等。这些资源文件往往并不是我们需要的,在使用模板时需要手动替换或者删除。但是网上下载的模板工程往往非常大,包含的资源非常多。这样手动改动起来的话,工作量会成倍增加。那么,是否可以考虑做一个小工具来高效完成这项枯燥的工作呢?要替换模板中的文字和图片,第一步就是要定位到这些图片和文字;其次才能考虑使用程序替换。那么,如何定位模板工程中的图片和文字呢?定位到之后又如何修改呢?如果要修改的话,又要修改哪些地方呢?接下来就来分析下整个解决过程。

    二、实现方案

         Adobe After Effects工程使用aep格式来存储。aep格式是一种紧凑的二进制格式,工程中的所有资源及组织结构都以二进制格式保存。如果要从这种二进制的格式中来定位图片和文字,倒也不是不可能:

        但是有一个致命的缺点。先不说定位的时候无法做到精确匹配,就算成功找到了文本或图片路径,替换的时候很可能还要进行位置移动。因为替换的文本可能比原文本长,如果不移动腾出空位的话,替换的内容就会覆盖掉后面的二进制数据。修改后的aep文件极有可能因此损坏。因此,直接修改aep文件是不可取的。经过一番搜索,得知AE工程还有另外一种存储格式:AEPX。

       

        *.aepx是以XML格式进行存储的。相对于二进制格式aep而言,aepx的文件尺寸比较大,加载速度也会慢些。但是XML格式非常容易操作,而且在成熟的XML库的帮助下,修改标签和遍历标签只需要几行代码即可搞定。那么,接下来的工作就是确定XML的组织结构以及需要修改哪些字段了。首先看一个比较复杂的AEP工程:

        这是一个典型的AEP工程,使用文件夹的方式来组织各种资源。那么XML中是怎么组织的呢?上面这个工程中存在8个顶级文件夹,可以在XML中看到对应8个<Item>标签:

        再来分析其中的合成(Composite):

        这张图是关键的:我们可以看到,文件夹中的子元素是以<Sfdr>标签来包裹的。而不管是Composite还是文件夹,都是以<Item>标签来表示的,只不过以子标签<idta>的值来区分。0001开头的表示是文件夹,0004开头的表示合成,而0007开头的则表示是其他普通资源文件,如图片、AI文件等。经过分析,文本都是以<Layr>标签包裹的,我们要替换文本的话,直接替换子标签<string>中的文本即可。那么图片是怎样一种结构呢?

        图片资源的引用是封装在<Pin>标签里面的<fileReference>里面,直接以路径的形式引用。确定了这些东西,就可以开始编码来定位文本和图片了。这里采用了一个C++ XML解析库TinyXML,不依赖其他外部库,接口简单。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    void XMLParser::parseTemplateItem(XMLNode* rootElement, int& index)
    {
        if (rootElement == nullptr)
        {
            return;
        }
     
        XMLElement* str = rootElement->FirstChildElement("string");
        const char* txt = str->GetText();
        XMLElement* idtaNode = rootElement->FirstChildElement("idta");
        if (idtaNode != nullptr)
        {
            const char* idatBdata = idtaNode->Attribute("bdata");
            ItemType itemType = whichType(idatBdata);
            if (itemType == NORMAL_ITEM)
            {
                XMLElement* pinNode = idtaNode->NextSiblingElement("Pin");
                if (pinNode != nullptr)
                {
                    XMLElement* sspcNode = pinNode->FirstChildElement("sspc");
                    if (sspcNode == nullptr)
                    {
                        return;
                    }
                    const char* sspcBdata = sspcNode->Attribute("bdata");
                    bool isNormalFormat = isImageFormat(sspcBdata);
                    if (isNormalFormat)
                    {
                        XMLElement* Als2Node = sspcNode->NextSiblingElement("Als2");
                        if (Als2Node == nullptr)
                        {
                            return;
                        }
                        XMLElement* fileReferenceNode = Als2Node->FirstChildElement("fileReference");
                        if (fileReferenceNode == nullptr)
                        {
                            return;
                        }
                        const char* fullPath = fileReferenceNode->Attribute("fullpath");
                        m_imageMap.insertMulti(fullPath, index);
                        index++;
                    }
                }
            }
            else if (itemType == COMPOSITE_ITEM)
            {
                XMLElement* LayrNode = idtaNode->NextSiblingElement("Layr");
                while (LayrNode != nullptr)
                {
                    XMLElement* stringNode = LayrNode->FirstChildElement("string");
                    if (stringNode)
                    {
                        // 文本为空的层直接跳过不要
                        const char* layerStr = stringNode->GetText();
                        if (layerStr != nullptr && strcmp(layerStr, ""))
                        {
                            XMLElement* tdgpOuter = stringNode->NextSiblingElement("tdgp");
                            if (tdgpOuter)
                            {
                                XMLElement* tdmnOuter = tdgpOuter->FirstChildElement("tdmn");
                                if (tdmnOuter)
                                {
                                    const char* tdmnOuterBdata = tdmnOuter->Attribute("bdata");
                                    // 'ADBE Text Properties'
                                    if (tdmnOuterBdata != nullptr && !strcmp("4144424520546578742050726f706572746965730000000000000000000000000000000000000000", tdmnOuterBdata))
                                    {
                                        XMLElement* tdgpInner = tdmnOuter->NextSiblingElement("tdgp");
                                        if (tdgpInner != nullptr)
                                        {
                                            XMLElement* tdmnInner = tdgpInner->FirstChildElement("tdmn");
                                            if (tdmnInner != nullptr)
                                            {
                                                const char* tdmnInnerBdata = tdmnInner->Attribute("bdata");
                                                // 'ADBE Text Document'
                                                if (tdmnInnerBdata != nullptr || !strcmp("41444245205465787420446f63756d656e7400000000000000000000000000000000000000000000", tdmnInnerBdata))
                                                {
                                                    m_textMap.insertMulti(layerStr, index);
                                                    index++;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    LayrNode = LayrNode->NextSiblingElement("Layr");
                }
            }
            else if (itemType == FOLDER_ITEM)
            {
                XMLElement* SfdrNode = idtaNode->NextSiblingElement("Sfdr");
                if (SfdrNode == nullptr)
                {
                    return;
                }
                XMLElement* tempItem = SfdrNode->FirstChildElement("Item");
                while (tempItem != nullptr)
                {
                    parseTemplateItem(tempItem, index);
                    tempItem = tempItem->NextSiblingElement("Item");
                }
            }
            else
            {
                return;
            }
        }
    }
  • 相关阅读:
    三大范式
    html 横线的代码
    CSS下拉 菜单3.27第一次
    JS页面三种打开方式及对话框
    函数整理
    3.22整理作业
    for循环,if 练习
    php测试题
    设计模式
    面向对象的三大特性
  • 原文地址:https://www.cnblogs.com/linnew/p/7773542.html
Copyright © 2020-2023  润新知