2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Visual Studio 2019のC#にて、中のxmlを直接叩いてODSを出力する。コードのサンプルです。

Posted at

表題の通り
Visual Studio 2019のC#にて、中のxmlを直接叩いてODSを出力する。
コードのサンプルでございます。

odsは、LibreOfficeや、OpenOfficeなどで使用できるファイル形式で
それぞれが、インストールされていない環境下でも、作成できます。
また、odsは、Microsoft Excelでも扱えますが、ずれが生じたりします。

尚、Microsoft Excelで扱うxlsxは、
内部は、似たようなxmlをzipでアーカイブしたものなので
[参考サイト]
java - How to create and write to Excel file (.xlsx)? - Stack Overflow
上記のサイト様を参考にして、
xmlの内容になるよう書き換えれば、xlsxファイルを、
Excelが存在しない環境下でも、作成できるようになります。

特段、NuGetなど他のプラグインや
別途外部dllを必要としないようにしています。

要は、バイナリを以下のように、直接叩いています。
1.ods内部のxmlのファイルの記述
2.xmlファイルを無圧縮でzipにアーカイブ。
3.画像は、メモリにJpeg形式で保存した上でアーカイブ。

※アーカイブするファイルの順番には、決まった順番があります。
※私は素人ですし
今回のコードは、あくまで最低限の動作する部分にとどめていますので、汚いです。
例外処理や、解放など至らぬ点が、多々ございます。ご了承ください。
もし実際に参考にされる際は、必要個所を必ず訂正してからにしてください。

下の画像は、今回コードでの作成例です。
参考画像

[コード作成参考サイト]
[VBA]CRC-32の計算 言語: VB.NET
C#实现crc32函数 - 时间是把杀刀猪 - CSDN博客
A CRC32 implementation in C# - Sanity Free Coding - C#, .NET, PHP

CreateODS.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;

class CreateODS
{   //LibreOfficeや、OpenOfficeなどで使用できるODS形式を作成する。
    //Excelでも、扱えるが、ズレたりします。。。

    //グローバル変数用

    //■Local file header関係+++++++++++++++++++++++++++++++++++++++++++++++
    //Local file headerの識別子 4bytes(0x04034b50)
    readonly byte[] local_file_header_signature = BitConverter.GetBytes(Convert.ToInt32(0x4034B50));

    //解凍に最低必要なバージョン 2bytes 今回は、ver.1.0で、無圧縮を指定(Default value)→0x000A 
    readonly byte[] version_needed_to_extract = BitConverter.GetBytes(Convert.ToInt16(0xA));

    //オプションフラグと、圧縮方法 2+2bytes{今回はオプションもなく、圧縮もしないので、0を指定共に0x0000}
    readonly byte[] general_purpose_and_compression_method = BitConverter.GetBytes(Convert.ToInt32(0x0));

    //ファイル更新時間 2bytes (秒数は偶数のみ指定可能)
    readonly byte[] last_mod_file_time = new byte[2] { 0, 0 }; //00:00:00固定とする。

    //ファイル更新年月日 2bytes
    readonly byte[] last_mod_file_date = new byte[2] { 147, 76 }; //2018/4/19固定とする。

    //CRC-32 4bytes{アーカイブ前の生ファイルから生成するCRC32}
    byte[] crc_32;
    List<Int32> ary_crc_32s = null; //zip内の各ファイルの、CRC-32を格納。

    //圧縮後のファイルサイズ 4bytes{今回は無圧縮なので圧縮前の値と同値}
    //圧縮前のファイルサイズ 4bytes
    byte[] uncompressed_size;
    List<Int32> ary_uncompressed_sizes = null; //zip内の各ファイルの、圧縮前のファイルサイズを格納。

    //アーカイブ前のファイル名のサイズを格納する 2bytes
    string file_name; //ファイル名
    byte[] file_name_ary; // ファイル名のバイナリ
    List<string> ary_file_name_arys = null; //zip内の各ファイルの、ファイル名のバイナリを格納。
    byte[] file_name_length; //ファイル名のバイナリサイズ

    //拡張フィールドのサイズ 2bytes{今回は、指定しないので0x0000}
    readonly byte[] extra_field_length = BitConverter.GetBytes(Convert.ToInt16(0x0));


    //■Central file header関係+++++++++++++++++++++++++++++++++++++++++++++++
    //Central file headerの識別子 4bytes(0x02014b50)
    readonly byte[] central_file_header_signature = BitConverter.GetBytes(Convert.ToInt32(0x2014B50));

    //作成したバージョン 1+1bytes{今回は、ver.1.0で、無圧縮を指定(Default value)→0x000A }
    readonly byte[] version_made_by = BitConverter.GetBytes(Convert.ToInt16(0xA));

    //ファイルの属性などの情報 2+2+2+4bytes → 今回は無視するのですべて0
    readonly byte[] file_attributes_etc = new byte[10] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    //対応するファイルのlocal file header信号のある位置 4bytes
    //出力zipの各local_file_header signatureの、バイト開始位置を示す4バイトのバイナリ配列を格納。
    List<Int32> relative_offset_of_local_header = null;


