78298


ZXing.NETを使用して、C#およびWPF環境で連結QRコードを作成する方法を知りたいです。  
私はZXingを使用してQRコードを作成できることに注意してください。以下は私のC#コードです。

[code]

        string ss = "9MA0GCSqGSIb3DQEBCwUABIIBABVim6AYRyf6AmkZFdAkKvaAvMkQEGYVwa2KUz/FBeu";
        int stCount = ss.Length;
        var qrcodeWriter = new BarcodeWriter();
    
        qrcodeWriter.Format = BarcodeFormat.QR_CODE;
    
        QrCodeEncodingOptions options = new QrCodeEncodingOptions()
        {
            DisableECI = true, // ​​Set the content encoding.
            CharacterSet = "UTF-8", // Set the width and height of the QR code.
            Width = 600,
            Height = 600,
            Margin = 1 // Set the margin of the QR code, the unit is not a fixed pixel.
        };
        qrcodeWriter.Write(ss)
            .Save(@"C:\Demo_Project_Barcode\QRImage\" + $"{txtFileName.Text}.png");
    
[/code]

これにより、QRコードを作成できることがわかっています。また、以下のサイトを参考にして、連結QRコードを作ってみました。

  * [Zxingを使って連結QRコードを読み書きする Part3:連結QRコードの出力 | Program Resource](https://programresource.net/2013/05/04/2193.html)

しかし、これはJavaを使っています。そのため、C#では利用できません。

私は次のようにしてZXing.NETの`QRCodeWriter`クラスにコードを追加しました。

[code]

        public sealed class QRCodeWriter : Writer
        {
            private const int QUIET_ZONE_SIZE = 4;
    
            // Rifat Code Adding New
            public BitMatrix encode_sa(byte page, byte totalpage, int parity,
                String contents, BarcodeFormat format,
                int width, int height, IDictionary<EncodeHintType, object> hints)
            {
                if (contents.Length == 0)
                {
                    throw new ArgumentException("Found empty contents");
                }
                if (format != BarcodeFormat.QR_CODE)
                {
                    //throw new IllegalArgumentException("Can only encode QR_CODE, but got " +format);
                    throw new ArgumentException("Can only encode QR_CODE, but got " + format);
                }
    
                if (width < 0 || height < 0)
                {
                    throw new ArgumentException("Requested dimensions are too small: " + width + 'x' + height);
                }
                ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
                if (hints != null)
                {
                    // ErrorCorrectionLevel requestedECLevel = (ErrorCorrectionLevel)hints.
                    //  ErrorCorrectionLevel requestedECLevel = (ErrorCorrectionLevel)hints.get(EncodeHintType.ERROR_CORRECTION);
                    var requestedECLevel = (ErrorCorrectionLevel)hints[EncodeHintType.ERROR_CORRECTION];
    
                    if (requestedECLevel != null)
                    {
                        errorCorrectionLevel = requestedECLevel;
                    }
                }
                int quietZone = QUIET_ZONE_SIZE;
                if (hints.ContainsKey(EncodeHintType.MARGIN))
                {
                    var quietZoneInt = hints[EncodeHintType.MARGIN];
                    if (quietZoneInt != null)
                    {
                        quietZone = Convert.ToInt32(quietZoneInt.ToString());
                    }
                }
    
                QRCode code = new QRCode();
                Encoder.encode_sa(page, totalpage, parity, contents, errorCorrectionLevel, hints, code);
                return renderResult(code, width, height, quietZone);
            }
    
[/code]

また、次のようにして`Encoder`クラスにコードを追加しました。

[code]

        public static class Encoder
        {
            public static void encode_sa(byte page, byte totalpage, int parity, String content,
                               ErrorCorrectionLevel ecLevel,
                               IDictionary<EncodeHintType, object> hints,
                               QRCode qrCode) //throws WriterException
            {
    
                String encoding = hints == null ? null : (String)hints[EncodeHintType.CHARACTER_SET];
                if (encoding == null)
                {
                    encoding = DEFAULT_BYTE_MODE_ENCODING;
                }
    
                // Step 1: Choose the mode (encoding).
                Mode mode = chooseMode(content, encoding);
    
                // Step 2: Append "bytes" into "dataBits" in appropriate encoding.
                BitArray dataBits = new BitArray();
    
                appendBytes(content, mode, dataBits, encoding);
                // Step 3: Initialize QR code that can contain "dataBits".
                int numInputBits = dataBits.Size;
                //initQRCode(numInputBits, ecLevel, mode, qrCode);
                // I don't know how to initialize it. I think I have to fix it from here.
    
                // Step 4: Build another bit vector that contains header and data.
                BitArray headerAndDataBits = new BitArray();
    
                // Step 4.1: Add code page info
                byte tmpbyte;
                tmpbyte = 0x3;
                headerAndDataBits.appendBits(tmpbyte, 4);
                headerAndDataBits.appendBits(page, 4);
                headerAndDataBits.appendBits(totalpage, 4);
                headerAndDataBits.appendBits((byte)((parity & 0xf0) >> 4), 4);
                headerAndDataBits.appendBits((byte)(parity & 0x0f), 4);
    
                // Step 4.5: Append ECI message if applicable
                if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.Equals(encoding))
                {
                    CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding);
                    if (eci != null)
                    {
                        appendECI(eci, headerAndDataBits);
                    }
                }
    
                appendModeInfo(mode, headerAndDataBits);
                //BitArray dataBits = new BitArray();
                //int numInputBits = dataBits.Size;
                int numLetters = mode == Mode.BYTE ? dataBits.SizeInBytes : content.Length;
                appendLengthInfo(numLetters, qrCode.Version, mode, headerAndDataBits);
                headerAndDataBits.appendBitArray(dataBits);
    
    
                //  terminateBits(numDataBytes, headerAndDataBits);
                Version version;//=new Version();
                var headerBits = new BitArray();
                if (hints != null && hints.ContainsKey(EncodeHintType.QR_VERSION))
                {
                    int versionNumber = Int32.Parse(hints[EncodeHintType.QR_VERSION].ToString());
                    version = Version.getVersionForNumber(versionNumber);
                    int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, version);
                    if (!willFit(bitsNeeded, version, ecLevel))
                    {
                        throw new WriterException("Data too big for requested version");
                    }
                }
                else
                {
                    version = recommendVersion(ecLevel, mode, headerBits, dataBits);
                }
                var ecBlocks = version.getECBlocksForLevel(ecLevel);
                var numDataBytes = version.TotalCodewords - ecBlocks.TotalECCodewords;
    
                // Terminate the bits properly.
                terminateBits(numDataBytes, headerAndDataBits);
    
                // Interleave data bits with error correction code.
                var finalBits = interleaveWithECBytes(headerAndDataBits,
                                                           version.TotalCodewords,
                                                           numDataBytes,
                                                           ecBlocks.NumBlocks);
    
                 qrCode = new QRCode
                {
                    ECLevel = ecLevel,
                    Mode = mode,
                    Version = version
                };
    
                //  Choose the mask pattern and set to "qrCode".
                var dimension = version.DimensionForVersion;
                var matrix = new ByteMatrix(dimension, dimension);
    
                // Enable manual selection of the pattern to be used via hint
                var maskPattern = -1;
                if (hints != null && hints.ContainsKey(EncodeHintType.QR_MASK_PATTERN))
                {
                    var hintMaskPattern = Int32.Parse(hints[EncodeHintType.QR_MASK_PATTERN].ToString());
                    maskPattern = QRCode.isValidMaskPattern(hintMaskPattern) ? hintMaskPattern : -1;
                }
    
                if (maskPattern == -1)
                {
                    maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix);
                }
                qrCode.MaskPattern = maskPattern;
    
                // Build the matrix and set it to "qrCode".
                MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix);
                qrCode.Matrix = matrix;
    
                //return qrCode;
            }
        }
    
