表題の通り
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
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;
}
}
例えば、
以下のようなコードで、ファイルを選択して
変換することが出来ます。
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が生成されてあると思います。