.NET 6で図を描く方法は?

.NET 6で図を描く方法は?

Microsoftの資料を調べたところ、.NET 6以降はWindowsでのみ使用可能であることがわかりました。ただし、公式からいくつかの解決策が提示されています。

最終更新 2023/05/28 21:31
VleaStwo
読了目安 6 分
カテゴリ
.NET
タグ
.NET C#

本記事は読者からの投稿です。より多くの方にご共有いただければ幸いです。

著者:VleaStwo

1. 言語背景

  1. .NET 6/7/8
  2. ASP.NET Core Blazor

2. 要件背景

  1. URLやその他の情報を二次元コード(QRコード)に変換する

    • 端末でスキャンして情報を表示するため
  2. QRコードの周辺にテキスト情報を配置する

    • ユーザーが直接情報を確認できるようにする (部分的に)

3. 解決方法の流れ

  1. QRコード生成ツール

QRコード生成のバージョンは多数ありますが、ここでは Net.Codecrete.QrCodeGenerator v2.0.3 を採用しました。

  • リンク:

GitHubリンク: https://github.com/manuelbl/QrCodeGenerator

NuGetリンク: https://www.nuget.org/packages/Net.Codecrete.QrCodeGenerator/2.0.3

特に問題はなかったため、他の手段は試していません。どのツールも大差ありません。

  1. 描画ツール

    • グループ内の先輩が提案した「Graphics」を試しましたが、失敗しました

  • Microsoftの資料を確認したところ、.NET 6以降はWindowsでのみ使用可能であることがわかりました。ただ、公式からいくつかの解決策も提示されていました:

  • 私が選んだのは SkiaSharp v2.88.3 です。
  • リンク:

GitHubリンク: https://github.com/mono/SkiaSharp

Nutgetリンク: https://www.nuget.org/packages/SkiaSharp/2.88.3

  1. レイアウト

QRコードとテキストの位置関係は基本的に固定です。手書きで紙に描くか、パソコン上で配置図を作成するかは自由ですが、事前に図を描く目的は位置の計算を容易にするためです。実際のレイアウトはご自身の業務に応じて設計してください(頭脳明晰な方は描かなくても大丈夫、頭脳キャッシュで十分です)。

実装コード

  1. QRコード生成

解説は不要で、一行コードで解決します:

// 呼び出し文
var qr = QrCode.EncodeText(mes, QrCode.Ecc.Quartile);

// ライブラリのソースコード
// 簡略化メモ
//    text: エンコードするテキスト
//    ecl:  QRコードの品質レベル
public static QrCode EncodeText(string text, Ecc ecl)
{
    Objects.RequireNonNull(text);
    Objects.RequireNonNull(ecl);
    var segments = QrSegment.MakeSegments(text);
    return EncodeSegments(segments, ecl);
}
  1. 描画
  • qrをビットマップのようなクラスに変換してから二次描画を行う必要があります。

https://www.nuget.org/packages/Net.Codecrete.QrCodeGenerator/2.0.3

には、プラットフォームごとに異なる方法が必要であることが記載されています。

Raster Images / Bitmaps Starting with .NET 6, System.Drawing is only supported on Windows operating system and thus cannot be used for multi-platform libraries like this one. Therefore, ToBitmap() has been removed and three options are now offered in the form of method extensions. To use it: Select one of the imaging libraries below Add the NuGet dependencies to your project Copy the appropriate QrCodeBitmapExtensions.cs file to your project Imaging library Recommendation NuGet dependencies Extension file System.Drawing For Windows only projects System.Drawing.Common QrCodeBitmapExtensions.cs SkiaSharp For macOS, Linux, iOS, Android and multi-platform projects SkiaSharp and SkiaSharp.NativeAssets.Linux (for Linux only) QrCodeBitmapExtensions.cs ImageSharp Currently in beta state SixLabors.ImageSharp.Drawing QrCodeBitmapExtensions.cs

上記の意味は、自分のニーズに応じて適切な変換方法を選択し、GitHubですでに用意されているデモ用の拡張クラスを直接コピーして使用できるということです。

  • ここでは SkiaSharp を使用します。まず上記のqrを利用可能な SKBitmap に変換する必要があります。変換方法はプロジェクトのGitHubから対応する拡張クラスを直接ダウンロードできます。ここでは直接ソースコードのリンクを掲載しますので、ご自身でダウンロードしてください: QrCode转SkiaSharp(SKBitmap)源码
<param name="scale">The width and height, in pixels, of each module.(翻译: 宽度和高度,以像素为单位,每个模块)</param>
<param name="border">The number of border modules to add to each of the four sides.(翻译: 边界模块的数量增加的四个方面)</param>
var bmp = qr.ToBitmap(10, 2);
  • bmpを取得した後、自分のレイアウト設定に基づいて、画像の寸法をベースとし、その1/10を文字の高さとします。
int h = bmp.Height / 10;
  • ペンを作成します。引数はよくあるものなので説明は省略します。
