本文是使用 WPF 做個 PowerPoint 系列的部落格,本文來告訴大家如何解析 PPT 裡面的文字描邊效果,在 WPF 應用中繪製出來,實現畫素級相同
背景知識
在開始之前,期望你瞭解了 PPT 解析的入門知識。如對 PPT 解析瞭解很少,請參閱 C# dotnet 使用 OpenXml 解析 PPT 檔案
在 PPT 裡面可以給文字的某些文字設定描邊效果,描邊效果從 OpenXML 層上是不屬於特效的,只是屬於邊框屬性。在 PPT 裡面,可以給文字加上 Outline 邊框屬性,從而讓文字描邊
效果
開始之前,先讓大家看一下效果
解析
開始之前,先進行讀取文件,程式碼如下。以下程式碼和測試檔案,都可以在本文末尾獲取
var file = new FileInfo("Test.pptx");
using var presentationDocument = PresentationDocument.Open(file.FullName, false);
var slide = presentationDocument.PresentationPart!.SlideParts.First().Slide;
本文以下程式碼,為了方便告訴大家核心部分邏輯,將根據 Test.pptx 文件進行忽略很多引數的判斷。在實際專案中,還請大家自行進行引數判斷邏輯
此測試文件在第一頁只有一個元素,就是本文的加文字描邊的元素,獲取的程式碼如下
var shape = slide.CommonSlideData!.ShapeTree!.GetFirstChild<Shape>()!;
此 Shape 的 OpenXML 內容大概如下
<p:sp>
<p:spPr>
<a:prstGeom prst="rect">
</a:prstGeom>
<a:noFill />
</p:spPr>
<p:txBody>
<a:bodyPr wrap="square" rtlCol="0">
<a:spAutoFit />
</a:bodyPr>
<a:lstStyle />
<a:p>
<a:r>
<a:rPr lang="zh-CN" altLang="en-US" sz="10000">
<a:ln w="9525">
<a:solidFill>
<a:srgbClr val="00FF00" />
</a:solidFill>
</a:ln>
</a:rPr>
<a:t>一行文字</a:t>
</a:r>
<a:endParaRPr lang="en-US" sz="10000" dirty="0" />
</a:p>
</p:txBody>
</p:sp>
在 PPT 裡面的文字框也是形狀,是預設的矩形
var shapeProperties = shape.ShapeProperties!;
var presetGeometry = shapeProperties.GetFirstChild<PresetGeometry>()!;
// 這是一個文字框
Debug.Assert(presetGeometry.Preset?.Value == ShapeTypeValues.Rectangle);
Debug.Assert(shapeProperties.GetFirstChild<NoFill>() is not null);
以上只是告訴大家可以如何獲取形狀,需要在自己的業務程式碼裡面,進行判斷
獲取文字框的文字,可以使用如下程式碼
var textBody = shape.TextBody!;
Debug.Assert(textBody != null);
一個文字里面有很多段落,段落裡面,文字有不同的樣式,如一段可以有不同加粗的文字。相同的樣式的文字放在一個 TextRun 裡面。不同的樣式的文字放在不同的 TextRun 裡面
因此解析需要先遍歷段落,再遍歷 TextRun 元素
foreach (var paragraph in textBody.Elements<DocumentFormat.OpenXml.Drawing.Paragraph>())
{
// 這個文字段落是沒有屬性的,為了方便樣式,就不寫程式碼
//if (paragraph.ParagraphProperties != null)
foreach (var run in paragraph.Elements<DocumentFormat.OpenXml.Drawing.Run>())
{
}
}
獲取 TextRun 的屬性如下
var runProperties = run.RunProperties!;
此屬性上可以拿到當前文字的字號等資訊,程式碼如下
var fontSize = new PoundHundredfold(runProperties.FontSize!.Value).ToPound();
接下來是本文的核心,獲取 Outline 屬性,程式碼如下
var outline = runProperties.Outline!;
對應的 OpenXML 程式碼如下
<a:ln w="9525">
<a:solidFill>
<a:srgbClr val="00FF00" />
</a:solidFill>
</a:ln>
我們所關注基本只有粗細和顏色,獲取方法分別如下
var outlineWidth = new Emu(outline.Width!.Value);
獲取顏色的程式碼如下
var solidFill = outline.GetFirstChild<SolidFill>()!;
var rgbColorModelHex = solidFill.GetFirstChild<RgbColorModelHex>()!;
var colorText = rgbColorModelHex.Val!;
通過 win10 uwp 顏色轉換 的方法可以將 colorText 轉換為 SolidColorBrush 物件
再獲取文字內容,大概就完成了
// 預設字型前景色是黑色
var text = run.Text!.Text;
接下來就是在介面繪製
繪製
如 WPF 文字描邊 部落格,先通過 FormattedText 構建出 Geometry 物件,再通過 Geometry 物件進行繪製
程式碼如下
var formattedText = new FormattedText(text, CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface
(
// 預設是宋體
new FontFamily("宋體"),
FontStyles.Normal,
FontWeights.Normal,
FontStretches.Normal
),
// 在 WPF 裡面,採用的是 EM 單位,約等於畫素單位
fontSize.ToPixel().Value,
Brushes.Black, 96);
通過 FormattedText 構建出 Geometry 物件程式碼如下
var geometry = formattedText.BuildGeometry(new ());
接著通過 System.Windows.Shapes.Path 將 Geometry 繪製到介面上
var path = new System.Windows.Shapes.Path
{
Data = geometry,
Fill = Brushes.Black,
Stroke = BrushCreator.CreateSolidColorBrush(colorText),
StrokeThickness = outlineWidth.ToPixel().Value,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
};
Root.Children.Add(path);
通過以上程式碼,即可在介面畫出和 PPT 一樣的介面
程式碼
本文所有程式碼和測試檔案放在github 和 gitee 歡迎訪問
可以通過如下方式獲取本文的原始碼,先建立一個空資料夾,接著使用命令列 cd 命令進入此空資料夾,在命令列裡面輸入以下程式碼,即可獲取到本文的程式碼
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 71af5b0e47493ff7f5f43be33583265805da9d84
以上使用的是 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
獲取程式碼之後,進入 Pptx 資料夾