[/code]

これは私は変化しましたZXingで。  
WPFで何をすべきか、どのようにすればよいですか？  
また、JavaコードをC#に変換していますが、間違いがある可能性があります。  
よろしくお願いいたします。


CC BY-SA 4.0
2021-07-16T01:35:22.463
78217
2022-08-10T08:49:53.633
2022-08-10T08:49:53.633
3060
47312
question
c# wpf qr-code
C#でZXingを使って連結QRコードを作成する


以下のようにすれば出来るでしょう。  
作成した連結QRコードが実際に連結QRコードとして読めるかどうかは確認していませんが、何かしら形の違う4つのQRコードが出来て表示されたのは確認しました。

* * *

ZXing.Netの改造では従来のパラメータは同じまま、連結QRコード用のパラメータを追加して処理を少し変えるだけなので、メソッドのオーバーロードを利用して同じメソッド名で実装出来ます。

`ZXing.Net/Source/lib/qrcode/QRCodeWriter.cs`に連結QRコード用の`encode()`メソッド追加  
元々ある`encode()`メソッドをコピーして、連結QRコード用のパラメータを追加し、連結QRコード用のEncoder.encodeを呼ぶように変更する。

[code]

    //----------------------------------------------------------------------
    /// <summary>
    /// </summary>
    /// <param name="contents">The contents to encode in the barcode</param>
    /// <param name="format">The barcode format to generate</param>
    /// <param name="width">The preferred width in pixels</param>
    /// <param name="height">The preferred height in pixels</param>
    /// <param name="hints">Additional parameters to supply to the encoder</param>
    /// <param name="current">The current number of concatenated QR code</param> // 追加
    /// <param name="last">The last number of concatenated QR code</param>       // 追加
    /// <param name="parity">The parity value of concatenated QR code</param>    // 追加
    /// <returns>
    /// The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
    /// </returns>
    public BitMatrix encode(String contents,
                            BarcodeFormat format,
                            int width,
                            int height,
                            IDictionary<EncodeHintType, object> hints,
                            int current,  // 追加
                            int last,     // 追加
                            byte parity)  // 追加
    