    //■End of central directory record関係+++++++++++++++++++++++++++++++++++++++++++++++
    //End of central directory record 4bytes(0x06054b50)
    readonly byte[] end_of_central_dir_signature = BitConverter.GetBytes(Convert.ToInt32(0x6054B50));

    //ディスク分割情報 2+2bytes{ディスク分割はしていないのですべて0}
    readonly byte[] number_of_this_disk = BitConverter.GetBytes(Convert.ToInt32(0x0));

    //アーカイブしたファイルの個数 2+2bytes{ディスク分割はしていないので同じ内容の重複}
    byte[] total_number_of_entries;

    //全Central file headerに要したの合計バイト数 4bytes
    byte[] size_of_the_central_directory;

    //最初のCentral file headerの記述されたバイト位置 4bytes
    long offset_of_start_of_central_directory_int;
    byte[] offset_of_start_of_central_directory;

    //zipファイルへのコメントのサイズ 2bytes{今回は、指定しないので0x0000}
    readonly byte[] ZIP_file_comment_length = BitConverter.GetBytes(Convert.ToInt16(0x0));

    //◆zipファイル入出力など、取扱関係+++++++++++++++++++++++++++++
    //出力zipのバイナリ一時格納用。
    List<byte> ary_zip_file = null;



    //■アーカイブする文字列関係+++++++++++++++++++++++++++++++++++++++++++++++
    //OpenDocument Spreadsheetであることを示す
    readonly string mimetype_xml = "application/vnd.oasis.opendocument.spreadsheet";

    //ドキュメントの印刷情報など
    readonly string styles_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
        "<office:document-styles xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\" xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\" " +
            "xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\" xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\" xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\" " +
            "xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\" xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\" " +
            "xmlns:of=\"urn:oasis:names:tc:opendocument:xmlns:of:1.2\" office:version=\"1.2\">" +
        "<office:font-face-decls><style:font-face style:name=\"MS Pゴシック\" svg:font-family=\"MS Pゴシック\"/></office:font-face-decls>" +
        "<office:styles><number:number-style style:name=\"N0\"><number:number number:min-integer-digits=\"1\"/></number:number-style><style:style style:name=\"Default\" style:family=\"table-cell\" style:data-style-name=\"N0\">" +
        "<style:table-cell-properties style:vertical-align=\"middle\" fo:background-color=\"transparent\"/>" +
        "<style:text-properties fo:color=\"#000000\" style:font-name=\"MS Pゴシック\" style:font-name-asian=\"MS Pゴシック\" style:font-name-complex=\"MS Pゴシック\" fo:font-size=\"11pt\" style:font-size-asian=\"11pt\" style:font-size-complex=\"11pt\"/></style:style>" +
        "<style:style style:family=\"graphic\" style:name=\"Graphics\"/><style:default-style style:family=\"graphic\">" +
        "<style:graphic-properties draw:fill=\"solid\" draw:fill-color=\"#000000\" draw:opacity=\"100%\" draw:stroke=\"solid\" svg:stroke-width=\"0.01389in\" svg:stroke-color=\"#ffffff\" svg:stroke-opacity=\"100%\" draw:stroke-linejoin=\"miter\" svg:stroke-linecap=\"butt\"/>" +
        "</style:default-style></office:styles>" +
        "<office:automatic-styles><style:page-layout style:name=\"pm1\">" +
        "<style:page-layout-properties fo:margin-top=\"0.3cm\" fo:margin-bottom=\"0.3cm\" fo:margin-left=\"0.7cm\" fo:margin-right=\"0.7cm\" style:print-orientation=\"portrait\" style:print-page-order=\"ttb\" style:first-page-number=\"continue\" style:scale-to=\"100%\" style:table-centering=\"both\" style:print=\"objects charts drawings\"/>" +
        "<style:header-style><style:header-footer-properties fo:min-height=\"0.45cm\" fo:margin-left=\"0.7cm\" fo:margin-right=\"0.7cm\" fo:margin-bottom=\"0cm\"/></style:header-style>" +
        "<style:footer-style><style:header-footer-properties fo:min-height=\"0.45cm\" fo:margin-left=\"0.7cm\" fo:margin-right=\"0.7cm\" fo:margin-top=\"0cm\"/></style:footer-style></style:page-layout></office:automatic-styles>" +
        "<office:master-styles><style:master-page style:name=\"mp1\" style:page-layout-name=\"pm1\">" +
        "<style:header><text:p>ヘッダー文字列</text:p></style:header><style:header-left style:display=\"false\"/>" +
        "<style:footer><text:p>フッター文字列</text:p></style:footer><style:footer-left style:display=\"false\"/>" +
        "</style:master-page></office:master-styles></office:document-styles>";

