一款批量修改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 ;
         }
     }
}

转载于:https://www.cnblogs.com/linnew/p/7773542.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值