[/code]

途中省略  
以下の行を連結QRコード用のEncoder.encodeを呼ぶように変更  
変更前

[code]

        var code = Encoder.encode(contents, errorCorrectionLevel, hints);
    
[/code]

変更後

[code]

        var code = Encoder.encode(contents, errorCorrectionLevel, hints, current, last, parity);
    
[/code]

以後省略

* * *

`ZXing.Net/Source/lib/qrcode/encoder/Encoder.cs`に連結QRコード用の`encode()`メソッド追加  
元々ある`encode()`メソッドをコピーして、連結QRコード用のパラメータを追加し、連結QRコード用のヘッダを作成するように変更する。

[code]

    //----------------------------------------------------------------------
    /// <summary>
    /// Encodes the specified content.
    /// </summary>
    /// <param name="content">The content.</param>
    /// <param name="ecLevel">The ec level.</param>
    /// <param name="hints">The hints.</param>
    /// <param name="current">The current.</param> // 追加
    /// <param name="last">The last.</param>       // 追加
    /// <param name="parity">The parity.</param>   // 追加
    /// <returns></returns>
    public static QRCode encode(String content,
                       ErrorCorrectionLevel ecLevel,
                       IDictionary<EncodeHintType, object> hints,
                       int current,  // 追加
                       int last,     // 追加
                       byte parity)  // 追加
    
[/code]

途中省略  
以下の行の間で連結QRコード用のヘッダを作成するように追加  
変更前

[code]

        // This will store the header information, like mode and
        // length, as well as "header" segments like an ECI segment.
        var headerBits = new BitArray();
    
        // Append ECI segment if applicable
        if (mode == Mode.BYTE && generateECI)
    
[/code]

変更後

[code]

        // This will store the header information, like mode and
        // length, as well as "header" segments like an ECI segment.
        var headerBits = new BitArray();
    
        //---------------------------------------------------↓ここから追加
        headerBits.appendBits(Mode.STRUCTURED_APPEND.Bits, 4);
        headerBits.appendBits(current, 4);
        headerBits.appendBits(last, 4);
        headerBits.appendBits((byte)((parity & 0xf0) >> 4), 4);
        headerBits.appendBits((byte)(parity & 0x0f), 4);
        //---------------------------------------------------↑ここまで追加
    
        // Append ECI segment if applicable
        if (mode == Mode.BYTE && generateECI)
    
[/code]

以後省略

* * *

アプリケーション側ではこんな感じで使えるでしょう。  
質問の紹介先からパリティ計算処理をコピーする。

[code]

    private byte calcParity(byte[] data)
    {
        byte parity;
    
        parity = data[0];
        for (int i = 1; i < data.Length; i++)
            parity = (byte)(parity ^ data[i]);//XOR
    
        return parity;
    }
    
[/code]

連結QRコード化したい文字列が`ss`に入っているとして、4個のQRコードにしたい場合：