var paint = new SKPaint
{
    Color = SKColors.Black,
    IsAntialias = true,
    IsLinearText = true,
    TextEncoding = SKTextEncoding.Utf8,
    Typeface = SKTypeface.FromFamilyName("微软雅黑"),
    TextSize = h,
    TextAlign = SKTextAlign.Left,
};
  • さらに、上部領域用に文字サイズ2倍のペンを作成します。
var tempPaint = paint.Clone();
tempPaint.TextSize = 2 * h;
  • 描画テーブル(キャンバス)を作成します。
SKSurface sKSurface = SKSurface.Create(
    new SKImageInfo(bmp.Width * 3, bmp.Height * 2));
  • キャンバスを作成し、白色で塗りつぶします。
var canvas = sKSurface.Canvas;
canvas.DrawColor(SKColors.White);
  • 上部領域を描画します(通常はあらかじめ用意された上部画像があるはずです)。
// 左から1単位インデントし、下方向に4単位オフセット
canvas.DrawText(top,
    new SKPoint(01 * h, 04 * h),
    tempPaint);
// topはstring型、上部領域のテキスト内容
// SKPointは通常のx,yで構成されるポイント
// 左上が(0,0)、右下が(∞,∞)であることを覚えておけば問題ありません。
  • タイトル領域を描画します。
// まず自作のアルゴリズムでstringをList<string>に変換
// 目的はテキストが長いときに改行できるようにすること
var titles = GetTextLines(title, 28).ToList();
for (int i = 0; i < titles.Count; i++)
{
    canvas.DrawText(
        titles[i],
        new SKPoint(01 * h, (07 + i) * h),
        paint);
}
  • 使用した改行アルゴリズムのソースコードは以下の通りです。
/// <summary>
/// 文字改行アルゴリズム
/// </summary>
/// <param name="text">原文</param>
/// <param name="maxLength">一行あたりの最大文字数</param>
/// <returns>複数行</returns>
private static IEnumerable<string> GetTextLines(
    string text, int maxLength)
{
    // 注意:maxLengthは文字数であり、実際の幅ではない
    string[] words = text.Select(x => x.ToString()).ToArray(); //text.Split(' ');
    List<string> lines = new List<string>();
    string line = string.Empty;
    foreach (string word in words)
    {
        //if (!string.IsNullOrEmpty(line))
        //{
        //    line += " ";
        //}
        if (line.Length + word.Length <= maxLength)
        {
            line += word;
        }
        else
        {
            lines.Add(line);
            line = word;
        }
    }
    lines.Add(line);
    return lines;
}

注意:これはネット上で見つけたものを一部修正したもので、私がゼロから書いたものではありません。

  • 左下のQRコード
// QRコード自体が周囲にインデント処理を施しているため、さらにインデントする必要はありません。
canvas.DrawBitmap(bmp, new SKPoint(00 * h, 10 * h));
  • 右下のテキストメモ
int row = 1;
for (int i = 0; i < notes.Count; i++)
{
    var strs = GetTextLines(notes[i], 18);
    foreach (var str in strs)
    {
        // 右下の10h位置、左右それぞれ1hインデント
        // row*h テキストを下に移動する位置
        canvas.DrawText(str,
        new SKPoint(10 * h, (10 + 1) * h + row * h), 
        paint);

        row++;
    }
}
  • 最後に出力
// filename 保存するファイル名(pngファイルであること)
using (var image = sKSurface.Snapshot())
{
    using (var writeStream = File.OpenWrite(filename))
    {
        // 80は品質、十分です
        image.Encode(
            SKEncodedImageFormat.Png,
            80)
            .SaveTo(writeStream);
    }
}

実際の効果

  • テストコード
static void Main(string[] args)
{
    // QRコードの内容
    string mes = "https://www.dotnet9.com/";
    // タイトル
    string title = "ThingsGateway 基于net6/7+ ,跨平台边缘采集(物联网)网关";
    // テキストメモ
    List<string> notes = new List<string>()
    {
        "Blazor Server架构,开发部署更简单",
        "采集/上传配置完全支持Excel导入导出",
        "插件式驱动,方便驱动二次开发",
        "时序数据库存储",
        "实时/历史报警(Sql转储),支持布尔/高低限值",
    };

    string filename = 
        Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".png");
    // 上部領域の内容
    string top = "开源.NET 7和Blazor组合开发";

    // カプセル化されたメソッド
    QrCreate.HorizontalDraw(mes, title, notes, filename, top);


    Console.WriteLine(filename);
    Console.ReadLine();
}
  • テスト結果

注:画像は白背景です。現在のページも白背景の場合は画像の境界が見えにくいことがあります。その場合は画像をダウンロードしてご覧ください。

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2026/04/22

各OSバージョンの.NETサポート状況(250707更新)

仮想マシンとテストマシンを使用して、各OSバージョンの.NETサポート状況を確認します。OSインストール後、対応するランタイムをインストールし、Stardustエージェントを実行できることを確認します(合格条件)。

続きを読む
同じカテゴリ / 同じタグ 2026/02/07

AOTの使用経験のまとめ

プロジェクト作成当初から、新機能を追加したり新しい構文を使用したりした場合には、すぐにAOT公開テストを実施するという良い習慣を身につけるべきです。

続きを読む