    //このシートで使用するファイル一覧
    readonly string META_INF_manifest_xml_header = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
        "<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\"><manifest:file-entry manifest:full-path=\"/\" manifest:media-type=\"application/vnd.oasis.opendocument.spreadsheet\"/>" +
        "<manifest:file-entry manifest:full-path=\"styles.xml\" manifest:media-type=\"text/xml\"/>" +
        "<manifest:file-entry manifest:full-path=\"content.xml\" manifest:media-type=\"text/xml\"/>";

    //画像指定用
    readonly string META_INF_manifest_xml_image = "<manifest:file-entry manifest:full-path=\"media/image1.jpeg\" manifest:media-type=\"image/jpeg\"/>";
    readonly string META_INF_manifest_xml_footer = "<manifest:file-entry manifest:full-path=\"meta.xml\" manifest:media-type=\"text/xml\"/></manifest:manifest>";


    //シートの内容すべてを格納するxml
    //※列幅、行高、画像のサイズの単位は、cmで統一。
    readonly string content_xml_header = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
        "<office:document-content xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\" xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\" " +
            "xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\" xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\" xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\" " +
            "xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\" xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\" " +
            "xmlns:of=\"urn:oasis:names:tc:opendocument:xmlns:of:1.2\" office:version=\"1.2\">" +
        "<office:font-face-decls><style:font-face style:name=\"MS Pゴシック\" svg:font-family=\"MS Pゴシック\"/></office:font-face-decls><office:automatic-styles>";

    //このシートで使用するセルの書式設定を格納
    readonly string content_xml_cell_style = "<style:style style:name=\"ce1\" style:family=\"table-cell\" style:parent-style-name=\"Default\" style:data-style-name=\"N0\"/>" + //標準スタイル
        "<style:style style:name=\"ce2\" style:family=\"table-cell\" style:parent-style-name=\"Default\" style:data-style-name=\"N0\"><style:table-cell-properties style:vertical-align=\"middle\" style:repeat-content=\"false\"/><style:paragraph-properties fo:text-align=\"center\"/><style:text-properties fo:font-size=\"12pt\" style:font-size-asian=\"12pt\" style:font-size-complex=\"12pt\"/></style:style>" + //サブタイトル書式
        "<style:style style:name=\"ce3\" style:family=\"table-cell\" style:parent-style-name=\"Default\" style:data-style-name=\"N0\"><style:table-cell-properties fo:border=\"thin solid #000000\" style:vertical-align=\"middle\" style:repeat-content=\"false\"/><style:paragraph-properties fo:text-align=\"center\"/></style:style>" + //肩書用
        "<style:style style:name=\"ce4\" style:family=\"table-cell\" style:parent-style-name=\"Default\" style:data-style-name=\"N0\"><style:table-cell-properties style:vertical-align=\"middle\" style:repeat-content=\"false\"/><style:paragraph-properties fo:text-align=\"center\"/><style:text-properties fo:font-size=\"16pt\" style:font-size-asian=\"16pt\" style:font-size-complex=\"16pt\"/></style:style>" + //タイトル用
        "<style:style style:name=\"ce5\" style:family=\"table-cell\" style:parent-style-name=\"Default\" style:data-style-name=\"N0\"><style:table-cell-properties fo:border=\"thin solid #000000\" style:vertical-align=\"middle\" style:repeat-content=\"false\"/><style:paragraph-properties fo:text-align=\"start\" fo:margin-left=\"0.353cm\"/></style:style>"; //本文用

    //列の幅
    readonly string content_xml_column_style = "<style:style style:name=\"co1\" style:family=\"table-column\"><style:table-column-properties fo:break-before=\"auto\" style:column-width=\"11.5cm\"/></style:style>" + //1列目の設定
        "<style:style style:name=\"co2\" style:family=\"table-column\"><style:table-column-properties fo:break-before=\"auto\" style:column-width=\"2.0cm\"/></style:style>"; //2~4列目の設定

    //行の高さ
    readonly string content_xml_row_style = "<style:style style:name=\"ro1\" style:family=\"table-row\"><style:table-row-properties style:row-height=\"0.48cm\" style:use-optimal-row-height=\"false\" fo:break-before=\"avoid\"/></style:style>" + //タイトル・肩書の行高
        "<style:style style:name=\"ro2\" style:family=\"table-row\"><style:table-row-properties style:row-height=\"1.06cm\" style:use-optimal-row-height=\"false\" fo:break-before=\"avoid\"/></style:style>" + //タイトル下部・押印枠上部
        "<style:style style:name=\"ro3\" style:family=\"table-row\"><style:table-row-properties style:row-height=\"0.5cm\" style:use-optimal-row-height=\"false\" fo:break-before=\"avoid\"/></style:style>" + //サブタイトル・押印枠下部
        "<style:style style:name=\"ro4\" style:family=\"table-row\"><style:table-row-properties style:row-height=\"0.48cm\" style:use-optimal-row-height=\"true\" fo:break-before=\"avoid\"/></style:style>" + //タイトルと本文の間隔
        "<style:style style:name=\"ro5\" style:family=\"table-row\"><style:table-row-properties style:row-height=\"0.85cm\" style:use-optimal-row-height=\"false\" fo:break-before=\"avoid\"/></style:style>" + //本文用
        "<style:style style:name=\"ro6\" style:family=\"table-row\"><style:table-row-properties style:row-height=\"0.48cm\" style:use-optimal-row-height=\"false\" fo:break-before=\"page\"/></style:style>"; //タイトル用(改行付き)

