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

分類:技術 時間:2016-09-30

一、需求分析

對于視頻后期剪輯及相關從業人員來說,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個lt;Itemgt;標簽:

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

這張圖是關鍵的:我們可以看到,文件夾中的子元素是以lt;Sfdrgt;標簽來包裹的。而不管是Composite還是文件夾,都是以lt;Itemgt;標簽來表示的,只不過以子標簽lt;idtagt;的值來區分。0001開頭的表示是文件夾,0004開頭的表示合成,而0007開頭的則表示是其他普通資源文件,如圖片、AI文件等。經過分析,文本都是以lt;Layrgt;標簽包裹的,我們要替換文本的話,直接替換子標簽lt;stringgt;中的文本即可。那么圖片是怎樣一種結構呢?

圖片資源的引用是封裝在lt;Pingt;標簽里面的lt;fileReferencegt;里面,直接以路徑的形式引用。確定了這些東西,就可以開始編碼來定位文本和圖片了。這里采用了一個C XML解析庫TinyXML,不依賴其他外部庫,接口簡單。

void XMLParser::parseTemplateItem(XMLNode* rootElement, intamp; index){	if (rootElement == nullptr)	{		return;	}	XMLElement* str = rootElement-gt;FirstChildElement("string");	const char* txt = str-gt;GetText();	XMLElement* idtaNode = rootElement-gt;FirstChildElement("idta");	if (idtaNode != nullptr)	{		const char* idatBdata = http://www.cnblogs.com/csuftzzk/p/idtaNode->Attribute("bdata");		ItemType itemType = whichType(idatBdata);		if (itemType == NORMAL_ITEM)		{			XMLElement* pinNode = idtaNode-gt;NextSiblingElement("Pin");			if (pinNode != nullptr)			{				XMLElement* sspcNode = pinNode-gt;FirstChildElement("sspc");				if (sspcNode == nullptr)				{					return;				}				const char* sspcBdata = http://www.cnblogs.com/csuftzzk/p/sspcNode->Attribute("bdata");				bool isNormalFormat = isImageFormat(sspcBdata);				if (isNormalFormat)				{					XMLElement* Als2Node = sspcNode-gt;NextSiblingElement("Als2");					if (Als2Node == nullptr)					{						return;					}					XMLElement* fileReferenceNode = Als2Node-gt;FirstChildElement("fileReference");					if (fileReferenceNode == nullptr)					{						return;					}					const char* fullPath = fileReferenceNode-gt;Attribute("fullpath");					m_imageMap.insertMulti(fullPath, index);					index  ;				}			}		}		else if (itemType == COMPOSITE_ITEM)		{			XMLElement* LayrNode = idtaNode-gt;NextSiblingElement("Layr");			while (LayrNode != nullptr)			{				XMLElement* stringNode = LayrNode-gt;FirstChildElement("string");				if (stringNode)				{					// 文本為空的層直接跳過不要					const char* layerStr = stringNode-gt;GetText();					if (layerStr != nullptr amp;amp; strcmp(layerStr, ""))					{						XMLElement* tdgpOuter = stringNode-gt;NextSiblingElement("tdgp");						if (tdgpOuter)						{							XMLElement* tdmnOuter = tdgpOuter-gt;FirstChildElement("tdmn");							if (tdmnOuter)							{								const char* tdmnOuterBdata = http://www.cnblogs.com/csuftzzk/p/tdmnOuter->Attribute("bdata");								// 'ADBE Text Properties'								if (tdmnOuterBdata != nullptr amp;amp; !strcmp("4144424520546578742050726f706572746965730000000000000000000000000000000000000000", tdmnOuterBdata))								{									XMLElement* tdgpInner = tdmnOuter-gt;NextSiblingElement("tdgp");									if (tdgpInner != nullptr)									{										XMLElement* tdmnInner = tdgpInner-gt;FirstChildElement("tdmn");										if (tdmnInner != nullptr)										{											const char* tdmnInnerBdata = http://www.cnblogs.com/csuftzzk/p/tdmnInner->Attribute("bdata");											// 'ADBE Text Document'											if (tdmnInnerBdata != nullptr || !strcmp("41444245205465787420446f63756d656e7400000000000000000000000000000000000000000000", tdmnInnerBdata))											{												m_textMap.insertMulti(layerStr, index);												index  ;											}										}									}								}							}						}					}				}				LayrNode = LayrNode-gt;NextSiblingElement("Layr");			}		}		else if (itemType == FOLDER_ITEM)		{			XMLElement* SfdrNode = idtaNode-gt;NextSiblingElement("Sfdr");			if (SfdrNode == nullptr)			{				return;			}			XMLElement* tempItem = SfdrNode-gt;FirstChildElement("Item");			while (tempItem != nullptr)			{				parseTemplateItem(tempItem, index);				tempItem = tempItem-gt;NextSiblingElement("Item");			}		}		else		{			return;		}	}}

三、修改字段

完成了圖片和文字的解析工作之后,剩下的就是替換了。不妨先來觀察下使用AE修改資源時,XML文件會發生哪些變化。這樣,我們用程序修改時,把相關的字段也修改掉就可以了。對于圖片修改可以看下圖:

總共需要修改三個地方。其中,"4a504547"是JPEG這八個字符的十六進制表示,有兩個地方需要同時修改。如果是替換成其他格式的圖片,也要修改成對應格式的十六進制表示。如:

'706e6721'  -gt; PNG format'4a504547'  -gt; JPEG or JPG format'5449465f'   -gt; TIF or TIFF format'424d5020'  -gt; BMP format

  另外一個要修改的就是lt;fileReferencegt;的屬性fullpath值了。也就是圖片資源的路徑。文本的修改就要稍顯復雜一點了。如下圖:

這里采用了一個小技巧,使用了文本層的一個屬性:text.sourceText=name。給了這個屬性之后,文本層的內容和名稱保持一致。也即是說,我們只要修改文本層的名稱,就能達到修改文本層內容的目的。這個技巧需要修改兩個地方。一個是lt;tdb4gt;標簽值的倒數第七位置1;另一個就是增加一個lt;tdb4gt;的兄弟標簽lt;exprgt;,其值為ldquo;746578742e736f75726365546578743d6e616d6500rdquo;,也就是"text.sourceText=name"的十六進制表示。這樣就實現了文本層和文本內容的同步設置了。

此外,Layr層不光只有text在里面,還有色塊(Solid)、過渡效果、動畫等內容。因此還需要根據lt;tdmngt;標簽的值來過濾。條件就是lt;tdmngt;的值:

4144424520546578742050726f706572746965730000000000000000000000000000000000000000 // 'ADBE Text Properties'41444245205472616e73666f726d2047726f75700000000000000000000000000000000000000000 // 'ADBE Transform Group'41444245204c61796572205374796c65730000000000000000000000000000000000000000000000 // 'ADBE Layer Styles'414442452045787472736e204f7074696f6e732047726f7570000000000000000000000000000000 // 'ADBE Extrsn Options Group'41444245204d6174657269616c204f7074696f6e732047726f757000000000000000000000000000 // 'ADBE Material Options Group'4144424520417564696f2047726f7570000000000000000000000000000000000000000000000000 // 'ADBE Audio Group'414442452047726f757020456e640000000000000000000000000000000000000000000000000000 // 'ADBE Group End'41444245205465787420446f63756d656e7400000000000000000000000000000000000000000000 // 'ADBE Text Document'4144424520546578742050617468204f7074696f6e73000000000000000000000000000000000000 // 'ADBE Path Options'414442452054696d652052656d617070696e67000000000000000000000000000000000000000000 // 'ADBE Time Remapping'  4144424520506c616e65204f7074696f6e732047726f757000000000000000000000000000000000 // 'ADBE Plane Options Group' 41444245204566666563742050617261646500000000000000000000000000000000000000000000 // 'ADBE Effect Parade' 

  只有內層lt;tdmngt;和外層lt;tdmngt;的值分別是'ADBE Text Properties'和'ADBE Text Document'的時候,lt;Layrgt;中包含的才是文本。這種過濾條件,能夠過濾掉其他的干擾數據,讓我專注于處理模板中的文本內容。

四、最終效果


Tags: 二進制 工作量 Adobe 圖片 程序

文章來源:


ads
ads

相關文章
ads

相關文章

ad