在 Office 文檔的一些有趣的設計,顏色和畫刷是可以繼承的,這個繼承包括了屬性的繼承。在形狀填充裡面使用的漸變色是可以一部分屬性放在主題裡面,主要找到主題裡面的畫刷,替換掉形狀自己定義的內容,才是形狀的畫刷 ...
在 Office 文檔的一些有趣的設計,顏色和畫刷是可以繼承的,這個繼承包括了屬性的繼承。在形狀填充裡面使用的漸變色是可以一部分屬性放在主題裡面,主要找到主題裡面的畫刷,替換掉形狀自己定義的內容,才是形狀的畫刷
我拿到一份有趣的課件,從這份課件的表現上,可以找到在一個 Shape 元素裡面的 ShapeProperties 定義了 GradientFill 屬性
// OpenXmlElementList element
var gradientFill = element.First<GradientFill>();
而這個 gradientFill 的 GradientStopList 的值是空
if (gradientFill.GradientStopList != null)
{
}
通過 Office Open XML - DrawingML - Shapes - Gradient Fill 可以知道,這個 GradientStopList 是用來定義漸變的顏色的,也就是這個值是空那麼就是漸變將會丟失顏色,也就是形狀填充丟失漸變色
從文檔上看,形狀屬性定義裡面沒有 gsLst
的值
<p:spPr>
<a:xfrm>
<a:off x="611560" y="1059582"/>
<a:ext cx="2120518" cy="645160"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
<a:gradFill flip="none" rotWithShape="1">
<a:lin ang="10800000" scaled="1"/>
<a:tileRect/>
</a:gradFill>
</p:spPr>
也就是 a:gradFill
裡面不包含 a:gsLst
的值,也就是沒有 a:gs
定義顏色
這個問題是 OpenXML a:gradFill without a:gsLst
漸變色裡面沒有 GradientStopList 的值
而在這個形狀一般都可以看到 ShapeStyle 的值,這個值裡面有一個屬性是 FillReference 表示樣式裡面的填充
<p:style>
<a:fillRef idx="2">
<a:schemeClr val="accent3"/>
</a:fillRef>
</p:style>
而這個樣式要的屬性是 idx 屬性,表示屬於主題的哪個樣式
那麼在 OpenXML SDK 裡面如何獲取主題,如果是在 Slide 頁面的元素可以通過下麵的代碼獲取主題
// Slide slide
var slidePart = slide.SlidePart;
FormatScheme formatScheme = slidePart?.ThemeOverridePart?.ThemeOverride?.FormatScheme;
當前,如果 Slide 拿不到就從 SlideLayoutPart 拿,再拿不到就從 SlideMasterPart 拿
然後是通過 FillReference 的 idx 從 FormatScheme 的 FillStyleList 拿到指定的元素,註意這裡的 idx 使用的是從 1 開始的下標。但是 FillStyleList 的數組是 C# 的數組,下標是從 0 開始
// FillReference reference, FormatScheme formatScheme
if (reference.Index != null && formatScheme != null)
{
var index = (int) reference.Index.Value;
var openXmlElementList = formatScheme.FillStyleList?.ChildElements;
if (openXmlElementList != null)
{
return GetThemeElement(index, openXmlElementList);
}
}
而 GetThemeElement 方法的實現如下
private static OpenXmlElement GetThemeElement(int index, OpenXmlElementList elements)
{
if (index > 0 && elements != null && elements.Count >= index)
{
//GetItem是0 base的數組,所以需要減去1
var xmlElement = elements.GetItem(index - 1);
return xmlElement;
}
return null;
}
此時就能拿到 OpenXmlElement 返回值了,這個 OpenXmlElement 是某個填充顏色。在 OpenXML SDK 裡面沒有給填充顏色一個基類,這部分的設計不是很好
在 PPT 文檔裡面對應的是 ppt\theme\themex.xml
文件裡面的 a:fillStyleLst
的值
<a:fmtScheme name="Office">
<a:fillStyleLst>
<a:solidFill>
<a:schemeClr val="phClr"/>
</a:solidFill>
<a:gradFill rotWithShape="1">
<a:gsLst>
<a:gs pos="0">
<a:schemeClr val="phClr">
<a:tint val="50000"/>
<a:satMod val="300000"/>
</a:schemeClr>
</a:gs>
<a:gs pos="35000">
<a:schemeClr val="phClr">
<a:tint val="37000"/>
<a:satMod val="300000"/>
</a:schemeClr>
</a:gs>
<a:gs pos="100000">
<a:schemeClr val="phClr">
<a:tint val="15000"/>
<a:satMod val="350000"/>
</a:schemeClr>
</a:gs>
</a:gsLst>
<a:lin ang="16200000" scaled="1"/>
</a:gradFill>
<!-- 忽略代碼 -->
上面文檔裡面用的是 idx 是 2 也就是對應 a:fillStyleLst
的第二項,也就是 a:gradFill
漸變的值
此時的主題的 a:gradFill
的 a:gsLst
將會被形狀的填充用到,如果形狀的填充的顏色也是漸變色,如果這個漸變色沒有設置 a:gsLst
的值,那麼將會採用主題裡面的 a:gsLst
的值。如果形狀自己定義了就使用形狀定義的
請看下圖就知道如何獲取
大部分的存在繼承關係和聯繫的都在 OpenXML SDK 裡面寫出來關係了,只有這些比較邊角的功能需要自己實現
而漸變色的各個屬性的行為請看 Office Open XML - DrawingML - Shapes - Gradient Fill
官方文檔請看 GradientFill Class (DocumentFormat.OpenXml.Drawing)