    //表全体
    readonly string content_xml_table_style = "<style:style style:name=\"ta1\" style:family=\"table\" style:master-page-name=\"mp1\"><style:table-properties table:display=\"true\" style:writing-mode=\"lr-tb\"/></style:style>";

    //画像
    readonly string content_xml_graphics_style = "<style:style style:family=\"graphic\" style:name=\"a0\" style:parent-style-name=\"Graphics\"><style:graphic-properties draw:fill=\"none\" draw:stroke=\"none\"/></style:style>";

    //スタイルフッター・シート本体開始
    readonly string content_xml_span1 = "</office:automatic-styles>" +
        "<office:body><office:spreadsheet><table:calculation-settings table:case-sensitive=\"false\" table:search-criteria-must-apply-to-whole-cell=\"true\" table:use-wildcards=\"true\" table:use-regular-expressions=\"false\" table:automatic-find-labels=\"false\"/>" +
        "<table:table table:name=\"Sheet1\" table:style-name=\"ta1\">" + //シート名
        "<table:table-column table:style-name=\"co1\" table:default-cell-style-name=\"ce1\"/>" + //1行目の書式設定
        "<table:table-column table:style-name=\"co2\" table:number-columns-repeated=\"3\" table:default-cell-style-name=\"ce1\"/>"; //2→4行目の書式設定

    //フッター。
    readonly string content_xml_footer = "</table:table></office:spreadsheet></office:body></office:document-content>";

    //更新日時など
    readonly string meta_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
        "<office:document-meta xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" office:version=\"1.2\">" +
        "<office:meta><meta:generator>photoreport.attachment.style</meta:generator>" +
        "<meta:initial-creator>TageSP</meta:initial-creator><dc:creator>TageSP</dc:creator>" +
        "<meta:creation-date>" +
            DateTime.UtcNow.ToString("yyyy-MM-dd") + "T" +
            DateTime.UtcNow.ToString("hh:mm:ss") + "Z" +
        "</meta:creation-date><dc:date>" +
            DateTime.UtcNow.ToString("yyyy-MM-dd") + "T" +
            DateTime.UtcNow.ToString("hh:mm:ss") + "Z" +
        "</dc:date></office:meta></office:document-meta>";



