WPF で PowerPoint シリーズを作る: OpenXML 解析に基づく PPT テキスト縁取り効果の実現

WPF で PowerPoint シリーズを作る: OpenXML 解析に基づく PPT テキスト縁取り効果の実現

この記事は WPF で PowerPoint シリーズを作るブログです。PPT のテキスト縁取り効果を解析し、WPF アプリケーションでピクセルレベルで同じように描画する方法を紹介します。

最終更新 2021/12/18 18:24
lindexi
読了目安 4 分
カテゴリ
WPF
タグ
.NET WPF OpenXML

本記事は、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内のテキストボックスも図形(Shape)であり、デフォルトでは矩形です。

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);

テキストには複数の段落が含まれ、段落内ではテキストに異なるスタイル(例:一部だけ太字)を適用できます。同じスタイルのテキストは1つの 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 颜色转换 の方法を使用して、colorTextSolidColorBrush オブジェクトに変換できます。

最後にテキストの内容を取得すれば、おおむね完了です。

// デフォルトのフォント前景色は黒です

var text = run.Text!.Text;

次は、UIに描画する段階です。

描画

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 をUIに描画します。

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と同じ外観のテキストをUIに描画できます。

コード

この記事のすべてのコードとテストファイルは、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 フォルダに移動してください。

参考

  1. WPF 文字描边
  2. 詳細については、Office 使用 OpenXML SDK 解析文档博客目录 を参照してください。

このブログ記事はバックアップです。公開後は更新されません。最新のブログをご覧になりたい場合は、https://blog.lindexi.com/ にアクセスしてください。

クリエイティブ・コモンズ・ライセンス

この作品は、クリエイティブ・コモンズ 表示 - 非営利 - 継承 4.0 国際 ライセンス の下に提供されています。転載、使用、再公開は自由ですが、記事の著者名 林德熙 (リンク: http://blog.csdn.net/lindexi_gd を含む) を必ず明記し、営利目的での使用は禁止し、この記事に基づいて修正された作品は同じライセンスで公開する必要があります。ご質問がある場合は、メール でご連絡ください。

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2025/09/13

WPF から Avalonia への移行シリーズ:なぜ WPF プログラムを Avalonia に移行しなければならないのか

過去数年間、当社の上位機ソフトウェアは主に WPF と WinForm で開発されてきました。これらの技術は Windows プラットフォームで非常に便利であり、小規模試作から現在の規模拡大による納品まで、私たちを支えてきました。しかし、ビジネスの発展や顧客ニーズの変化に伴い、単一の Windows テクノロジースタックは私たちが必ず乗り越えなければならない壁となってきました。

続きを読む
同じカテゴリ / 同じタグ 2025/01/26

WPF カスタムXMLファイルによる国際化

この記事では、WPFプログラムでカスタムXMLファイルを使用して国際化を実現する方法について詳しく説明します。必要なNuGetパッケージのインストール、言語リストの動的取得、言語の動的切り替え、コードおよびXAMLインターフェースでの翻訳文字列の使用などを含み、ソースコードのリンクも提供し、開発者がWPFアプリケーションの国際化を簡単に実装できるように支援します。

続きを読む