[code]

    string ss = "9MA0GCSqGSIb3DQEBCwUABIIBABVim6AYRyf6AmkZFdAkKvaAvMkQEGYVwa2KUz/FBeu";
    byte parity = calcParity(Encoding.UTF8.GetBytes(ss));                // 全体のパリティ値
    int codesize = Convert.ToInt32(Math.Ceiling((double)ss.Length / 4)); // 個々のQRコードの文字列長
    
    var hints = new Dictionary<EncodeHintType, object> 
    {
        [EncodeHintType.CHARACTER_SET] = "UTF-8",
        [EncodeHintType.ERROR_CORRECTION] = "L",
        [EncodeHintType.MARGIN] = 4,
        [EncodeHintType.QR_VERSION] = 8  // 何か相応しいバージョン(大きさ)を指定しておく
    };
    
    Image[] images = { QrImage1, QrImage2, QrImage3, QrImage4 }; // あらかじめXAMLでImageを4つ作成しておき配列化
    
    QRCodeWriter QrWriter = new QRCodeWriter();  // QRCode作成用
    BarcodeWriter writer = new BarcodeWriter();  // Bitmapへの変換用
    
    for (int i = 0, iOffset = 0; i < 4; i++)
    {
        int length = Math.Min(codesize, ss.Length - iOffset);
        string codestr = ss.Substring(iOffset, length);  // 分割した文字列の切り出し
        iOffset += length;
    
        // 200はQRCodeイメージの適当なサイズ、3は4つの連結QRCodeにする際の最終番号
        using (var bmp = writer.Write(QrWriter.encode(codestr, BarcodeFormat.QR_CODE, 200, 200, hints, i, 3, parity)))
        using (var ms = new MemoryStream())
        {
            bmp.Save(ms, ImageFormat.Bmp);
            // WPFで扱えるImageSourceに変換
            var source = BitmapFrame.Create(ms, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
            images[i].Source = source;
        }
    }
    
[/code]

* * *

ちなみにZXingでは無いけれどこうしたライブラリもあり、こちらでは連結QRcode(structured-
append)がサポートされているようなので、選択肢として考えられるかもしれませんね。  
[libqrencode 日本語](https://fukuchi.org/works/qrencode/index.html.ja) /
[libqrencode english](https://fukuchi.org/works/qrencode/index.html.en)  
[fukuchi/libqrencode](https://github.com/fukuchi/libqrencode)


CC BY-SA 4.0
2021-07-19T13:44:56.153
78298
2021-07-19T15:46:21.480
2021-07-19T15:46:21.480
26370
26370
78217
answer


まずはVisual Studioのバージョンや、ターゲットフレームワークのバージョン (.NET Frameworkまたは.NET Coreのバージョン)
を提示してください。また、外部ライブラリを使用する場合、そのバージョンも提示するようにしてください。  
バージョンによって利用できる機能が変わるため、回答も変わる可能性があります。

ひとまずVisual Studio 2015以降、.NET Framework 4.5以降の環境であると仮定します。  
ZXing.NETのバージョンは[v0.16.6](https://github.com/micjahn/ZXing.Net/releases/tag/v0.16.6.0)と仮定します。  
また、WPF/XAMLのデータバインディングおよびMVVMパターンに関する基礎知識があるということを前提とします。

以下のサンプルでは、4つのエディットボックスに、任意の文字列を手動で分割入力しておき、"Divided QR" のチェックボックスをONにして
"Encode" ボタンを押すと、分割QR用のヘッダーが付与された分割QRコード（連結QRコード）が4つ生成されるはずです。  
（"Divided QR" のチェックボックスをOFFにすると、通常のスタンドアロンQRコードになります）

MainWindow.xaml:

[code]

    <Window x:Class="WpfZXingTest1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfZXingTest1"
            mc:Ignorable="d"
            Title="MainWindow"
            Width="800"
            Height="350"
            WindowStartupLocation="CenterScreen"
            >
        <Grid Margin="10">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="10"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <ItemsControl Grid.Row="0" x:Name="qrImageListPanel">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Vertical" Margin="10">
                            <TextBox Text="{Binding QRCodeString}"/>
                            <Path Data="{Binding QRCodeGeometry}" MinWidth="100" MinHeight="100" Width="Auto" Height="Auto" Margin="10" Fill="Black"/>
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
            <Button Grid.Row="2" x:Name="buttonEncode" Click="buttonEncode_Click" Content="Encode" HorizontalAlignment="Right" Padding="10,5"/>
            <CheckBox Grid.Row="2" x:Name="checkDividedQR" Content="Divided QR" HorizontalAlignment="Left" VerticalAlignment="Center"/>
        </Grid>
    </Window>
    
[/code]

MainWindow.xaml.cs:

[code]

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace WpfZXingTest1
    {
        /// <summary>
        /// MainWindow.xaml の相互作用ロジック
        /// </summary>
        public partial class MainWindow : Window
        {
            private const int NumberOfDivisions = 4;
    
            public MainWindow()
            {
                InitializeComponent();
    
                this.checkDividedQR.IsChecked = true;
    
                for (int i = 0; i < NumberOfDivisions; ++i)
                {
                    _dividedQRCodeList.Add(new MyQRCodeViewModel() { QRCodeString = "abc" + i });
                }
    
                var qrCode = new ZXing.QrCode.Internal.QRCode();
    
                this.qrImageListPanel.ItemsSource = _dividedQRCodeList;
            }
    
            private readonly ObservableCollection<MyQRCodeViewModel> _dividedQRCodeList = new ObservableCollection<MyQRCodeViewModel>();
    
            // Use ZXing.Presentation.BarcodeWriterGeometry for Geometry.
            // Use ZXing.Presentation.BarcodeWriter for WriteableBitmap.
            // https://github.com/micjahn/ZXing.Net/blob/master/Clients/WPFDemo/WPFDemo/MainWindow.xaml.cs
    
            private void BuildStandaloneQRCodes()
            {
                foreach (var qrCode in _dividedQRCodeList)
                {
                    var writer = new ZXing.Presentation.BarcodeWriterGeometry
                    {
                        Format = ZXing.BarcodeFormat.QR_CODE,
                        Options = new ZXing.Common.EncodingOptions
                        {
                            Width = 100,
                            Height = 100,
                            Margin = 4,
                            Hints =
                                {
                                    { ZXing.EncodeHintType.ERROR_CORRECTION, ZXing.QrCode.Internal.ErrorCorrectionLevel.H },
                                    { ZXing.EncodeHintType.CHARACTER_SET, MyQRCodeHelper.EncodingName_UTF_8 },
                                },
                        },
                    };
                    var geom = writer.Write(qrCode.QRCodeString);
                    qrCode.QRCodeGeometry = geom;
                }
            }
    
            private void BuildDividedQRCodes()
            {
                if (_dividedQRCodeList.Count > MyQRCodeHelper.MaxNumberOfDivisions)
                {
                    throw new NotSupportedException($"Number of divisions must be less than or equal to {MyQRCodeHelper.MaxNumberOfDivisions}.");
                }
    
                // Connect manually-divided source strings.
                byte count = (byte)_dividedQRCodeList.Count;
                string connectedString = "";
                foreach (var qrCode in _dividedQRCodeList)
                {
                    connectedString += qrCode.QRCodeString;
                }
                byte[] binaryData = Encoding.UTF8.GetBytes(connectedString);
                byte parity = MyQRCodeHelper.CalcParity(binaryData);
    
                var options = new ZXing.Common.EncodingOptions
                {
                    Width = 100,
                    Height = 100,
                    Margin = 4,
                    Hints =
                        {
                            { ZXing.EncodeHintType.ERROR_CORRECTION, ZXing.QrCode.Internal.ErrorCorrectionLevel.H },
                            { ZXing.EncodeHintType.CHARACTER_SET, MyQRCodeHelper.EncodingName_UTF_8 },
                        },
                };
    
                for (byte i = 0; i < count; ++i)
                {
                    string content = _dividedQRCodeList[i].QRCodeString;
                    var matrix = MyQRCodeHelper.EncodeToDvidedQRCodeBitMatrix(i, count, parity, content, options);
                    var writer = new ZXing.Presentation.BarcodeWriterGeometry
                    {
                        Format = ZXing.BarcodeFormat.QR_CODE,
                    };
                    _dividedQRCodeList[i].QRCodeGeometry = writer.Write(matrix);
                }
            }
    
            private void buttonEncode_Click(object sender, RoutedEventArgs e)
            {
                try
                {
                    bool dividedQR = this.checkDividedQR.IsChecked ?? false;
                    if (dividedQR)
                    {
                        this.BuildDividedQRCodes();
                    }
                    else
                    {
                        this.BuildStandaloneQRCodes();
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.ToString());
                    MessageBox.Show(ex.Message, this.Title, MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }
    
        public static class MyQRCodeHelper
        {
            // Old information: the following example code is based on ZXing v2.0.
            // https://programresource.net/2013/05/04/2193.html
    
            // ZXing.Common.StringUtils.UTF8 and ISO88591 are private constants as of ZXing.NET v0.16.6.
            // https://github.com/micjahn/ZXing.Net/blob/v0.16.6.0/Source/lib/common/StringUtils.cs
    
            public const string EncodingName_UTF_8 = "UTF-8";
            public const string EncodingName_ISO_8859_1 = "ISO-8859-1"; // Latin-1
    
            public const int MaxNumberOfDivisions = 16;
    
            public static byte CalcParity(byte[] data)
            {
                if (data == null)
                {
                    throw new ArgumentNullException($"{nameof(data)} must not be null.");
                }
                if (data.Length <= 0)
                {
                    throw new ArgumentException($"{nameof(data)} must not be empty.");
                }
                byte parity = data[0];
                for (int i = 1; i < data.Length; ++i)
                {
                    parity = (byte)(parity ^ data[i]); // XOR
                }
                return parity;
            }
    
            // Use reflection API in order to make divided QR code without modifying ZXing.NET.
    
            private static MethodInfo GetNonPublicStaticMethodInfo(Type type, string name, Type[] argTypes)
            {
                return type.GetMethod(name, BindingFlags.NonPublic | BindingFlags.Static, Type.DefaultBinder, argTypes, null);
            }
    
            private static ZXing.QrCode.Internal.QRCode EncodeToDividedQRCode(byte pageIndex, byte totalPages, byte parity, string content, ZXing.Common.EncodingOptions options)
            {
                // https://github.com/micjahn/ZXing.Net/blob/v0.16.6.0/Source/lib/qrcode/encoder/Encoder.cs
                Type typeOfEncoder = typeof(ZXing.QrCode.Internal.Encoder);
    
                var ecLevel = ZXing.QrCode.Internal.ErrorCorrectionLevel.L;
                {
                    object temp;
                    if (options.Hints.TryGetValue(ZXing.EncodeHintType.ERROR_CORRECTION, out temp))
                    {
                        ecLevel = (ZXing.QrCode.Internal.ErrorCorrectionLevel)temp;
                    }
                }
                string encoding = EncodingName_ISO_8859_1;
                {
                    object temp;
                    if (options.Hints.TryGetValue(ZXing.EncodeHintType.CHARACTER_SET, out temp))
                    {
                        encoding = (string)temp;
                    }
                }
                int? versionNumber = null;
                {
                    object temp;
                    if (options.Hints.TryGetValue(ZXing.EncodeHintType.QR_VERSION, out temp))
                    {
                        versionNumber = (int)temp;
                    }
                }
                int? maskPattern = null;
                {
                    object temp;
                    if (options.Hints.TryGetValue(ZXing.EncodeHintType.QR_MASK_PATTERN, out temp))
                    {
                        int tempMaskPattern = (int)temp;
                        if (ZXing.QrCode.Internal.QRCode.isValidMaskPattern(tempMaskPattern))
                        {
                            maskPattern = tempMaskPattern;
                        }
                    }
                }
    
                // Step 1: Choose the mode (encoding).
                var chooseMode = GetNonPublicStaticMethodInfo(typeOfEncoder, "chooseMode",
                    new[] { typeof(string), typeof(string) });
                var mode = (ZXing.QrCode.Internal.Mode)chooseMode.Invoke(null, new object[] { content, encoding });
    
                // Step 2: Append bytes into dataBits in appropriate encoding.
                var dataBits = new ZXing.Common.BitArray();
    
                var appendBytes = GetNonPublicStaticMethodInfo(typeOfEncoder, "appendBytes",
                    new[] { typeof(string), typeof(ZXing.QrCode.Internal.Mode), typeof(ZXing.Common.BitArray), typeof(string) });
                appendBytes.Invoke(null, new object[] { content, mode, dataBits, encoding });
    
    #if false
                // Step 3: Initialize QR code that can contain dataBits.
                int numInputBits = dataBits.Size;
                initQRCode(numInputBits, ecLevel, mode, qrCode);
    #endif
    
                // Step 4: Build another bit vector that contains header and data.
                var headerAndDataBits = new ZXing.Common.BitArray();
    
                // Step 4.1: Add code page info.
                const byte tmpByte = 0x3; // Magic number for divided QR code.
                headerAndDataBits.appendBits(tmpByte, 4);
                headerAndDataBits.appendBits(pageIndex, 4);
                headerAndDataBits.appendBits(totalPages, 4);
                headerAndDataBits.appendBits((byte)((parity & 0xf0) >> 4), 4);
                headerAndDataBits.appendBits((byte)(parity & 0x0f), 4);
    
                //var headerBits = new ZXing.Common.BitArray();
                var headerBits = headerAndDataBits;
    
                // Step 4.5: Append ECI message if applicable.
                if (mode == ZXing.QrCode.Internal.Mode.BYTE && encoding != EncodingName_ISO_8859_1)
                {
                    var eci = ZXing.Common.CharacterSetECI.getCharacterSetECIByName(encoding);
                    if (eci != null)
                    {
                        var appendECI = GetNonPublicStaticMethodInfo(typeOfEncoder, "appendECI",
                            new[] { typeof(ZXing.Common.CharacterSetECI), typeof(ZXing.Common.BitArray) });
                        appendECI.Invoke(null, new object[] { eci, headerBits });
                    }
                }
    
                var appendModeInfo = GetNonPublicStaticMethodInfo(typeOfEncoder, "appendModeInfo",
                    new[] { typeof(ZXing.QrCode.Internal.Mode), typeof(ZXing.Common.BitArray) });
                appendModeInfo.Invoke(null, new object[] { mode, headerBits });
    
                ZXing.QrCode.Internal.Version version;
                if (versionNumber != null)
                {
                    version = ZXing.QrCode.Internal.Version.getVersionForNumber(versionNumber.Value);
                    var calculateBitsNeeded = GetNonPublicStaticMethodInfo(typeOfEncoder, "calculateBitsNeeded",
                        new[] { typeof(ZXing.QrCode.Internal.Mode), typeof(ZXing.Common.BitArray), typeof(ZXing.Common.BitArray), typeof(ZXing.QrCode.Internal.Version) });
                    int bitsNeeded = (int)calculateBitsNeeded.Invoke(null, new object[] { mode, headerBits, dataBits, version });
                    var willFit = GetNonPublicStaticMethodInfo(typeOfEncoder, "willFit",
                        new[] { typeof(int), typeof(ZXing.QrCode.Internal.Version), typeof(ZXing.QrCode.Internal.ErrorCorrectionLevel) });
                    if (!(bool)willFit.Invoke(null, new object[] { bitsNeeded, version, ecLevel }))
                    {
                        throw new ZXing.WriterException("Data too big for requested version.");
                    }
                }
                else
                {
                    var recommendVersion = GetNonPublicStaticMethodInfo(typeOfEncoder, "recommendVersion",
                        new[] { typeof(ZXing.QrCode.Internal.ErrorCorrectionLevel), typeof(ZXing.QrCode.Internal.Mode), typeof(ZXing.Common.BitArray), typeof(ZXing.Common.BitArray) });
                    version = (ZXing.QrCode.Internal.Version)recommendVersion.Invoke(null, new object[] { ecLevel, mode, headerBits, dataBits });
                }
                var ecBlocks = version.getECBlocksForLevel(ecLevel);
                int numDataBytes = version.TotalCodewords - ecBlocks.TotalECCodewords;
    
                var qrCode = new ZXing.QrCode.Internal.QRCode()
                {
                    ECLevel = ecLevel,
                    Mode = mode,
                    Version = version,
                    Matrix = new ZXing.QrCode.Internal.ByteMatrix(version.DimensionForVersion, version.DimensionForVersion),
                    //Matrix = new ZXing.QrCode.Internal.ByteMatrix(options.Width, options.Height),
                };
    
                //headerAndDataBits.appendBitArray(headerBits);
    
                int numLetters = (mode == ZXing.QrCode.Internal.Mode.BYTE) ? dataBits.SizeInBytes : content.Length;
                var appendLengthInfo = GetNonPublicStaticMethodInfo(typeOfEncoder, "appendLengthInfo",
                    new[] { typeof(int), typeof(ZXing.QrCode.Internal.Version), typeof(ZXing.QrCode.Internal.Mode), typeof(ZXing.Common.BitArray) });
                appendLengthInfo.Invoke(null, new object[] { numLetters, version, mode, headerAndDataBits });
    
                headerAndDataBits.appendBitArray(dataBits);
    
                // Step 5: Terminate the bits properly.
                var terminateBits = GetNonPublicStaticMethodInfo(typeOfEncoder, "terminateBits",
                    new[] { typeof(int), typeof(ZXing.Common.BitArray) });
                terminateBits.Invoke(null, new object[] { numDataBytes, headerAndDataBits });
    
                // Step 6: Interleave data bits with error correction code.
                var interleaveWithECBytes = GetNonPublicStaticMethodInfo(typeOfEncoder, "interleaveWithECBytes",
                    new[] { typeof(ZXing.Common.BitArray), typeof(int), typeof(int), typeof(int) });
                var finalBits = (ZXing.Common.BitArray)interleaveWithECBytes.Invoke(null, new object[] { headerAndDataBits, version.TotalCodewords, numDataBytes, ecBlocks.NumBlocks });
    
                // Step 7: Choose the mask pattern and set to qrCode.
                var matrix = new ZXing.QrCode.Internal.ByteMatrix(qrCode.Matrix.Width, qrCode.Matrix.Height);
                if (maskPattern == null)
                {
                    var chooseMaskPattern = GetNonPublicStaticMethodInfo(typeOfEncoder, "chooseMaskPattern",
                        new[] { typeof(ZXing.Common.BitArray), typeof(ZXing.QrCode.Internal.ErrorCorrectionLevel), typeof(ZXing.QrCode.Internal.Version), typeof(ZXing.QrCode.Internal.ByteMatrix) });
                    maskPattern = (int)chooseMaskPattern.Invoke(null, new object[] { finalBits, ecLevel, version, matrix });
                }
                qrCode.MaskPattern = maskPattern.Value;
    
                // Step 8. Build the matrix and set it to qrCode.
                ZXing.QrCode.Internal.MatrixUtil.buildMatrix(finalBits, ecLevel, version, qrCode.MaskPattern, matrix);
                qrCode.Matrix = matrix;
    #if false
                // Step 9. Make sure we have a valid QR Code.
                if (!qrCode.isValid())
                {
                    throw new ZXing.WriterException("Invalid QR code.");
                }
    #endif
    
                return qrCode;
            }
    
            public static ZXing.Common.BitMatrix EncodeToDvidedQRCodeBitMatrix(byte pageIndex, byte totalPages, byte parity, string content, ZXing.Common.EncodingOptions options)
            {
                if (string.IsNullOrEmpty(content))
                {
                    throw new ArgumentException($"{nameof(content)} must be neither null nor empty.");
                }
                if (options == null)
                {
                    throw new ArgumentNullException($"{nameof(options)} must not be null.");
                }
                if (options.Width <= 0)
                {
                    throw new ArgumentOutOfRangeException($"{nameof(options)}.Width must be positive.");
                }
                if (options.Height <= 0)
                {
                    throw new ArgumentOutOfRangeException($"{nameof(options)}.Height must be positive.");
                }
    
                var code = EncodeToDividedQRCode(pageIndex, totalPages, parity, content, options);
                // Instance of ZXing.QrCode.QRCodeWriter is not necessary.
                // https://github.com/micjahn/ZXing.Net/blob/v0.16.6.0/Source/lib/qrcode/QRCodeWriter.cs
                Type typeOfQRCodeWriter = typeof(ZXing.QrCode.QRCodeWriter);
                var renderResult = GetNonPublicStaticMethodInfo(typeOfQRCodeWriter, "renderResult",
                    new[] { typeof(ZXing.QrCode.Internal.QRCode), typeof(int), typeof(int), typeof(int) });
                return (ZXing.Common.BitMatrix)renderResult.Invoke(null, new object[] { code, options.Width, options.Height, options.Margin });
            }
        }
    
        public class MyQRCodeViewModel : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
            protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
            {
                this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
    
            private string _qrCodeString;
            private Geometry _qrCodeGeometry;
    
            public string QRCodeString
            {
                get { return _qrCodeString; }
                set
                {
                    _qrCodeString = value;
                    this.NotifyPropertyChanged();
                }
            }
    
            public Geometry QRCodeGeometry
            {
                get { return _qrCodeGeometry; }
                set
                {
                    _qrCodeGeometry = value;
                    this.NotifyPropertyChanged();
                }
            }
        }
    }
    
[/code]

ZXing.NETのGitHubリポジトリでは、WPF向けのデモクライアントアプリのソースコードが併せて公開されています。

  * <https://github.com/micjahn/ZXing.Net/tree/v0.16.6.0/Clients/WPFDemo/WPFDemo>

`ZXing.Presentation.BarcodeWriterGeometry`を使うことで、`ZXing.Common.BitMatrix`から[`Geometry`](https://docs.microsoft.com/en-
us/dotnet/api/system.windows.media.geometry)を生成できます。  
`ZXing.Presentation.BarcodeWriter`を使うことで、`ZXing.Common.BitMatrix`から[`WriteableBitmap`](https://docs.microsoft.com/en-
us/dotnet/api/system.windows.media.imaging.writeablebitmap)を生成できます。

ZXing.NET本体のコードを修正してビルドする作業が面倒だったので、上記サンプルではリフレクションを使って`ZXing.QrCode.Internal.Encoder`や`ZXing.QrCode.QRCodeWriter`の非公開メソッドを呼び出すようにしてみました。  
これにより、ZXing.NET本体はNuGet Package Manager経由でプロジェクトに追加するだけで済みます。  
リフレクションで内部APIにアクセスする方法は、ZXing.NETの内部仕様が変わるたびに修正が必要になりますが、分割QRコード対応のためのパッチを毎回当ててビルドするのと大差はありません。

**ZXing.NET本体のコードを修正してビルドする方法、リフレクションを使用する方法、いずれを利用するにしても、実際にコードをアプリケーションに組み込む際は、派生元であるZXing.NETのライセンス
(Apache 2.0 License) に従ってください。**

  * <https://github.com/micjahn/ZXing.Net/blob/v0.16.6.0/COPYING>

余談ですが、.NETの`IDictionary<TKey,
TValue>.Item[TKey]`プロパティ（インデクサー）は、該当するキーが含まれていない場合は`KeyNotFoundException`例外をスローします。

  * [IDictionary<TKey,TValue>.Item[TKey] Property (System.Collections.Generic) | Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.idictionary-2.item)

キーが存在するかどうか分からない状況で値を取得する場合、`TryGetValue()`メソッドを使います。

  * [IDictionary<TKey,TValue>.TryGetValue(TKey, TValue) Method (System.Collections.Generic) | Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.idictionary-2.trygetvalue)


CC BY-SA 4.0
2021-07-19T15:12:42.613
78301
2021-07-19T15:54:51.473
2021-07-19T15:54:51.473
15413
15413
78217
answer