    public async System.Threading.Tasks.Task create_ods(string[] imagepath, string filepath)
    {


        if (System.IO.File.Exists(filepath))
        {   //上書きする前に削除する
            try
            {
                System.IO.File.Delete(filepath);
            }
            catch { }
        }

        //◎使用する各配列の初期化++++++++++++++++++++++++++
        ary_Clear(ref ary_zip_file, false);
        ary_crc_32s = new List<Int32> { };
        ary_uncompressed_sizes = new List<Int32> { };
        ary_file_name_arys = new List<string> { };
        relative_offset_of_local_header = new List<Int32> { };

        //zipは、無圧縮で自分でバイナリを打っていく。
        using (System.IO.FileStream fs = new System.IO.FileStream(filepath, System.IO.FileMode.Create, System.IO.FileAccess.Write))
        {
            StringBuilder sheetdata = new StringBuilder();
            sheetdata.Clear();

            for (int i = 0; i <= 5; i++)
            {
                //実際に格納するバイナリを格納する
                byte[] moto_bs = null;

                //順番に従った格納をする。
                if (i == 0)
                {   //アーカイブの先頭はmimetype固定。
                    moto_bs = System.Text.Encoding.UTF8.GetBytes(mimetype_xml);
                    file_name = "mimetype";
                }
                else if (i == 1)
                {   //印刷設定
                    moto_bs = System.Text.Encoding.UTF8.GetBytes(styles_xml);
                    file_name = "styles.xml";
                }
                else if (i == 2)
                {   //使用するファイルのリスト
                    sheetdata.Append(META_INF_manifest_xml_header);
                    sheetdata.Append(META_INF_manifest_xml_image);
                    sheetdata.Append(META_INF_manifest_xml_footer);
                    moto_bs = System.Text.Encoding.UTF8.GetBytes(sheetdata.ToString());
                    sheetdata.Clear();
                    file_name = "META-INF/manifest.xml";

                }
                else if (i == 3)
                {   //シート本文
                    sheetdata.Append(content_xml_header);
                    sheetdata.Append(content_xml_cell_style);
                    sheetdata.Append(content_xml_column_style);
                    sheetdata.Append(content_xml_row_style);
                    sheetdata.Append(content_xml_table_style);
                    sheetdata.Append(content_xml_graphics_style);
                    sheetdata.Append(content_xml_span1);

                    //本文は別処理
                    sheetdata.Append(content_xml_make_main());

                    //フッターで閉じる
                    sheetdata.Append(content_xml_footer);

                    moto_bs = System.Text.Encoding.UTF8.GetBytes(sheetdata.ToString());
                    file_name = "content.xml";
                }
                else if (i == 4)
                {   //更新日時など
                    moto_bs = System.Text.Encoding.UTF8.GetBytes(meta_xml);
                    file_name = "meta.xml";
                }
                else if (i == 5)
                {   //挿入画像
                    using (Bitmap bitmap = new Bitmap(imagepath[0]))
                    {
                        using (var baos = new MemoryStream())
                        {   //今回は、とりあえず1つだけ画像を適用する。  
                            bitmap.Save(baos, System.Drawing.Imaging.ImageFormat.Jpeg);
                            moto_bs = ((MemoryStream)baos).ToArray();
                        }
                    }
                    file_name = "media/image1.jpeg";
                }




                //■Local file headerの作成------------ここから-----------------
                //●予めlocal_file_header_signatureの記述位置を格納しておく(4バイト)
                relative_offset_of_local_header.Add(Convert.ToInt32(fs.Length)); //つまり現在のファイルサイズ

                //○local_file_header_signatureの記述(4バイト)
                ary_zip_file.AddRange(local_file_header_signature);

                //○解凍に必要なバージョンの記述(2バイト)
                ary_zip_file.AddRange(version_needed_to_extract);

                //○オプションフラグと、圧縮方法の記述(2+2バイト)
                ary_zip_file.AddRange(general_purpose_and_compression_method);

                //ファイルの更新時刻を格納(2バイト)//////////////////////////////////
                ary_zip_file.AddRange(last_mod_file_time);

                //○ファイルの更新年月日を格納(2バイト)//////////////////////////////////
                ary_zip_file.AddRange(last_mod_file_date);


                //○CRC32(4バイト)
                //元ファイルのバイナリからCRC-32を取得する。
                crc_32 = await CRC_32_Checksum(moto_bs); //計算に時間がかかるので、待機させる
                ary_zip_file.AddRange(crc_32);
                //再計算が面倒なので、central file headerに再利用するために記憶しておく。
                ary_crc_32s.Add(BitConverter.ToInt32(crc_32, 0));


                //○圧縮後のファイルサイズの取得(4バイト){無圧縮なので圧縮後のサイズと同値}
                uncompressed_size = BitConverter.GetBytes(Convert.ToInt32(moto_bs.Length));
                ary_zip_file.AddRange(uncompressed_size);

                //○圧縮前のファイルサイズの取得(4バイト)
                ary_zip_file.AddRange(uncompressed_size);
                //再計算が面倒なので、central file headerに再利用するために記憶しておく。
                ary_uncompressed_sizes.Add(Convert.ToInt32(moto_bs.Length));

                //○圧縮前のファイル名のサイズの取得(2バイト)
                //文字列をUTF-8コードとしてバイナリ化する
                file_name_ary = System.Text.Encoding.UTF8.GetBytes(file_name);

                //文字列長を取得する。
                file_name_length = BitConverter.GetBytes(Convert.ToInt16(file_name_ary.Length));
                ary_zip_file.AddRange(file_name_length); //格納

                //○拡張フィールドのサイズ指定(2バイト){今回は指定なし}
                ary_zip_file.AddRange(extra_field_length); //格納


                //○アーカイブ前のファイル名の格納
                ary_zip_file.AddRange(file_name_ary);
                //file_name_aryに再利用するために記憶しておく。
                ary_file_name_arys.Add(file_name);

                try
                { //一旦、書き貯めたバイト型配列の内容をファイルに出力する
                    fs.Write((byte[])ary_zip_file.ToArray(), 0, ary_zip_file.Count);
                    fs.Flush();
                }
                catch { }
                ary_zip_file.Clear(); //初期化
                //■Local file headerの作成------------ここまで-----------------
                await System.Threading.Tasks.Task.Delay(10);


                //■ファイル本体のバイナリを書き込む
                try
                { //ファイルに出力する
                    fs.Write(moto_bs, 0, moto_bs.Length);
                    fs.Flush();
                }
                catch { }
                await System.Threading.Tasks.Task.Delay(10);//これがないと、書き込み途中で放棄される??

                //初期化
                Array.Clear(moto_bs, 0, moto_bs.Length);
                Array.Clear(crc_32, 0, crc_32.Length);
                Array.Clear(file_name_length, 0, file_name_length.Length);
                Array.Clear(file_name_ary, 0, file_name_ary.Length);
            }


            //★中盤戦。central file headerの記述********************************************
            //予め最初のCentral file headerの記述開始バイト位置を記憶しておく
            offset_of_start_of_central_directory_int = fs.Length;
            offset_of_start_of_central_directory = BitConverter.GetBytes(Convert.ToInt32(offset_of_start_of_central_directory_int));


            //各ファイル分ループ
            for (int i = 0; i < ary_file_name_arys.Count; i++)
            {
                byte[] bs;
                //■central file headerの作成------------ここから-----------------
                //○central_file_header_signatureの記述(4バイト)
                ary_zip_file.AddRange(central_file_header_signature);

                //○zipを作った時のバージョンとOSの記述(1+1バイト)
                ary_zip_file.AddRange(version_made_by);

                //○解凍に必要なバージョンの記述(2バイト)
                ary_zip_file.AddRange(version_needed_to_extract);

                //○オプションフラグと圧縮方法の記述(2+2バイト)
                ary_zip_file.AddRange(general_purpose_and_compression_method);

                //○ファイルの更新時刻(2バイト)[固定値]
                ary_zip_file.AddRange(last_mod_file_time);

                //○ファイルの更新年月日(2バイト)[固定値]
                ary_zip_file.AddRange(last_mod_file_date);

                //○CRC-32(4バイト)
                //https://www.morgantechspace.com/2013/08/convert-object-to-byte-array-and-vice.html
                ary_zip_file.AddRange(BitConverter.GetBytes(ary_crc_32s[i]));


                //○圧縮後のファイルサイズ(4バイト) {今回は、無圧縮なので、圧縮前のファイルサイズを流用}
                bs = BitConverter.GetBytes(ary_uncompressed_sizes[i]);
                ary_zip_file.AddRange(bs);

                //○圧縮前のファイルサイズ(4バイト)
                ary_zip_file.AddRange(bs);
                Array.Clear(bs, 0, bs.Length);//初期化

                //○ファイル名のサイズ(2バイト)
                byte[] name_bs = System.Text.Encoding.UTF8.GetBytes(ary_file_name_arys[i]);
                ary_zip_file.AddRange(BitConverter.GetBytes(Convert.ToInt16(name_bs.Length)));


                //○拡張フィールドのサイズ(2バイト)
                ary_zip_file.AddRange(extra_field_length);

                //○ファイルの属性などの情報(2+2+2+4バイト)
                ary_zip_file.AddRange(file_attributes_etc);

                //○対応するファイルのlocal File header信号のある位置(4バイト)
                bs = BitConverter.GetBytes(relative_offset_of_local_header[i]);
                ary_zip_file.AddRange(bs);
                Array.Clear(bs, 0, bs.Length);//初期化


                //○アーカイブ前のファイル名の格納
                ary_zip_file.AddRange(name_bs);
                Array.Clear(name_bs, 0, name_bs.Length);//初期化

                try
                { //一旦、書き貯めたバイト型配列の内容をファイルに出力する
                    fs.Write((byte[])ary_zip_file.ToArray(), 0, ary_zip_file.Count);
                    fs.Flush();
                }
                catch { }
                ary_zip_file.Clear(); //初期化
                //■central file headerの作成------------ここまで-----------------
                await System.Threading.Tasks.Task.Delay(10);
            }

            //◎使用する各配列の一旦、クリア++++++++++++++++++++++++++
            ary_crc_32s.Clear();
            ary_uncompressed_sizes.Clear();
            relative_offset_of_local_header.Clear();


            //★終盤戦End of central directory recordの記述********************************************
            //全Central file headerに要したの合計バイト数を予め算出しておく
            size_of_the_central_directory = BitConverter.GetBytes(
                Convert.ToInt32(fs.Length - offset_of_start_of_central_directory_int));

            //■End of central directory recordの作成------------ここから-----------------
            //○end_of_central_dir_signatureの記述(4バイト)
            ary_zip_file.AddRange(end_of_central_dir_signature);

            //○ディスク分割情報(2+2バイト)
            ary_zip_file.AddRange(number_of_this_disk);

            //○アーカイブしたファイルの個数(2+2バイト){同じ内容を重複して格納}
            total_number_of_entries = BitConverter.GetBytes(Convert.ToInt16(ary_file_name_arys.Count));
            ary_zip_file.AddRange(total_number_of_entries);
            ary_zip_file.AddRange(total_number_of_entries); //※重複分

            //○全Central file headerに要したの合計バイト数を格納(4バイト)
            ary_zip_file.AddRange(size_of_the_central_directory);

            //○最初のCentral file headerの記述されたバイト位置(4バイト)
            ary_zip_file.AddRange(offset_of_start_of_central_directory);

            //○ファイルへのコメントのサイズ(2バイト){今回はなし}
            ary_zip_file.AddRange(ZIP_file_comment_length);

            try
            { //一旦、書き貯めたバイト型配列の内容をファイルに出力する
                fs.Write((byte[])ary_zip_file.ToArray(), 0, ary_zip_file.Count);
                fs.Flush();
                await System.Threading.Tasks.Task.Delay(10);//これがないと、書き込み途中で放棄される??
            }
            catch { }
            ary_zip_file.Clear(); //初期化
            //■End of central directory recordの作成------------ここまで-----------------


            //初期化
            Array.Clear(total_number_of_entries, 0, total_number_of_entries.Length);
            Array.Clear(size_of_the_central_directory, 0, size_of_the_central_directory.Length);//初期化
            Array.Clear(offset_of_start_of_central_directory, 0, offset_of_start_of_central_directory.Length);//初期化


            //◎使用する各配列の念のためクリア++++++++++++++++++++++++++
            ary_crc_32s.Clear();
            ary_uncompressed_sizes.Clear();
            relative_offset_of_local_header.Clear();

            //◎配列の初期化2++++++++++++++++++++++++++
            ary_Clear(ref ary_zip_file, true);
            ary_file_name_arys.Clear();

            //■後処理
            fs.Close(); //ファイルを閉じる 
        }


    }


