🙆

Windows限定でSVGファイルを画像化する

2021/06/27に公開約4,000字

SVGファイルを画像化したい場合、SharpVectorsなどのライブラリーがありますが、Windowsプラットフォーム限定であればライブラリー無しで一応は実現できます。

SVGのレンダリング

古き良き(?)WbBrowserコントロールを使います。
最新の規格に追随することは望めませんが、基本的なレンダリングできると思われます。

WindowsでSVGを表示するアプリケーションの先行事例(ブラウザー系を除く)としてはmicrosoft/PowerToysがあります。
SvgThumbnailProvider.cs
を参考にすれば大体実装できます。

大まかな方式

  1. WebBrowserSiteBaseを継承したクラスで制限をかける。
  2. WebBrowserコントロールを(フォームに追加せずに)作成する。
  3. WebBrowser.DocumentTextでSVGファイルを読み込ませる。
  4. ロードが終わるまでApplication.DoEvents();する。(力業!)
  5. WebBrowser.DrawToBitmap()でビットマップに描画する。

WebBrowserのセキュリティ

SVGファイルにスクリプトを書けますし、外部リソースへのリンクも出来ます。

インプロセスのWebBrowserコントロールでそれが出来ては困りますが、
WebBrowserSiteBaseを継承したクラスで制限をかけているようです。

/// <summary>
/// Extend the WebBrowserSite with IDispatch implementation to handle the DISPID_AMBIENT_DLCONTROL.
/// More details: https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa770041(v=vs.85)?redirectedfrom=MSDN#controlling-download-and-execution.
/// </summary>
protected class WebBrowserSiteExt : WebBrowserSite, IReflect
{
    // Dispid of DISPID_AMBIENT_DLCONTROL is defined in MsHtmdid.h header file in distributed Windows Sdk component.
    private const string DISPIDAMBIENTDLCONTROL = "[DISPID=-5512]";
...
    public object? InvokeMember(string name, BindingFlags invokeAttr, Binder? binder, object? target, object?[]? args, ParameterModifier[]? modifiers, CultureInfo? culture, string[]? namedParameters)
    {
        if (name?.Equals(DISPIDAMBIENTDLCONTROL, StringComparison.CurrentCulture) == true)
        {
            // Using InvariantCulture since this is used for web browser configurations
            return Convert.ToInt32(
                WebBrowserDownloadControlFlags.DLIMAGES |
                WebBrowserDownloadControlFlags.PRAGMA_NO_CACHE |
                WebBrowserDownloadControlFlags.FORCEOFFLINE |
                WebBrowserDownloadControlFlags.NO_CLIENTPULL |
                WebBrowserDownloadControlFlags.NO_SCRIPTS |
                WebBrowserDownloadControlFlags.NO_JAVA |
                WebBrowserDownloadControlFlags.NO_FRAMEDOWNLOAD |
                WebBrowserDownloadControlFlags.NOFRAMES |
                WebBrowserDownloadControlFlags.NO_DLACTIVEXCTLS |
                WebBrowserDownloadControlFlags.NO_RUNACTIVEXCTLS |
                WebBrowserDownloadControlFlags.NO_BEHAVIORS |
                WebBrowserDownloadControlFlags.SILENT, CultureInfo.InvariantCulture);
        }

この他にも、スレッド/プロセス単位で制限をかけることが出来ます。

Introduction to Feature Controls (Windows) | Microsoft Docs

手元のWindows 10 21H1では、下記の様な設定になっておりほぼ問題が無い状態でしたが、使用するユーザーの設定により緩められている可能性があるので、念のためアプリケーション側で上書きしておくで、安心することが出来ます。

CoInternetIsFeatureEnabled で取得した結果
FEATURE_OBJECT_CACHING = 0
FEATURE_ZONE_ELEVATION = 1
FEATURE_MIME_HANDLING = 1
FEATURE_MIME_SNIFFING = 1
FEATURE_WINDOW_RESTRICTIONS = 1
FEATURE_WEBOC_POPUPMANAGEMENT = 1
FEATURE_BEHAVIORS = 0
FEATURE_DISABLE_MK_PROTOCOL = 0
FEATURE_LOCALMACHINE_LOCKDOWN = 1
FEATURE_SECURITYBAND = 1
FEATURE_RESTRICT_ACTIVEXINSTALL = 1
FEATURE_VALIDATE_NAVIGATE_URL = 1
FEATURE_RESTRICT_FILEDOWNLOAD = 1
FEATURE_ADDON_MANAGEMENT = 1
FEATURE_PROTOCOL_LOCKDOWN = 1
FEATURE_HTTP_USERNAME_PASSWORD_DISABLE = 1
FEATURE_SAFE_BINDTOOBJECT = 1
FEATURE_UNC_SAVEDFILECHECK = 1
FEATURE_GET_URL_DOM_FILEPATH_UNENCODED = 0
FEATURE_TABBED_BROWSING = 1
FEATURE_SSLUX = 1
FEATURE_DISABLE_NAVIGATION_SOUNDS = 1
FEATURE_DISABLE_LEGACY_COMPRESSION = 0
FEATURE_FORCE_ADDR_AND_STATUS = 1
FEATURE_XMLHTTP = 0
FEATURE_DISABLE_TELNET_PROTOCOL = 1
FEATURE_FEEDS = 1
FEATURE_BLOCK_INPUT_PROMPTS = 1

制限をかける場合は、P/InvokeでCoInternetSetFeatureEnabled関数を呼び出します。

サンプルアプリケーション

必要な処理をまとめたサンプルを下記に置いておきました。

https://github.com/udaken/WinSvgRenderer

Discussion

ログインするとコメントできます