ブラウザだけでJavaScriptで「小説家になろう」と「カクヨム」の大体のルビを置換して、1つ以上の.txtファイルを.odtファイルに変換しよう!
Githubでソース コードをパブリック ドメインで公開しております。
マイクロソフトのBing検索エンジンで「github eliphas1810-tools」などで検索してみてください。
残念ながらグーグル検索エンジンでは検索できません。
.txtファイル内の「小説家になろう」と「カクヨム」の大体のルビをLibreOfficeのWriterなどの.odtファイル内のcontent.xmlのルビのXMLに置換して、1つ以上の.txtファイルを.odtファイルに変換します。
「小説家になろう」と「カクヨム」の半角縦線(|)のルビには対応していません。
ルビが大量に有る文書から、ルビ有りの.odtファイルを作成したい場合に役立つと思います。
また、.txtファイル名の先頭から半角数字の連続と半角空白を除去した名前をLibreOfficeのWriterなどの「見出し1」にします。
例えば、「0001 第一話.txt」ファイルが存在する場合は「第一話」を「見出し1」にします。
LibreOfficeのWriterなどの「見出し1」は、LibreOfficeのWriterなどでPDFファイルを作成した時に、PDFファイルの目次に成ります。
ちなみに、LibreOfficeのWriterでルビの背景が灰色に成ってしまう場合は、ルビの強調表示が原因なので、Ctrl + F8で強調表示をオンからオフへ切り替えて解消します。
また、LibreOfficeのWriterでルビの均等割付は分かり難い「121」と「010」という名前に成っています。
LibreOfficeのWriterのルビの均等割付「121」は、テキストとルビの横幅を大きいほうに統一してから、テキストかルビのうち文字数の少ないほうを横幅の端に隣接しないように配置します。
当プログラムはLibreOfficeのWriterのルビの均等割付「121」のXMLへ置換します。
LibreOfficeのWriterのルビの均等割付「121」の例
――――――――――――――――――――
宇 宙
ユニバース
――――――――――――――――――――
LibreOfficeのWriterのルビの均等割付「010」は、テキストとルビの横幅を大きいほうに統一してから、テキストかルビのうち文字数の少ないほうを横幅の端に隣接するように配置します。
LibreOfficeのWriterのルビの均等割付「010」は、少し見栄えが悪いです。
LibreOfficeのWriterのルビの均等割付「010」の例
――――――――――――――――――――
宇 宙
ユニバース
――――――――――――――――――――
LibreOfficeのWriterでは、デフォルトでは、ルビが有る行だけルビの分だけ行間が空いてしまうので、次のように行間を設定します。
①Ctrl + Aで文字を全て選択状態にします。
②メニューの「書式」>「段落の書式」をクリックして選択します。
③「段落」の子画面が表示されます。
④「段落」の子画面で、「インデントと間隔」タブをクリックし、「行間」を「1行」から「固定」へ変更し、「数値」を例えば「30pt」に変更し、「OK」ボタンをクリックして押す。
※30ptは本文の文字フォントの大きさが14ptの場合の、切りの良い設定値です。
※30ptでは「見出し1」の文字フォントの大きさが18ptの場合、「見出し1」のテキストにルビが有ると、そのルビの大半が見切れて非表示に成ってしまいます。
※「行間」と言う名前ですが、実際は、1行分の幅と行間の幅の合計のようです。
修正後は、「見出し1」の前で「改ページ」したりして見栄えを整えましょう。
LibreOfficeのWriterはCtrl + Enterで「改ページ」できます。
ちなみに、LibreOfficeのWriterで縦書きにする方法は、次のように成ります。
ところで、LibreOffice24.2で、横書きで「行間」を「固定」にしてルビが綺麗に表示されていてもLibreOffice7.3のWriterで縦書きにすると「行間」が「固定」なのに「固定」ではなくルビの有る行だけルビの分だけ行間が大きく成ってしまう不具合が、直されて、無くなっていました。
さて、まず、縦書きにした直後はルビが有るテキストが左にズレてしまう不具合が有るので、事前に、次のようにして対応します。
①Ctrl + Aで文字を全て選択状態にします。
②メニューバーの「書式」>「段落の書式」をクリックして選択します。
③「段落」子画面が表示されます。
④「段落」子画面で、「配置」タブをクリックして選択し、「文字の配置」の「行の配置」を「自動」から「下」または「英字下揃え」へ変更し、「OK」ボタンをクリックして押します。
いよいよ、実際に、縦書きにします。
⑤メニューバーの「書式」>「ページスタイル」をクリックして選択します。
⑥「ページスタイル」子画面が表示されます。
⑦「ページスタイル」子画面で、「ページ」タブをクリックして選択し、「印刷の向き」を「縦」から「横」へ変更し、「文字の方向」を「左から右へ(横書き)」から「右から左へ(縦書き)」へ変更し、「OK」ボタンをクリックして押します。
ただし、LibreOffice7.3のWriterで縦書きにしてから、保存してしまうと、ページの最後であるページの左側で行間が小さく成ったり、無く成ったり、他の行と重なって表示されてしまいました。
当然、その状態でPDFファイルを作成すると、PDFファイルでも、ページの最後であるページの左側で行間が小さかったり、無かったり、他の行と重なったりしてしまいました。
ページ数が多い場合、修正は、とても大変です。
例えば、著者の場合、「正法眼蔵の現代語訳」は2256ページ有ります。
そのため、LibreOffice7.3などのWriterで縦書きのPDFファイルを作成したい場合は、縦書きにしてから、保存せず、PDFファイルを作成するほうが良いようです。
LibreOffice7.3でもWriterで最初から縦書きにしてゼロから文字を記入していけば、複数の行が重なる事が無いかもしれません。
不具合が有るかもしれないので利用は自己責任でお願いいたします。
2024年9月22日時点の最新のChromeとFirefoxで動作を確認しました。
LibreOffice24.2で動作を確認しました。
ただし、LibreOffice24.2以外のバージョンではエラーが発生するかもしれません。
Windowsでも動くかもしれませんが、未確認です。
コピペする場合は、2文字の全角空白を4文字の半角空白に、全角縦線を|に置換してください。
JaTextToOdt.htmlなどの適当な名前の空の.htmlファイルの内容にコピペしてから、UTF-8という文字コードで保存し、ChromeやFirefoxといったブラウザで.htmlファイルを見ると、利用できます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>1つ以上の.txtファイルを.odtファイルに変換</title>
<!-- This application is under the "Public Domain". -->
<!-- 当アプリケーションはパブリック ドメインです。 -->
</head>
<body>
<div>
1つ以上の.txtファイルを選択 <input type="file" id="file" accept=".txt" multiple />
</div>
<br />
<div>
文書のタイトル <input type="text" id="title" />
</div>
<br />
<div>
セリフ フォント <input type="text" id="serif" value="Noto Serif CJK JP" />
</div>
<br />
<div>
サン セリフ フォント <input type="text" id="sansSerif" value="Noto Sans CJK JP" />
</div>
<br />
<div>
<button type="button" id="convertTextToOdt">1つ以上の.txtファイルを.odtファイルに変換</button>
</div>
<br />
<p id="message"></p>
<br />
<br />
<p> 選択した1つ以上の.txtファイルをLibreOfficeのWriterなどの.odtファイルに変換します。</p>
<br />
<p> 「文書のタイトル」に指定した名前で.odtファイルを作成します。</p>
<p> 例えば、「文書のタイトル」に「正法眼蔵の現代語訳」を指定した場合は、「正法眼蔵の現代語訳.odt」という名前のファイルを作成します。</p>
<br />
<p> 「文書のタイトル」に指定した名前で.odtファイル内のcontent.xmlの「タイトル」を作成します。</p>
<br />
<p> .txtファイル名の先頭から、半角数字の連続と半角空白を除去した名前を.odtファイル内のcontent.xmlの「見出し1」にします。</p>
<p> 例えば、「0001 第一話.txtファイル」が存在する場合は「第一話」を「見出し1」にします。</p>
<br />
<p> .txtファイル名の文字順で並べるので、例えば「0001 第一話.txt」、「0002 第二話.txt」のように、「半角数字の番号 順番の見出し.txt」といった規則で.txtファイル名をつけてください。</p>
<br />
<p> .txtファイルの文字コードはUTF-8にしてください。</p>
<br />
<p> 漢字 半角括弧開き ひらがなかカタカナ 半角括弧閉じ を.odtファイル内のcontent.xmlのルビのXMLに置換します。</p>
<br />
<p> 全角縦線 二重山括弧開き以外の文字 二重山括弧開き 二重山括弧閉じ以外の文字 二重山括弧閉じ を.odtファイル内のcontent.xmlのルビのXMLに置換します。</p>
<p> 上記の「全角縦線」は全角縦線です。</p>
<p> 半角縦線(|)の場合は、.odtファイル内のcontent.xmlのルビのXMLに置換しません。</p>
<br />
<p> 漢字 二重山括弧開き ひらがなかカタカナ 二重山括弧閉じ を.odtファイル内のcontent.xmlのルビのXMLに置換します。</p>
<br />
<p> 空行を.odtファイル内のcontent.xmlの空行のXMLに置換します。</p>
<br />
<p> 何らかのサーバー上で動作する訳ではありません。</p>
<p> あなたのパソコンやスマホなどのブラウザーが動作させます。</p>
<p> オフラインでも動作します。</p>
<script>
//In XML escape, ' must be converted to ' .
function escapeXml(string) {
string = string.replaceAll(/&/g, "&");
string = string.replaceAll(/"/g, """);
string = string.replaceAll(/'/g, "'");
string = string.replaceAll(/</g, "<");
string = string.replaceAll(/>/g, ">");
return string;
}
function $(id) {
return document.getElementById(id);
}
function readAsArrayBufferSync(file) {
return new Promise(function (resolve, reject) {
var fileReader = new FileReader();
fileReader.onload = function () { resolve(fileReader.result); };
fileReader.onerror = function () { reject(fileReader.error); };
fileReader.readAsArrayBuffer(file);
});
}
function readAsTextSync(file) {
return new Promise(function (resolve, reject) {
var fileReader = new FileReader();
fileReader.onload = function () { resolve(fileReader.result); };
fileReader.onerror = function () { reject(fileReader.error); };
fileReader.readAsText(file);
});
}
function crc32(array) {
/*
var crc32Table = [];
for (var index = 0; index < 256; index++) {
var output = index;
for (var bit = 0; bit < 8; bit++) {
if ((output & 0x1) == 0) {
output = output >>> 1;
} else {
output = (output >>> 1) ^ 0xEDB88320;
}
}
crc32Table.push(output >>> 0);
}
*/
var crc32Table = [
0x00000000,
0x77073096,
0xEE0E612C,
0x990951BA,
0x076DC419,
0x706AF48F,
0xE963A535,
0x9E6495A3,
0x0EDB8832,
0x79DCB8A4,
0xE0D5E91E,
0x97D2D988,
0x09B64C2B,
0x7EB17CBD,
0xE7B82D07,
0x90BF1D91,
0x1DB71064,
0x6AB020F2,
0xF3B97148,
0x84BE41DE,
0x1ADAD47D,
0x6DDDE4EB,
0xF4D4B551,
0x83D385C7,
0x136C9856,
0x646BA8C0,
0xFD62F97A,
0x8A65C9EC,
0x14015C4F,
0x63066CD9,
0xFA0F3D63,
0x8D080DF5,
0x3B6E20C8,
0x4C69105E,
0xD56041E4,
0xA2677172,
0x3C03E4D1,
0x4B04D447,
0xD20D85FD,
0xA50AB56B,
0x35B5A8FA,
0x42B2986C,
0xDBBBC9D6,
0xACBCF940,
0x32D86CE3,
0x45DF5C75,
0xDCD60DCF,
0xABD13D59,
0x26D930AC,
0x51DE003A,
0xC8D75180,
0xBFD06116,
0x21B4F4B5,
0x56B3C423,
0xCFBA9599,
0xB8BDA50F,
0x2802B89E,
0x5F058808,
0xC60CD9B2,
0xB10BE924,
0x2F6F7C87,
0x58684C11,
0xC1611DAB,
0xB6662D3D,
0x76DC4190,
0x01DB7106,
0x98D220BC,
0xEFD5102A,
0x71B18589,
0x06B6B51F,
0x9FBFE4A5,
0xE8B8D433,
0x7807C9A2,
0x0F00F934,
0x9609A88E,
0xE10E9818,
0x7F6A0DBB,
0x086D3D2D,
0x91646C97,
0xE6635C01,
0x6B6B51F4,
0x1C6C6162,
0x856530D8,
0xF262004E,
0x6C0695ED,
0x1B01A57B,
0x8208F4C1,
0xF50FC457,
0x65B0D9C6,
0x12B7E950,
0x8BBEB8EA,
0xFCB9887C,
0x62DD1DDF,
0x15DA2D49,
0x8CD37CF3,
0xFBD44C65,
0x4DB26158,
0x3AB551CE,
0xA3BC0074,
0xD4BB30E2,
0x4ADFA541,
0x3DD895D7,
0xA4D1C46D,
0xD3D6F4FB,
0x4369E96A,
0x346ED9FC,
0xAD678846,
0xDA60B8D0,
0x44042D73,
0x33031DE5,
0xAA0A4C5F,
0xDD0D7CC9,
0x5005713C,
0x270241AA,
0xBE0B1010,
0xC90C2086,
0x5768B525,
0x206F85B3,
0xB966D409,
0xCE61E49F,
0x5EDEF90E,
0x29D9C998,
0xB0D09822,
0xC7D7A8B4,
0x59B33D17,
0x2EB40D81,
0xB7BD5C3B,
0xC0BA6CAD,
0xEDB88320,
0x9ABFB3B6,
0x03B6E20C,
0x74B1D29A,
0xEAD54739,
0x9DD277AF,
0x04DB2615,
0x73DC1683,
0xE3630B12,
0x94643B84,
0x0D6D6A3E,
0x7A6A5AA8,
0xE40ECF0B,
0x9309FF9D,
0x0A00AE27,
0x7D079EB1,
0xF00F9344,
0x8708A3D2,
0x1E01F268,
0x6906C2FE,
0xF762575D,
0x806567CB,
0x196C3671,
0x6E6B06E7,
0xFED41B76,
0x89D32BE0,
0x10DA7A5A,
0x67DD4ACC,
0xF9B9DF6F,
0x8EBEEFF9,
0x17B7BE43,
0x60B08ED5,
0xD6D6A3E8,
0xA1D1937E,
0x38D8C2C4,
0x4FDFF252,
0xD1BB67F1,
0xA6BC5767,
0x3FB506DD,
0x48B2364B,
0xD80D2BDA,
0xAF0A1B4C,
0x36034AF6,
0x41047A60,
0xDF60EFC3,
0xA867DF55,
0x316E8EEF,
0x4669BE79,
0xCB61B38C,
0xBC66831A,
0x256FD2A0,
0x5268E236,
0xCC0C7795,
0xBB0B4703,
0x220216B9,
0x5505262F,
0xC5BA3BBE,
0xB2BD0B28,
0x2BB45A92,
0x5CB36A04,
0xC2D7FFA7,
0xB5D0CF31,
0x2CD99E8B,
0x5BDEAE1D,
0x9B64C2B0,
0xEC63F226,
0x756AA39C,
0x026D930A,
0x9C0906A9,
0xEB0E363F,
0x72076785,
0x05005713,
0x95BF4A82,
0xE2B87A14,
0x7BB12BAE,
0x0CB61B38,
0x92D28E9B,
0xE5D5BE0D,
0x7CDCEFB7,
0x0BDBDF21,
0x86D3D2D4,
0xF1D4E242,
0x68DDB3F8,
0x1FDA836E,
0x81BE16CD,
0xF6B9265B,
0x6FB077E1,
0x18B74777,
0x88085AE6,
0xFF0F6A70,
0x66063BCA,
0x11010B5C,
0x8F659EFF,
0xF862AE69,
0x616BFFD3,
0x166CCF45,
0xA00AE278,
0xD70DD2EE,
0x4E048354,
0x3903B3C2,
0xA7672661,
0xD06016F7,
0x4969474D,
0x3E6E77DB,
0xAED16A4A,
0xD9D65ADC,
0x40DF0B66,
0x37D83BF0,
0xA9BCAE53,
0xDEBB9EC5,
0x47B2CF7F,
0x30B5FFE9,
0xBDBDF21C,
0xCABAC28A,
0x53B39330,
0x24B4A3A6,
0xBAD03605,
0xCDD70693,
0x54DE5729,
0x23D967BF,
0xB3667A2E,
0xC4614AB8,
0x5D681B02,
0x2A6F2B94,
0xB40BBE37,
0xC30C8EA1,
0x5A05DF1B,
0x2D02EF8D
];
var output = 0xFFFFFFFF;
for (var index = 0; index < array.length; index++) {
output = ((output >>> 8) ^ crc32Table[array[index] ^ (output & 0xFF)]) >>> 0; //Firefoxが浮動小数点数と誤解しないように>>> 0
}
return (~output) >>> 0; //Firefoxが浮動小数点数と誤解しないように>>> 0
}
function StoredZip() {
this.fileArray = [];
this.add = function (file) {
this.fileArray.push(file);
};
this.zip = async function () {
var fileArray = this.fileArray;
var numberOfFiles = fileArray.length;
var date = new Date();
var hours = date.getHours();
var minutes = date.getMinutes();
var seconds = date.getSeconds();
var zipTime = (hours << 11) + (minutes << 5) + Math.floor(seconds / 2);
var year = date.getFullYear();
var month = (date.getMonth() + 1); //1以上12以下
var dayOfMonth = date.getDate();
var zipDate = ((year - 1980) << 9) + (month << 5) + dayOfMonth;
//zipのローカル ファイル ヘッダーは30バイト + ファイル名のサイズ + ファイルの内容のサイズ
//zipのセントラル ディレクトリ ヘッダーは46バイト + ファイル名のサイズ
//zipのセントラル ディレクトリの終端レコードは22バイト
var zipLocalFileHeaderStartIndex = 0;
var zipCentralDirectoryHeaderTotalSize = 0;
var zipFileSize = 22;
for (var fileIndex = 0; fileIndex < fileArray.length; fileIndex++) {
var file = fileArray[fileIndex];
file.zipLocalFileHeaderStartIndex = zipLocalFileHeaderStartIndex;
var fileName = file.name;
if (fileName.startsWith("/")) {
fileName = fileName.substring(1);
}
var fileNameByteArray = new TextEncoder("utf-8").encode(fileName);
var fileNameSize = fileNameByteArray.length;
//ファイルの場合
if (fileName.endsWith("/") == false) {
var fileContentArrayBuffer = await readAsArrayBufferSync(file);
var fileContentSize = fileContentArrayBuffer.byteLength;
zipLocalFileHeaderStartIndex = zipLocalFileHeaderStartIndex + 30 + fileNameSize + fileContentSize;
zipCentralDirectoryHeaderTotalSize = zipCentralDirectoryHeaderTotalSize + 46 + fileNameSize;
zipFileSize = zipFileSize + 76 + (fileNameSize * 2) + fileContentSize;
//ディレクトリの場合
} else {
zipLocalFileHeaderStartIndex = zipLocalFileHeaderStartIndex + 30 + fileNameSize;
zipCentralDirectoryHeaderTotalSize = zipCentralDirectoryHeaderTotalSize + 46 + fileNameSize;
zipFileSize = zipFileSize + 76 + (fileNameSize * 2);
}
}
var zipCentralDirectoryHeaderStartIndex = zipLocalFileHeaderStartIndex;
var arrayBuffer = new ArrayBuffer(zipFileSize);
var dataView = new DataView(arrayBuffer);
var byteIndex = 0;
for (var fileIndex = 0; fileIndex < fileArray.length; fileIndex++) {
var file = fileArray[fileIndex];
var fileName = file.name;
if (fileName.startsWith("/")) {
fileName = fileName.substring(1);
}
var fileNameByteArray = new TextEncoder("utf-8").encode(fileName);
var fileNameSize = fileNameByteArray.length;
var fileContentArrayBuffer = await readAsArrayBufferSync(file);
var fileContentSize = fileContentArrayBuffer.byteLength;
var fileContentByteArray = new Uint8Array(fileContentArrayBuffer);
var fileContentCrc32 = crc32(fileContentByteArray);
//zipのローカル ファイル ヘッダー
var zipLocalFileHeader = [
//ローカル ファイル ヘッダーを表す固定値
0x50,
0x4B,
0x03,
0x04,
//当zipの展開に必要な最小バージョン
//
//バージョン1.0は16進数表記で順に0A、00。
//
0x0A,
0x00,
//当zipの汎用ビット フラグ
//
//当zip内のファイル名の文字コードがUTF-8の場合は16進数表記で順に00、08。
//
0x00,
0x08,
//当zipの圧縮方法
//
//無圧縮のzipのSTOREDは16進数表記で順に00、00。
//
0x00,
0x00,
//当zip内の当ファイルの最終更新時刻
((zipTime << 8) >>> 8),
(zipTime >>> 8),
//当zip内の当ファイルの最終更新日
((zipDate << 8) >>> 8),
(zipDate >>> 8),
//当zip内の当ファイルの内容のCRC32
//
//ディレクトリの場合は16進数表記で順に00、00、00、00。
//
((fileContentCrc32 << 24) >>> 24),
((fileContentCrc32 << 16) >>> 24),
((fileContentCrc32 << 8) >>> 24),
(fileContentCrc32 >>> 24),
//圧縮後の当zip内の当ファイルのファイル サイズ
//
//ディレクトリの場合は16進数表記で順に00、00、00、00。
//
((fileContentSize << 24) >>> 24),
((fileContentSize << 16) >>> 24),
((fileContentSize << 8) >>> 24),
(fileContentSize >>> 24),
//圧縮前の当zip内の当ファイルのファイル サイズ
//
//ディレクトリの場合は16進数表記で順に00、00、00、00。
//
((fileContentSize << 24) >>> 24),
((fileContentSize << 16) >>> 24),
((fileContentSize << 8) >>> 24),
(fileContentSize >>> 24),
//当zip内の当ファイルの名前のサイズ
((fileNameSize << 8) >>> 8),
(fileNameSize >>> 8),
//zipのエクストラ フィールド
0x00,
0x00
];
//当zip内の当ファイルの名前
//
//ディレクトリの場合はディレクトリ名 + 半角スラッシュ記号(/)
//
//ディレクトリ内のファイルの場合はディレクトリ名 + 半角スラッシュ記号(/) + ファイル名
//
for (var index = 0; index < fileNameByteArray.length; index++) {
zipLocalFileHeader.push(fileNameByteArray[index]);
}
//当zip内の当ファイルの内容
//
//ディレクトリの場合は無し
//
//ファイルの場合
//
//ファイルの名前の最後の文字が半角スラッシュ記号ではない場合
//
if (fileName.endsWith("/") == false) {
for (var index = 0; index < fileContentByteArray.length; index++) {
zipLocalFileHeader.push(fileContentByteArray[index]);
}
}
for (var index = 0; index < zipLocalFileHeader.length; index++) {
dataView.setUint8(byteIndex, zipLocalFileHeader[index], /* リトル エンディアン */ true);
byteIndex++;
}
}
for (var fileIndex = 0; fileIndex < fileArray.length; fileIndex++) {
var file = fileArray[fileIndex];
var zipLocalFileHeaderStartIndex = file.zipLocalFileHeaderStartIndex;
var fileName = file.name;
if (fileName.startsWith("/")) {
fileName = fileName.substring(1);
}
var fileNameByteArray = new TextEncoder("utf-8").encode(fileName);
var fileNameSize = fileNameByteArray.length;
var fileContentArrayBuffer = await readAsArrayBufferSync(file);
var fileContentSize = fileContentArrayBuffer.byteLength;
var fileContentByteArray = new Uint8Array(fileContentArrayBuffer);
var fileContentCrc32 = crc32(fileContentByteArray);
//zipのセントラル ディレクトリ ヘッダー
var zipCentralDirectoryHeader = [
//セントラル ディレクトリ ヘッダーを表す固定値
0x50,
0x4B,
0x01,
0x02,
//当zipを作成したアプリケーションが対応可能なzipのバージョン
0x0A,
//当zipを作成したOS
//
//Unixは16進数表記で03
//
0x03,
//当zipの展開に必要な最小バージョン
//
//バージョン1.0は16進数表記で順に0A、00。
//
0x0A,
0x00,
//当zipの汎用ビット フラグ
//
//当zip内のファイル名の文字コードがUTF-8の場合は16進数表記で順に00、08。
//
0x00,
0x08,
//当zipの圧縮方法
//
//無圧縮のzipのSTOREDは16進数表記で順に00、00。
//
0x00,
0x00,
//当zip内の当ファイルの最終更新時刻
((zipTime << 8) >>> 8),
(zipTime >>> 8),
//当zip内の当ファイルの最終更新日
((zipDate << 8) >>> 8),
(zipDate >>> 8),
//当zip内の当ファイルの内容のCRC32
//
//ディレクトリの場合は16進数表記で順に00、00、00、00。
//
((fileContentCrc32 << 24) >>> 24),
((fileContentCrc32 << 16) >>> 24),
((fileContentCrc32 << 8) >>> 24),
(fileContentCrc32 >>> 24),
//圧縮後の当zip内の当ファイルのファイル サイズ
//
//ディレクトリの場合は16進数表記で順に00、00、00、00。
//
((fileContentSize << 24) >>> 24),
((fileContentSize << 16) >>> 24),
((fileContentSize << 8) >>> 24),
(fileContentSize >>> 24),
//圧縮前の当zip内の当ファイルのファイル サイズ
//
//ディレクトリの場合は16進数表記で順に00、00、00、00。
//
((fileContentSize << 24) >>> 24),
((fileContentSize << 16) >>> 24),
((fileContentSize << 8) >>> 24),
(fileContentSize >>> 24),
//当zip内の当ファイルの名前のサイズ
((fileNameSize << 8) >>> 8),
(fileNameSize >>> 8),
//zipのエクストラ フィールド
0x00,
0x00,
//zipのファイルのコメントのサイズ
//
//zipのファイルのコメントを利用しない場合は16進数表記で順に00、00。
//
0x00,
0x00,
//対応するローカル ファイル ヘッダーが存在するディスクの番号
//
//ディスク分割していない場合は16進数表記で順に00、00。
//
0x00,
0x00,
//(zip)内的ファイル属性
//
//画像などのバイナリーデータの場合は16進数表記で順に00、00。
//
//画像などのテキスト データの場合は16進数表記で順に01、00。
//
0x00,
0x00,
//当zipを作成したOS依存の(zip)外的ファイル属性
0x00,
0x00,
0x00,
0x00,
//対応するローカル ファイル ヘッダーの開始バイトのインデックス番号
((zipLocalFileHeaderStartIndex << 24) >>> 24),
((zipLocalFileHeaderStartIndex << 16) >>> 24),
((zipLocalFileHeaderStartIndex << 8) >>> 24),
(zipLocalFileHeaderStartIndex >>> 24)
];
//当zip内の当ファイルの名前
//
//ディレクトリの場合はディレクトリ名 + 半角スラッシュ記号(/)
//
//ディレクトリ内のファイルの場合はディレクトリ名 + 半角スラッシュ記号(/) + ファイル名
//
for (var index = 0; index < fileNameByteArray.length; index++) {
zipCentralDirectoryHeader.push(fileNameByteArray[index]);
}
for (var index = 0; index < zipCentralDirectoryHeader.length; index++) {
dataView.setUint8(byteIndex, zipCentralDirectoryHeader[index], /* リトル エンディアン */ true);
byteIndex++;
}
}
//zipのセントラル ディレクトリの終端レコード
var zipEndOfCentralDirectoryRecord = [
//セントラル ディレクトリの終端レコードを表す固定値
0x50,
0x4B,
0x05,
0x06,
//当ディスクの番号
//
//ディスク分割していない場合は16進数表記で順に00、00。
//
0x00,
0x00,
//セントラル ディレクトリが開始されるディスクの番号
//
//ディスク分割していない場合は16進数表記で順に00、00。
//
0x00,
0x00,
//(当ディスクに存在する)セントラル ディレクトリの総数
((numberOfFiles << 8) >>> 8),
(numberOfFiles >>> 8),
//セントラル ディレクトリの総数
((numberOfFiles << 8) >>> 8),
(numberOfFiles >>> 8),
//セントラル ディレクトリの総サイズ
((zipCentralDirectoryHeaderTotalSize << 24) >>> 24),
((zipCentralDirectoryHeaderTotalSize << 16) >>> 24),
((zipCentralDirectoryHeaderTotalSize << 8) >>> 24),
(zipCentralDirectoryHeaderTotalSize >>> 24),
//セントラル ディレクトリの開始バイトのインデックス番号
((zipCentralDirectoryHeaderStartIndex << 24) >>> 24),
((zipCentralDirectoryHeaderStartIndex << 16) >>> 24),
((zipCentralDirectoryHeaderStartIndex << 8) >>> 24),
(zipCentralDirectoryHeaderStartIndex >>> 24),
//当zipのコメントのサイズ
0x00,
0x00
];
for (var index = 0; index < zipEndOfCentralDirectoryRecord.length; index++) {
dataView.setUint8(byteIndex, zipEndOfCentralDirectoryRecord[index], /* リトル エンディアン */ true);
byteIndex++;
}
return new Uint8Array(arrayBuffer);
};
}
$("convertTextToOdt").onclick = async function () {
$("message").innerHTML = "";
var title = $("title").value;
var serif = $("serif").value;
var sansSerif = $("sansSerif").value;
if (title == "") {
$("message").innerHTML = "文書のタイトルを記入してください。";
return;
}
if (serif == "") {
$("message").innerHTML = "セリフ フォントを記入してください。";
return;
}
if (sansSerif == "") {
$("message").innerHTML = "サン セリフ フォントを記入してください。";
return;
}
var textFileArray = [];
var files = $("file").files;
for (var index = 0; index < files.length; index++) {
var file = files[index];
var name = file.name;
var type = file.type;
if (type == "text/plain") {
textFileArray.push(file);
}
}
var compare = function (a, b) {
if (a.name == b.name) {
return 0;
}
if (a.name < b.name) {
return -1;
}
return 1;
};
textFileArray.sort(compare);
if (textFileArray.length == 0) {
$("message").innerHTML = ".txtファイルを1つ以上、選択してください。";
return;
}
var storedZip = new StoredZip();
var mimetypeBlob = new Blob([new TextEncoder("utf-8").encode("application/vnd.oasis.opendocument.text")]);
mimetypeBlob.name = "mimetype";
storedZip.add(mimetypeBlob);
var manifestXmlBlob = new Blob([new TextEncoder("utf-8").encode([
'<?xml version="1.0" encoding="UTF-8"?>',
'<manifest:manifest',
' xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"',
' manifest:version="1.3"',
' xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0"',
'>',
' <manifest:file-entry manifest:full-path="/" manifest:version="1.3" manifest:media-type="application/vnd.oasis.opendocument.text" />',
' <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" />',
'</manifest:manifest>\n'
].join("\n"))]);
manifestXmlBlob.name = "META-INF/manifest.xml";
storedZip.add(manifestXmlBlob);
//content.xml
var contentXmlArray = [
'<?xml version="1.0" encoding="UTF-8"?>',
'<office:document-content',
' xmlns:officeooo="http://openoffice.org/2009/office"',
' xmlns:css3t="http://www.w3.org/TR/css3-text/"',
' xmlns:grddl="http://www.w3.org/2003/g/data-view#"',
' xmlns:xhtml="http://www.w3.org/1999/xhtml"',
' xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0"',
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"',
' xmlns:rpt="http://openoffice.org/2005/report"',
' xmlns:dc="http://purl.org/dc/elements/1.1/"',
' xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0"',
' xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"',
' xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"',
' xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"',
' xmlns:oooc="http://openoffice.org/2004/calc"',
' xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"',
' xmlns:ooow="http://openoffice.org/2004/writer"',
' xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0"',
' xmlns:xlink="http://www.w3.org/1999/xlink"',
' xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"',
' xmlns:ooo="http://openoffice.org/2004/office"',
' xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"',
' xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"',
' xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"',
' xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"',
' xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2"',
' xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0"',
' xmlns:tableooo="http://openoffice.org/2009/table"',
' xmlns:drawooo="http://openoffice.org/2010/draw"',
' xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0"',
' xmlns:dom="http://www.w3.org/2001/xml-events"',
' xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0"',
' xmlns:xsd="http://www.w3.org/2001/XMLSchema"',
' xmlns:math="http://www.w3.org/1998/Math/MathML"',
' xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0"',
' xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0"',
' xmlns:xforms="http://www.w3.org/2002/xforms"',
' office:version="1.3"',
'>',
' <office:scripts/>',
' <office:font-face-decls>',
' <style:font-face style:name="' + escapeXml(sansSerif) + '" svg:font-family="'' + escapeXml(sansSerif) + ''" style:font-family-generic="swiss" style:font-pitch="variable" />',
' <style:font-face style:name="' + escapeXml(serif) + '" svg:font-family="'' + escapeXml(serif) + ''" style:font-family-generic="roman" style:font-pitch="variable"/>',
' <style:font-face style:name="' + escapeXml(serif) + '" svg:font-family="'' + escapeXml(serif) + ''" style:font-family-generic="system" style:font-pitch="variable"/>',
' </office:font-face-decls>',
' <office:automatic-styles>',
' <style:style style:name="P1" style:family="paragraph" style:parent-style-name="Title">',
' <loext:graphic-properties draw:fill-gradient-name="gradient" draw:fill-hatch-name="hatch" />',
' <style:paragraph-properties fo:line-height="1.058cm" />',
' <style:text-properties style:font-name-asian="' + escapeXml(serif) + '" />',
' </style:style>',
' <style:style style:name="P2" style:family="paragraph" style:parent-style-name="Heading_20_1">',
' <loext:graphic-properties draw:fill-gradient-name="gradient" draw:fill-hatch-name="hatch" />',
' <style:paragraph-properties fo:line-height="1.058cm" fo:text-align="center" style:justify-single-word="false" />',
' </style:style>',
' <style:style style:name="P3" style:family="paragraph" style:parent-style-name="Standard">',
' <loext:graphic-properties draw:fill-gradient-name="gradient" draw:fill-hatch-name="hatch" />',
' <style:paragraph-properties fo:line-height="1.058cm" />',
' </style:style>',
' <style:style style:name="Ru1" style:family="ruby">',
' <style:ruby-properties style:ruby-align="distribute-space" style:ruby-position="above" loext:ruby-position="above" />',
' <!-- style:ruby-alignのdistribute-spaceは端に付かないルビの均等割付「121」。 -->',
' <!-- style:ruby-alignのdistribute-letterは端に付くルビの均等割付「010」。 -->',
' </style:style>',
' </office:automatic-styles>',
' <office:body>',
' <office:text>',
' <text:sequence-decls>',
' <text:sequence-decl text:display-outline-level="0" text:name="Illustration" />',
' <text:sequence-decl text:display-outline-level="0" text:name="Table" />',
' <text:sequence-decl text:display-outline-level="0" text:name="Text" />',
' <text:sequence-decl text:display-outline-level="0" text:name="Drawing" />',
' <text:sequence-decl text:display-outline-level="0" text:name="Figure" />',
' </text:sequence-decls>',
'',
' <text:p text:style-name="P1">' + escapeXml(title) + '</text:p>',
];
//The following is repeated the number of text files.
for (var index = 0; index < textFileArray.length; index++) {
var name = textFileArray[index].name;
contentXmlArray.push('');
contentXmlArray.push('');
contentXmlArray.push('');
contentXmlArray.push('');
contentXmlArray.push(' <text:p text:style-name="P3" /><!-- 空行 -->');
contentXmlArray.push(' <text:p text:style-name="P3" /><!-- 空行 -->');
contentXmlArray.push(' <text:p text:style-name="P3" /><!-- 空行 -->');
contentXmlArray.push(' <text:p text:style-name="P3" /><!-- 空行 -->');
contentXmlArray.push('');
contentXmlArray.push(' <text:h text:style-name="P2" text:outline-level="1">' + escapeXml(name.replace(/^[0-9]*[ ]*|\.[tT][xX][tT]$/g, "")) + '</text:h>');
contentXmlArray.push('');
contentXmlArray.push(' <text:p text:style-name="P3" /><!-- 空行 -->');
contentXmlArray.push('');
var text = await readAsTextSync(textFileArray[index]);
text = text.replaceAll(/\r\n/g, "\n");
text = text.replaceAll(/\r/g, "\n");
var lineArray = text.split("\n");
for (var i = 0; i < lineArray.length; i++) {
var line = lineArray[i];
//If the readed line is empty,
if (line == "") {
contentXmlArray.push(' <text:p text:style-name="P3" /><!-- 空行 -->');
//If the readed line is the ordinary text,
} else {
//XML escape
var string = escapeXml(line);
//漢字 半角括弧開き ひらがなかカタカナ 半角括弧閉じ をルビに置換
string = string.replaceAll(/([一-鿋々]+)\(([ぁ-ゖァ-ヺー]+)\)/g, '<text:ruby text:style-name="Ru1"><text:ruby-base>$1</text:ruby-base><text:ruby-text>$2</text:ruby-text></text:ruby>');
//全角縦線 二重山括弧開き以外の文字 二重山括弧開き 二重山括弧閉じ以外の文字 二重山括弧閉じ をルビに置換
string = string.replaceAll(/全角縦線([^《]+)《([^》]+)》/g, '<text:ruby text:style-name="Ru1"><text:ruby-base>$1</text:ruby-base><text:ruby-text>$2</text:ruby-text></text:ruby>');
//漢字 二重山括弧開き ひらがなかカタカナ 二重山括弧閉じ をルビに置換
string = string.replaceAll(/([一-鿋々]+)《([ぁ-ゖァ-ヺー]+)》/g, '<text:ruby text:style-name="Ru1"><text:ruby-base>$1</text:ruby-base><text:ruby-text>$2</text:ruby-text></text:ruby>');
contentXmlArray.push(' <text:p text:style-name="P3">' + string + '</text:p>');
}
}
}
contentXmlArray.push(' </office:text>');
contentXmlArray.push(' </office:body>');
contentXmlArray.push('</office:document-content>\n');
var contentXmlBlob = new Blob([new TextEncoder("utf-8").encode(contentXmlArray.join("\n"))]);
contentXmlBlob.name = "content.xml";
storedZip.add(contentXmlBlob);
//styles.xml
var stylesXmlBlob = new Blob([new TextEncoder("utf-8").encode([
'<?xml version="1.0" encoding="UTF-8"?>',
'<office:document-styles',
' xmlns:officeooo="http://openoffice.org/2009/office"',
' xmlns:css3t="http://www.w3.org/TR/css3-text/"',
' xmlns:rpt="http://openoffice.org/2005/report"',
' xmlns:dc="http://purl.org/dc/elements/1.1/"',
' xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0"',
' xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" ',
' xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"',
' xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"',
' xmlns:oooc="http://openoffice.org/2004/calc"',
' xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"',
' xmlns:ooow="http://openoffice.org/2004/writer"',
' xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0"',
' xmlns:xlink="http://www.w3.org/1999/xlink"',
' xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"',
' xmlns:ooo="http://openoffice.org/2004/office"',
' xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"',
' xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"',
' xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"',
' xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"',
' xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2"',
' xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0"',
' xmlns:tableooo="http://openoffice.org/2009/table"',
' xmlns:drawooo="http://openoffice.org/2010/draw"',
' xmlns:grddl="http://www.w3.org/2003/g/data-view#"',
' xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0"',
' xmlns:dom="http://www.w3.org/2001/xml-events"',
' xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0"',
' xmlns:math="http://www.w3.org/1998/Math/MathML"',
' xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0"',
' xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0"',
' xmlns:xhtml="http://www.w3.org/1999/xhtml"',
' office:version="1.3"',
'>',
' <office:font-face-decls>',
' <style:font-face style:name="' + escapeXml(serif) + '" svg:font-family="'' + escapeXml(serif) + ''" style:font-family-generic="roman" style:font-pitch="variable" />',
' <style:font-face style:name="' + escapeXml(sansSerif) + '" svg:font-family="'' + escapeXml(sansSerif) + ''" style:font-family-generic="swiss" style:font-pitch="variable" />',
' <style:font-face style:name="' + escapeXml(serif) + '" svg:font-family="'' + escapeXml(serif) + ''" style:font-family-generic="system" style:font-pitch="variable" />',
' </office:font-face-decls>',
' <office:styles>',
' <draw:gradient draw:name="gradient" draw:style="linear" draw:start-color="#000000" draw:end-color="#ffffff" draw:start-intensity="100%" draw:end-intensity="100%" draw:angle="0deg" draw:border="0%" />',
' <draw:hatch draw:name="hatch" draw:style="single" draw:color="#3465a4" draw:distance="0.02cm" draw:rotation="0" />',
' <style:default-style style:family="graphic">',
' <style:graphic-properties',
' svg:stroke-color="#3465a4"',
' draw:fill-color="#729fcf"',
' fo:wrap-option="no-wrap"',
' draw:shadow-offset-x="0.3cm"',
' draw:shadow-offset-y="0.3cm"',
' draw:start-line-spacing-horizontal="0.283cm"',
' draw:start-line-spacing-vertical="0.283cm"',
' draw:end-line-spacing-horizontal="0.283cm"',
' draw:end-line-spacing-vertical="0.283cm"',
' style:flow-with-text="false"',
' />',
' <style:paragraph-properties',
' style:text-autospace="ideograph-alpha"',
' style:line-break="strict"',
' style:writing-mode="lr-tb"',
' style:font-independent-line-spacing="false"',
' >',
' <style:tab-stops />',
' </style:paragraph-properties>',
' <style:text-properties',
' style:use-window-font-color="true"',
' loext:opacity="0%"',
' style:font-name="' + escapeXml(serif) + '"',
' fo:font-size="14pt"',
' fo:language="en"',
' fo:country="US"',
' style:letter-kerning="true"',
' style:font-name-asian="' + escapeXml(serif) + '"',
' style:font-size-asian="14pt"',
' style:language-asian="ja"',
' style:country-asian="JP"',
' style:font-name-complex="' + escapeXml(serif) + '"',
' style:font-size-complex="14pt"',
' style:language-complex="hi"',
' style:country-complex="IN"',
' />',
' </style:default-style>',
' <style:default-style style:family="paragraph">',
' <style:paragraph-properties',
' fo:orphans="2"',
' fo:widows="2"',
' fo:hyphenation-ladder-count="no-limit"',
' style:text-autospace="ideograph-alpha"',
' style:punctuation-wrap="hanging"',
' style:line-break="strict"',
' style:tab-stop-distance="1.251cm"',
' style:writing-mode="page"',
' />',
' <style:text-properties',
' style:use-window-font-color="true"',
' loext:opacity="0%"',
' style:font-name="' + escapeXml(serif) + '"',
' fo:font-size="14pt"',
' fo:language="en"',
' fo:country="US"',
' style:letter-kerning="true"',
' style:font-name-asian="' + escapeXml(serif) + '"',
' style:font-size-asian="14pt"',
' style:language-asian="ja"',
' style:country-asian="JP"',
' style:font-name-complex="' + escapeXml(serif) + '"',
' style:font-size-complex="14pt"',
' style:language-complex="hi"',
' style:country-complex="IN"',
' />',
' </style:default-style>',
' <style:default-style style:family="table">',
' <style:table-properties table:border-model="collapsing" />',
' </style:default-style>',
' <style:default-style style:family="table-row">',
' <style:table-row-properties fo:keep-together="auto" />',
' </style:default-style>',
' <style:style style:name="Standard" style:family="paragraph" style:class="text" />',
' <style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">',
' <style:paragraph-properties fo:margin-top="0.423cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:keep-with-next="always" />',
' <style:text-properties',
' style:font-name="' + escapeXml(sansSerif) + '"',
' fo:font-family="'' + escapeXml(sansSerif) + ''"',
' style:font-family-generic="swiss"',
' style:font-pitch="variable"',
' fo:font-size="14pt"',
' style:font-name-asian="' + escapeXml(serif) + '"',
' style:font-family-asian="'' + escapeXml(serif) + ''"',
' style:font-family-generic-asian="system"',
' style:font-pitch-asian="variable"',
' style:font-size-asian="14pt"',
' />',
' </style:style>',
' <style:style',
' style:name="Text_20_body"',
' style:display-name="Text body"',
' style:family="paragraph"',
' style:parent-style-name="Standard"',
' style:class="text"',
' >',
' <style:paragraph-properties',
' fo:margin-top="0cm"',
' fo:margin-bottom="0.247cm"',
' style:contextual-spacing="false"',
' fo:line-height="115%"',
' />',
' </style:style>',
' <style:style style:name="List" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="list">',
' <style:text-properties style:font-size-asian="14pt" style:font-name-complex="' + escapeXml(sansSerif) + '" style:font-family-complex="' + escapeXml(sansSerif) + '" style:font-family-generic-complex="swiss" />',
' </style:style>',
' <style:style style:name="Caption" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">',
' <style:paragraph-properties fo:margin-top="0.212cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" text:number-lines="false" text:line-number="0" />',
' <style:text-properties fo:font-size="14pt" fo:font-style="italic" style:font-size-asian="14pt" style:font-style-asian="italic" style:font-name-complex="' + escapeXml(sansSerif) + '" style:font-family-complex="' + escapeXml(sansSerif) + '" style:font-family-generic-complex="swiss" style:font-size-complex="14pt" style:font-style-complex="italic" />',
' </style:style>',
' <style:style style:name="Index" style:family="paragraph" style:parent-style-name="Standard" style:class="index">',
' <style:paragraph-properties text:number-lines="false" text:line-number="0" />',
' <style:text-properties style:font-size-asian="14pt" style:font-name-complex="' + escapeXml(sansSerif) + '" style:font-family-complex="' + escapeXml(sansSerif) + '" style:font-family-generic-complex="swiss" />',
' </style:style>',
' <style:style style:name="Title" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">',
' <style:paragraph-properties fo:text-align="center" style:justify-single-word="false" />',
' <style:text-properties fo:font-size="28pt" fo:font-weight="bold" style:font-size-asian="28pt" style:font-weight-asian="bold" style:font-size-complex="28pt" style:font-weight-complex="bold" />',
' </style:style>',
' <style:style style:name="Heading_20_1" style:display-name="Heading 1" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="1" style:class="text">',
' <style:paragraph-properties fo:margin-top="0.423cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" />',
' <style:text-properties fo:font-size="18pt" fo:font-weight="bold" style:font-size-asian="18pt" style:font-weight-asian="bold" style:font-size-complex="18pt" style:font-weight-complex="bold" />',
' </style:style>',
' <style:style style:name="Rubies" style:family="text">',
' <style:text-properties fo:font-size="6pt" style:text-underline-style="none" style:font-size-asian="6pt" style:font-size-complex="6pt" style:text-emphasize="none" />',
' </style:style>',
' <text:outline-style style:name="Outline">',
' <text:outline-level-style text:level="1" style:num-format="">',
' <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">',
' <style:list-level-label-alignment text:label-followed-by="listtab" />',
' </style:list-level-properties>',
' </text:outline-level-style>',
' <text:outline-level-style text:level="2" style:num-format="">',
' <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">',
' <style:list-level-label-alignment text:label-followed-by="listtab" />',
' </style:list-level-properties>',
' </text:outline-level-style>',
' <text:outline-level-style text:level="3" style:num-format="">',
' <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">',
' <style:list-level-label-alignment text:label-followed-by="listtab" />',
' </style:list-level-properties>',
' </text:outline-level-style>',
' <text:outline-level-style text:level="4" style:num-format="">',
' <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">',
' <style:list-level-label-alignment text:label-followed-by="listtab" />',
' </style:list-level-properties>',
' </text:outline-level-style>',
' <text:outline-level-style text:level="5" style:num-format="">',
' <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">',
' <style:list-level-label-alignment text:label-followed-by="listtab" />',
' </style:list-level-properties>',
' </text:outline-level-style>',
' <text:outline-level-style text:level="6" style:num-format="">',
' <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">',
' <style:list-level-label-alignment text:label-followed-by="listtab" />',
' </style:list-level-properties>',
' </text:outline-level-style>',
' <text:outline-level-style text:level="7" style:num-format="">',
' <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">',
' <style:list-level-label-alignment text:label-followed-by="listtab" />',
' </style:list-level-properties>',
' </text:outline-level-style>',
' <text:outline-level-style text:level="8" style:num-format="">',
' <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">',
' <style:list-level-label-alignment text:label-followed-by="listtab" />',
' </style:list-level-properties>',
' </text:outline-level-style>',
' <text:outline-level-style text:level="9" style:num-format="">',
' <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">',
' <style:list-level-label-alignment text:label-followed-by="listtab" />',
' </style:list-level-properties>',
' </text:outline-level-style>',
' <text:outline-level-style text:level="10" style:num-format="">',
' <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">',
' <style:list-level-label-alignment text:label-followed-by="listtab" />',
' </style:list-level-properties>',
' </text:outline-level-style>',
' </text:outline-style>',
' <text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document" />',
' <text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0" />',
' <text:linenumbering-configuration text:number-lines="false" text:offset="0.499cm" style:num-format="1" text:number-position="left" text:increment="5" />',
' </office:styles>',
' <office:automatic-styles>',
' <style:page-layout style:name="Mpm1">',
' <style:page-layout-properties fo:page-width="21.001cm" fo:page-height="29.7cm" style:num-format="1" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:footnote-max-height="0cm" loext:margin-gutter="0cm">',
' <style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="solid" style:adjustment="left" style:rel-width="25%" style:color="#000000" />',
' </style:page-layout-properties>',
' <style:header-style />',
' <style:footer-style />',
' </style:page-layout>',
' </office:automatic-styles>',
' <office:master-styles>',
' <style:master-page style:name="Standard" style:page-layout-name="Mpm1" />',
' </office:master-styles>',
'</office:document-styles>\n'
].join("\n"))]);
stylesXmlBlob.name = "styles.xml";
storedZip.add(stylesXmlBlob);
var blob = new Blob([await storedZip.zip()], {type: "application/zip"});
var url = URL.createObjectURL(blob);
var aTagElement = document.createElement("a");
aTagElement.href = url;
aTagElement.download = title + ".odt";
aTagElement.click();
URL.revokeObjectURL(url);
$("message").innerHTML = "1つ以上の.txtファイルから.odtファイルへの変換が完了しました。";
};
</script>
</body>
</html>
.txtファイル名の先頭から半角数字の連続と半角空白を除去した名前を各話の見出しにします。
例えば、0001 第一話.txtファイルが存在する場合は第一話を見出しにします。
.txtファイル名の文字順で並べるので、例えば0001 第一話.txt、0002 第二話.txtのように、半角数字の番号 半角空白 順番の各話の見出し.txtといった規則で.txtファイル名をつけてください。
.txtファイルの文字コードはUTF-8にしてください。
漢字 半角括弧開き ひらがなかカタカナ 半角括弧閉じ をcontent.xmlのルビのXMLに置換します。
漢字 二重山括弧開き ひらがなかカタカナ 二重山括弧閉じ をcontent.xmlのルビのXMLに置換します。
全角縦線 二重山括弧開き以外の文字 二重山括弧開き 二重山括弧閉じ以外の文字 二重山括弧閉じ をcontent.xmlのルビのXMLに置換します。
空行をcontent.xmlの空行のXMLに置換します。
補足で、.odtファイルの仕様を簡単に説明します。
①.odtファイルで認められているzip圧縮アルゴリズムは無圧縮(STORED)か、DEFLATEDだけです。
②.odtファイルであるzipファイル内の最初のファイルは、mimetypeファイルである必要が有ります。
③.odtファイルであるzipファイル内には、mimetypeファイルとMETA-INF/manifest.xmlが存在する必要が有ります。
④.odtファイルであるzipファイル内に、mimetypeファイル、META-INF/manifest.xml、文書の内容であるcontent.xmlの3つが有れば、LibreOffice24.2のWriterは正常なファイルとして開いてくれますが、styles.xmlが無いと、フォントの種類や太字などの情報が無く成ってしまいます。
ちなみに、LibreOfficeのWriterなどがodtファイル内のcontent.xmlなどのファイルの内容の半角空白を無視するように仕様に書かれていますが、そのため、LibreOfficeのWriterはcontent.xmlなどを半角空白も改行コードも無しに1行だけで作成してしまいます。
そのせいで、CPUの性能が悪くメモリ容量も少ないパソコンで文書量が多いcontent.xmlファイルをエディターで開くと、パソコンがフリーズしかけます。
ご注意ください。