    public string title_string = "タイトル";
    public string subtitle_string = "サブタイトル";
    public string[] seal_string;
    public string[] main_string;

    public StringBuilder content_xml_make_main()
    {   //content.xmlの本文を作成する場所
        StringBuilder xmldata = new StringBuilder();

        //◆1行目(タイトル・肩書)
        xmldata.Append("<table:table-row table:style-name=\"ro1\">");
        //1列目{2行目と結合}
        xmldata.Append("<table:table-cell office:value-type=\"string\" table:number-columns-spanned=\"1\" table:number-rows-spanned=\"2\" table:style-name=\"ce4\"><text:p>" +
            title_string +
            "</text:p></table:table-cell>");

        //2列目(空白)
        xmldata.Append("<table:table-cell table:style-name=\"ce1\"/>");

        //3列目(肩書1)
        xmldata.Append("<table:table-cell office:value-type=\"string\" table:style-name=\"ce3\"><text:p>" +
            seal_string[0] +
            "</text:p></table:table-cell>");
        //4列目(肩書2)
        xmldata.Append("<table:table-cell office:value-type=\"string\" table:style-name=\"ce3\"><text:p>" +
            seal_string[1] +
            "</text:p></table:table-cell>");

        //1行目おわり
        xmldata.Append("</table:table-row>");


        //◆2行目(押印枠)
        xmldata.Append("<table:table-row table:style-name=\"ro2\">");
        //1列目は、1行目との結合で遮蔽されている
        xmldata.Append("<table:covered-table-cell/>");
        //2列目(空白)
        xmldata.Append("<table:table-cell table:style-name=\"ce1\"/>");
        //3列目(押印枠1){3行目と結合}
        xmldata.Append("<table:table-cell office:value-type=\"string\" table:number-columns-spanned=\"1\" table:number-rows-spanned=\"2\" table:style-name=\"ce3\"/>");
        //4列目(押印枠1){3行目と結合}
        xmldata.Append("<table:table-cell office:value-type=\"string\" table:number-columns-spanned=\"1\" table:number-rows-spanned=\"2\" table:style-name=\"ce3\"/>");
        xmldata.Append("</table:table-row>");

        //◆3行目(サブタイトル)
        xmldata.Append("<table:table-row table:style-name=\"ro3\">");
        //1列目(サブタイトル)
        xmldata.Append("<table:table-cell office:value-type=\"string\" table:style-name=\"ce2\"><text:p>" +
            subtitle_string +
            "</text:p></table:table-cell>");
        //2列目(空白)
        xmldata.Append("<table:table-cell table:style-name=\"ce1\"/>");
        //3、4列目は、2行目との結合で遮蔽されている
        xmldata.Append("<table:covered-table-cell/>");
        xmldata.Append("<table:covered-table-cell/>");
        xmldata.Append("</table:table-row>");

        //◆4行目(全て空白)
        xmldata.Append("<table:table-row table:style-name=\"ro4\"/>");

        //◆5→14行目(本文){10行分}
        for (int i = 0; i < 10; i++)
        {
            xmldata.Append("<table:table-row table:style-name=\"ro5\">");
            if (i == 0)
            {
                //1行目は写真の挿入
                xmldata.Append("<table:table-cell office:value-type=\"string\" table:number-columns-spanned=\"1\" table:number-rows-spanned=\"10\" table:style-name=\"ce3\">" +
                    "<text:p/>" + //文字はなし
                    "<draw:frame draw:z-index=\"1\" draw:id=\"id0\" draw:style-name=\"a0\" draw:name=\"pic_image1\" svg:x=\"0.5cm\" svg:y=\"0.1cm\" svg:width=\"10.5cm\" svg:height=\"8.3cm\" style:rel-width=\"scale\" style:rel-height=\"scale\">" +
                    "<draw:image xlink:href=\"media/image1.jpeg\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"/>" +
                    "<svg:title/><svg:desc/></draw:frame></table:table-cell>");
            }
            else
            {
                //上のセルと結合
                xmldata.Append("<table:covered-table-cell/>");
            }

            //コメント本文の記述
            xmldata.Append("<table:table-cell office:value-type=\"string\" table:number-columns-spanned=\"3\" table:number-rows-spanned=\"1\" table:style-name=\"ce3\"><text:p>" +
                main_string[i] +
                "</text:p></table:table-cell>");

            //右2行は結合により遮蔽されている。
            xmldata.Append("<table:covered-table-cell table:number-columns-repeated=\"2\"/>");
            xmldata.Append("</table:table-row>");

        }

        return xmldata;
    }

    public void ary_Clear(ref List<byte> ary, bool Clear_only_flg)
    {
        //指定した配列の初期化をする
        if (ary != null)
        {
            ary.Clear();
            ary = null;
        }
        //初期化のみの場合→出る
        if (Clear_only_flg) { return; }

        //新たに配列をセットしなおす
        ary = new List<byte>();
    }

    private async System.Threading.Tasks.Task<byte[]> CRC_32_Checksum(byte[] bytes)
    {
        //★CRC32の計算をするところ  
        //UIntegerの方法では、上手くできないのですorz  
        //また、返数をByte()でなく、Long型にすると、値が狂います。  

        //https://code.msdn.microsoft.com/office/VBACRC-32-dad7d087  
        //https://blog.csdn.net/ciaos/article/details/12490911
        //http://sanity-free.org/12/crc32_implementation_in_csharp.html

        //計算に時間がかかるので、待機させる
        System.Threading.Tasks.Task<byte[]> uploadTask = System.Threading.Tasks.Task.Factory.StartNew<byte[]>(
        () => {
            //■CRC32のテーブル情報の事前生成  
            uint[] crc32Table = new UInt32[256];
            //CRC-32 の除数0x104c11db7を、予めビットの並び順を左右逆転させた値(0xEDB88320) を使用する。
            uint poly = 0xedb88320;

            uint temp = 0;
            for (uint i = 0; i < crc32Table.Length; ++i)
            {
                temp = i;
                for (int j = 8; j > 0; --j)
                {
                    if ((temp & 1) == 1) //&で、andのビット演算
                    {
                        temp = (uint)((temp >> 1) ^ poly); //^で、xorのビット演算
                    }
                    else
                    {
                        temp >>= 1; //÷(2の1乗)と同じ効果  
                    }
                }
                crc32Table[i] = temp;
            }


            //初期化  
            uint crc = 0xffffffff;

            //■渡されたファイルのバイナリから、CRC-32を算出して返す 
            for (int i = 0; i < bytes.Length; ++i)
            {   //■計算の引き合いに出すテーブルのインデックス番号の算出  
                byte index = (byte)(((crc) & 0xff) ^ bytes[i]);

                //■次のビットにスライドする。  
                //→(crc >> 8)は、crcを(2の8乗)で割っていることと同義  
                crc = (uint)((crc >> 8) ^ crc32Table[index]);
            }

            //ビット反転後バイト配列に変換して返す
            return BitConverter.GetBytes(~crc); //^で、notのビット演算
        });

        return await uploadTask;
    }
}

例えば、
以下のようなコードで、ファイルを選択して
変換することが出来ます。

Form1.cs
        private async void Form1_Load(object sender, EventArgs e)
        {   //ここは適当に記述しています。
            using (OpenFileDialog fileDialog = new OpenFileDialog())
            {   //PDFに直接変換したい画像ファイルを複数選択できるようにしている。
                fileDialog.Filter = "画像データ (*.bmp;*jpeg;*.jpg;*.png)|*.bmp;*jpeg;*.jpg;*.png";
                fileDialog.Multiselect = true;

                //「開く」ボタンが選択された時の処理
                if (fileDialog.ShowDialog() == DialogResult.OK)
                {   
                    //変換したい画像のリストを取得
                    string[] fileName = fileDialog.FileNames;  //こんな感じで選択されたファイルのパスが取得できる

                    //PDFは、今回は仮に、最初のファイルと同じフォルダに「test.pdf」として生成される。
                    CreateODS cls = new CreateODS();

                    //入力する文字を横着して、ここにて指定。
                    cls.title_string = "ここにタイトル";
                    cls.subtitle_string = "ここにサブタイトル";
                    cls.seal_string = new string[] { "肩書1", "肩書2" };
                    //本文のところ。
                    cls.main_string = new string[] { "以下、本文です", "今日は晴れ",
                        "湿度は高め", "温度も高め", "いわゆる猛暑日", "とても暑い",
                        "困ったものです", "しかし、", "これも夏", "楽しまねば"};

                    await cls.create_ods(fileName,
                        Path.Combine(Path.GetDirectoryName(fileName[0]), "test.ods"));

                    //おしまいのメッセージ
                    MessageBox.Show("END");
                }
            }

            //さようなら
            Application.Exit();
        }

以上のコードを実行すると
指定した画像のフォルダに、odsが生成されてあると思います。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?