-*- mode: org -*- #+TITLE: doc_reform output odt #+DESCRIPTION: documents - structuring, publishing in multiple formats & search #+FILETAGS: :spine:output:xml:odt: #+AUTHOR: Ralph Amissah #+EMAIL: [[mailto:ralph.amissah@gmail.com][ralph.amissah@gmail.com]] #+COPYRIGHT: Copyright (C) 2015 - 2019 Ralph Amissah #+LANGUAGE: en #+STARTUP: indent content hideblocks hidestars #+OPTIONS: H:3 num:nil toc:t \n:nil @:t ::t |:t ^:nil _:nil -:t f:t *:t <:t #+OPTIONS: TeX:t LaTeX:t skip:nil d:nil todo:t pri:nil tags:not-in-toc #+OPTIONS: author:nil email:nil creator:nil timestamp:nil #+PROPERTY: header-args :results silent :padline no :exports code :cache no :noweb yes #+EXPORT_SELECT_TAGS: export - [[./spine.org][spine]] [[./][org/]] - [[./output_hub.org][output_hub]] * odt :odt: - cover object types - zip contents - xml closures? unnecessary, this is sax rather than dom no? |---------------------------------+-----------------------+-------------------+------------------------+------------------| | function | filename | module | variable | output_odt | |---------------------------------+-----------------------+-------------------+------------------------+------------------| | identify doc filetype | mimetype | odt_mimetypes | mimetypes | output_odt_fixed | |---------------------------------+-----------------------+-------------------+------------------------+------------------| | doc manifest | manifest.rdf | | | output_odt_fixed | |---------------------------------+-----------------------+-------------------+------------------------+------------------| | settings | settings.xml | outputODTsettings | | output_odt_fixed | |---------------------------------+-----------------------+-------------------+------------------------+------------------| | doc xml styles | styles.xml | outputODTstyles | | output_odt_fixed | |---------------------------------+-----------------------+-------------------+------------------------+------------------| | identify doc root * (imagelist) | META-INF/manifest.xml | odt_container_xml | meta_inf_container_xml | | |---------------------------------+-----------------------+-------------------+------------------------+------------------| | doc content * | content.xml | odt_content | content_odt | | |---------------------------------+-----------------------+-------------------+------------------------+------------------| | images * | Pictures/ | | | copy_odt_images | |---------------------------------+-----------------------+-------------------+------------------------+------------------| | doc meta * (timestamp) | meta.xml | odt_metadata | | | |---------------------------------+-----------------------+-------------------+------------------------+------------------| ** _module template_ :odf:odt:module: #+BEGIN_SRC d :tangle "../src/doc_reform/io_out/odt.d" :noweb yes module doc_reform.io_out.odt; template formatODT() { <> mixin spineOutputRgxInit; struct formatODT { static auto rgx = Rgx(); <> } } template outputODT() { <> mixin InternalMarkup; mixin spineOutputRgxInit; auto rgx = Rgx(); // mixin outputXmlODT; <> <> <> <> <> <> <> <> <> <> <> } #+END_SRC ** odt format objects *** detail **** odf structure - unless code - images - links - internal / relative - external **** object attrib ***** tags #+name: odt_format_objects #+BEGIN_SRC d string _tags(O)(const O obj){ string _tags = ""; if (obj.tags.anchor_tags.length > 0) { foreach (tag_; obj.tags.anchor_tags) { if (tag_.length > 0) { _tags ~= format(q"┃ ┃", _special_characters(tag_, obj), _special_characters(tag_, obj), ); } } } return _tags; } #+END_SRC ****** anchor tags #+name: odt_format_objects #+BEGIN_SRC d string _xhtml_anchor_tags(O)(O obj) { const(string[]) anchor_tags = obj.tags.anchor_tags; string tags=""; if (anchor_tags.length > 0) { foreach (tag; anchor_tags) { if (!(tag.empty)) { tags ~= ""; } } } return tags; } #+END_SRC ***** ocn object number display #+name: odt_format_objects #+BEGIN_SRC d string obj_num(O)(const O obj){ // TODO string _on; _on = (obj.metainfo.object_number.empty) ? "" : (format(q"┃ 「%s」┃", obj.metainfo.object_number, )); return _on; } #+END_SRC ***** footnotes #+name: odt_format_objects #+begin_src d string _footnotes()(string _txt){ static auto rgx = Rgx(); _txt = _txt.replaceAll( rgx.inline_notes_al_regular_number_note, format(q"┃ %s %s ┃", "$1", "$1", "$2", ) ); return _txt; } #+end_src ***** bullet #+name: odt_format_objects #+BEGIN_SRC d string _bullet(O)(const O obj){ string _b = ""; if (obj.attrib.bullet) { _b = format(q"┃● ┃",); } return _b; } #+END_SRC ***** para (with bullet, indent levels, footnotes extracted) #+name: odt_format_objects #+BEGIN_SRC d string _indent(O)(string _txt, const O obj) { // TODO // if (obj.attrib.indent_base > 0 || // obj.attrib.indent_hang > 0 // ) { if (obj.metainfo.is_a == "toc") { _txt = format(q"┃ %s %s%s%s ┃", (obj.attrib.indent_base < 4) ? "\n " : "", obj.attrib.indent_base, obj.attrib.indent_base, _tags(obj), _txt, obj_num(obj), ); } else if (!empty(obj.metainfo.object_number)) { if (obj.attrib.indent_base == 0 && obj.attrib.indent_hang == 0) { _txt = format(q"┃ %s %s%s%s ┃", _bullet(obj), obj.metainfo.object_number, obj.metainfo.object_number, _tags(obj), _txt, obj_num(obj), ); } else if (obj.attrib.indent_base == obj.attrib.indent_hang) { _txt = format(q"┃ %s %s%s%s ┃", obj.attrib.indent_base, _bullet(obj), obj.metainfo.object_number, obj.metainfo.object_number, _tags(obj), _txt, obj_num(obj), ); } else { _txt = format(q"┃ %s %s%s%s ┃", obj.attrib.indent_base, obj.attrib.indent_hang, _bullet(obj), obj.metainfo.object_number, obj.metainfo.object_number, _tags(obj), _txt, obj_num(obj), ); } } else { if (obj.attrib.indent_base == 0 && obj.attrib.indent_hang == 0) { /+ can omit and would explicitly set indent base and hang as 0 each below +/ _txt = format(q"┃ %s %s%s%s ┃", _bullet(obj), _tags(obj), _txt, obj_num(obj), ); } else if (obj.attrib.indent_base == obj.attrib.indent_hang) { _txt = format(q"┃ %s %s%s%s ┃", obj.attrib.indent_base, _bullet(obj), _tags(obj), _txt, obj_num(obj), ); } else { _txt = format(q"┃ %s %s%s%s ┃", _bullet(obj), obj.attrib.indent_base, obj.attrib.indent_hang, _tags(obj), _txt, obj_num(obj), ); } } return _txt; } #+END_SRC ***** block type #+name: odt_format_objects #+BEGIN_SRC d string _block_type_delimiters(O)(string[] _block_lines, const O obj) { // TODO string _block = ""; foreach (i, _line; _block_lines) { _line = _footnotes(_line); if (i == 0) { _block ~= format(q"┃ %s %s ┃", _bullet(obj), obj.metainfo.object_number, obj.metainfo.object_number, // _tags(obj), _line, ); } else { _block ~= format(q"┃ %s┃", _line); } } _block ~= format(q"┃ 「%s」 ┃", obj_num(obj)); return _block; } #+END_SRC **** object inline ***** special characters #+name: odt_format_objects #+BEGIN_SRC d string _special_characters(O)(string _txt, const O obj) { _txt = _txt .replaceAll(rgx.xhtml_ampersand, "&") .replaceAll(rgx.xhtml_quotation, """) .replaceAll(rgx.xhtml_less_than, "<") .replaceAll(rgx.xhtml_greater_than, ">") .replaceAll(rgx.nbsp_char, " "); return _txt; } #+END_SRC ***** preserve white space #+name: odt_format_objects #+BEGIN_SRC d string _preserve_white_spaces(O)(string _txt, const O obj) { if (obj.metainfo.is_a == "code" || obj.metainfo.is_a == "verse" || obj.metainfo.is_a == "block") { _txt = _txt .replaceAll(rgx.space, " "); } return _txt; } #+END_SRC ***** font_face #+name: odt_format_objects #+BEGIN_SRC d string _font_face(string _txt){ _txt = _txt .replaceAll(rgx.inline_strike, "$1") .replaceAll(rgx.inline_insert, "$1") .replaceAll(rgx.inline_cite, "$1") .replaceAll(rgx.inline_emphasis, format(q"┃%s┃","$1")) .replaceAll(rgx.inline_bold, format(q"┃%s┃","$1")) .replaceAll(rgx.inline_italics, format(q"┃%s┃","$1")) .replaceAll(rgx.inline_underscore, format(q"┃%s┃","$1")) .replaceAll(rgx.inline_superscript, format(q"┃%s┃","$1")) .replaceAll(rgx.inline_subscript, format(q"┃%s┃","$1")) .replaceAll(rgx.inline_mono, format(q"┃%s┃","$1")); return _txt; } #+END_SRC ***** object number #+name: odt_format_objects #+BEGIN_SRC d auto _obj_num(O)(O obj) { // NOT USED YET struct objNum { string reference() { return format(q"┃ ┃", obj.object_number, obj.object_number, ); } string display() { return format(q"┃ %s%s%s ┃", on_o, obj.object_number, on_c, ); } } return objNum(); } #+END_SRC ***** break page #+name: odt_format_objects #+BEGIN_SRC d string _break_page()() { return format(q"┃ ┃", ); } #+END_SRC #+BEGIN_SRC d string _break_page()() { return format(q"┃ ┃", ); } #+END_SRC ***** empty lines break #+name: odt_format_objects #+BEGIN_SRC d string _empty_line_break(O)(string _txt, const O obj) { if (obj.metainfo.is_a == "code" || obj.metainfo.is_a == "verse" || obj.metainfo.is_a == "block") { _txt = _txt .replaceAll(rgx.br_empty_line, "
"); } return _txt; } #+END_SRC ***** links: url, mail #+name: odt_format_objects #+BEGIN_SRC d string _links(O)(string _txt, const O obj) { if (obj.metainfo.is_a != "code") { if (obj.metainfo.is_a == "toc") { _txt = replaceAll!(m => m[1] ~ "┤" ~ (replaceAll!(n => n["type"] ~ n["path"] ~ (n["file"].encodeComponent) )((obj.stow.link[m["num"].to!ulong]).to!string, rgx.uri_identify_components)) ~ "├" )(_txt, rgx.inline_link_number_only) .replaceAll(rgx.inline_link, format(q"┃%s┃", _special_characters("$3", obj), _special_characters("$1", obj) )); } else { _txt = replaceAll!(m => m[1] ~ "┤" ~ (replaceAll!(n => n["type"] ~ n["path"] ~ (n["file"].encodeComponent) )((obj.stow.link[m["num"].to!ulong]).to!string, rgx.uri_identify_components)) ~ "├" )(_txt, rgx.inline_link_number_only) .replaceAll(rgx.inline_link, format(q"┃%s┃", _special_characters("$2", obj), _special_characters("$1", obj) )); } } debug(links) { if (obj.text.match(rgx.inline_link_number) && _txt.match(rgx.inline_link_number_only) ) { writeln(">> ", _txt); writeln("is_a: ", obj.metainfo.is_a); } } return _txt; } #+END_SRC ***** image #+name: odt_format_objects #+BEGIN_SRC d string _images(O)(string _txt, const O obj) { if (_txt.match(rgx.inline_image)) { _txt = _txt .replaceAll(rgx.inline_image, ("$1 $6")) .replaceAll( rgx.inline_link_empty, ("$1")); } return _txt; } #+END_SRC **** markup hub (including font face) #+name: odt_format_objects #+BEGIN_SRC d string markup(O)(const O obj) { /+ markup TODO +/ string _txt = obj.text; _txt = _special_characters(_txt, obj); // TODO & why both obj & obj.text, consider also in output_xmls.org if (obj.metainfo.is_a == "code" || obj.metainfo.is_a == "verse" || obj.metainfo.is_a == "block") { _txt = replaceAll!(m => _preserve_white_spaces(m[1], obj))(_txt, rgx.spaces_keep); } // check that this does what you want, keep: leading spaces (indent) & more than single spaces within text // _txt = _preserve_white_spaces(_txt, obj); // (obj.metainfo.is_a == "code" || obj.metainfo.is_a == "verse" || obj.metainfo.is_a == "block") _txt = _font_face(_txt); _txt = _images(_txt, obj); // (obj.metainfo.is_a != "code") _txt = _links(_txt, obj); // (obj.metainfo.is_a != "code") _txt = _empty_line_break(_txt, obj); // (obj.metainfo.is_a == "code" || obj.metainfo.is_a == "verse" || obj.metainfo.is_a == "block") return _txt; } #+END_SRC *** objects **** para type ***** heading #+name: odt_format_objects #+BEGIN_SRC d string heading(O,M)( const O obj, const M doc_matters, ) { assert(obj.metainfo.is_of_part == "body" || "frontmatter" || "backmatter"); assert(obj.metainfo.is_of_section == "body" || "toc" || "endnotes" || "glossary" || "bibliography" || "bookindex" || "blurb"); assert(obj.metainfo.is_of_type == "para"); assert(obj.metainfo.is_a == "heading"); string _o_txt_odt = markup(obj); if (obj.metainfo.dummy_heading && (obj.metainfo.is_a == "toc" || obj.metainfo.is_a == "heading")) { _o_txt_odt = ""; } else if (obj.metainfo.is_a == "toc") { _o_txt_odt = format(q"┃%s %s%s%s ┃", _break_page, obj.metainfo.heading_lev_markup, obj.metainfo.heading_lev_markup, _tags(obj), _o_txt_odt, obj_num(obj), ); } else { _o_txt_odt = _footnotes(_o_txt_odt); _o_txt_odt = format(q"┃%s %s%s%s ┃", _break_page, obj.metainfo.heading_lev_markup, obj.metainfo.heading_lev_markup, obj.metainfo.object_number, obj.metainfo.object_number, _tags(obj), _o_txt_odt, obj_num(obj), ); } return _o_txt_odt; } #+END_SRC ***** para #+name: odt_format_objects #+BEGIN_SRC d string para(O,M)( const O obj, const M doc_matters, ) { assert(obj.metainfo.is_of_part == "body" || "frontmatter" || "backmatter"); assert(obj.metainfo.is_of_section == "body" || "toc" || "endnotes" || "glossary" || "bibliography" || "bookindex" || "blurb"); assert(obj.metainfo.is_of_type == "para"); assert(obj.metainfo.is_a == "para" || "toc" || "endnotes" || "glossary" || "bibliography" || "bookindex" || "blurb"); string _o_txt_odt; if (obj.metainfo.dummy_heading && (obj.metainfo.is_a == "toc" || obj.metainfo.is_a == "heading")) { _o_txt_odt = ""; } else { _o_txt_odt = markup(obj); _o_txt_odt = _footnotes(_o_txt_odt); _o_txt_odt = _indent(_o_txt_odt, obj); // final setting? } return _o_txt_odt; } #+END_SRC **** block type ***** quote #+name: odt_format_objects #+BEGIN_SRC d string quote(O,M)( const O obj, const M doc_matters, ) { assert(obj.metainfo.is_of_part == "body"); assert(obj.metainfo.is_of_section == "body" || "glossary" || "bibliography" || "bookindex" || "blurb"); assert(obj.metainfo.is_of_type == "block"); assert(obj.metainfo.is_a == "quote"); string _o_txt_odt = markup(obj); _o_txt_odt = _footnotes(_o_txt_odt); // decide return _o_txt_odt; } #+END_SRC ***** group - group delimiter - preserves double newlines (paragraph delimiter) - the "group" delimiter is different from the "block" delimiter in that groups do not preserve whitespace, the "block" mark does #+name: odt_format_objects #+BEGIN_SRC d string group(O,M)( const O obj, const M doc_matters, ) { assert(obj.metainfo.is_of_part == "body"); assert(obj.metainfo.is_of_section == "body" || "glossary" || "bibliography" || "bookindex" || "blurb"); assert(obj.metainfo.is_of_type == "block"); assert(obj.metainfo.is_a == "group"); string _o_txt_odt = markup(obj); /+ TODO - split lines - only double newlines (paragraph delimiter), (not line breaks, single new lines) - no hard space indentation +/ string[] _block_lines = (_o_txt_odt).split(rgx.br_newlines_linebreaks); _o_txt_odt = _block_type_delimiters(_block_lines, obj); return _o_txt_odt; } #+END_SRC ***** block - block delimiter - preserves spaces - preserves newlines - the "block" delimiter is different from the "group" delimiter, in that blocks preserve whitespace, the "group" mark does not - - split lines - each line including empty lines - hard space indentation - "^[ ]"   - count number only at beginning of line and replace each #+name: odt_format_objects #+BEGIN_SRC d string block(O,M)( const O obj, const M doc_matters, ) { assert(obj.metainfo.is_of_part == "body"); assert(obj.metainfo.is_of_section == "body" || "glossary" || "bibliography" || "bookindex" || "blurb"); assert(obj.metainfo.is_of_type == "block"); assert(obj.metainfo.is_a == "block"); string _o_txt_odt = markup(obj); string[] _block_lines = (_o_txt_odt).split(rgx.br_newlines_linebreaks); _o_txt_odt = _block_type_delimiters(_block_lines, obj); return _o_txt_odt; } #+END_SRC ***** verse - poem delimiters - creates verses where double newlines occur (paragraph delimiter) - preserves spaces - preserves newlines #+name: odt_format_objects #+BEGIN_SRC d string verse(O,M)( const O obj, const M doc_matters, ) { assert(obj.metainfo.is_of_part == "body"); assert(obj.metainfo.is_of_section == "body" || "glossary" || "bibliography" || "bookindex" || "blurb"); assert(obj.metainfo.is_of_type == "block"); assert(obj.metainfo.is_a == "verse"); string _o_txt_odt = markup(obj); string[] _block_lines = (_o_txt_odt).split(rgx.br_newlines_linebreaks); _o_txt_odt = _block_type_delimiters(_block_lines, obj); return _o_txt_odt; } #+END_SRC ***** code #+name: odt_format_objects #+BEGIN_SRC d string code(O,M)( const O obj, const M doc_matters, ) { assert(obj.metainfo.is_of_part == "body"); assert(obj.metainfo.is_of_section == "body"); assert(obj.metainfo.is_of_type == "block"); assert(obj.metainfo.is_a == "code"); string _o_txt_odt = markup(obj); /+ TODO - split lines - each line including empty lines - hard space indentation - "^[ ]"   - count number only at beginning of line and replace each +/ string[] _block_lines = (_o_txt_odt).split(rgx.br_newlines_linebreaks); string _block = ""; foreach (i, _line; _block_lines) { if (i == 1) { _block ~= format(q"┃ %s ┃", obj.metainfo.object_number, obj.metainfo.object_number, _line, ); } else { _block ~= format(q"┃ %s┃", _line); } } _block ~= format(q"┃ 「%s」 ┃", obj_num(obj)); _o_txt_odt = _block; return _o_txt_odt; } #+END_SRC ***** table ****** tablarize #+name: odt_format_objects #+BEGIN_SRC d auto tablarize(O)( const O obj, string _txt, ) { string[] _table_rows = (_txt).split(rgx.table_delimiter_row); string[] _table_cols; string _table; string _tablenote; foreach(row_idx, row; _table_rows) { _table_cols = row.split(rgx.table_delimiter_col); _table ~= ""; foreach(col_idx, cell; _table_cols) { if ((_table_cols.length == 1) && (_table_rows.length <= row_idx+2)) { // check row_idx+2 (rather than == ++row_idx) _tablenote ~= cell; } else { _table ~= format(q"┃ %s ┃", (row_idx == 0 && obj.table.heading) ? "Table_Heading" : "P_table_cell", cell, ); } } _table ~= ""; } auto t = tuple( _table, _tablenote, ); return t; } #+END_SRC ****** table #+name: odt_format_objects #+BEGIN_SRC d int _table_number = 0; string table(O,M)( const O obj, const M doc_matters, ) { assert(obj.metainfo.is_of_part == "body"); assert(obj.metainfo.is_of_section == "body"); assert(obj.metainfo.is_of_type == "block"); assert(obj.metainfo.is_a == "table"); string _o_txt_odt = markup(obj); auto t = tablarize(obj, _o_txt_odt); string _note = t[1]; _o_txt_odt = format(q"┃ %s 「%s」 ┃", _table_number++, obj.metainfo.object_number, obj.metainfo.object_number, obj.table.number_of_columns, t[0], obj.metainfo.object_number, // _note, ); return _o_txt_odt; } #+END_SRC ** write odt output :odf:odt:out: #+name: output_odt #+BEGIN_SRC d void writeOutputODT(W,I)( const W odt_content, I doc_matters, ) { auto pth_odt = spinePathsODT!()(doc_matters); auto fn_odt = pth_odt.odt_file; auto zip = new ZipArchive(); // ZipArchive zip = new ZipArchive(); void ODTzip()(string contents, string fn) { auto zip_arc_member_file = new ArchiveMember(); zip_arc_member_file.name = fn; auto zip_data = new OutBuffer(); (doc_matters.opt.action.debug_do) ? zip_data.write(contents.dup) : zip_data.write(contents.dup .replaceAll(rgx.spaces_line_start, "") .replaceAll(rgx.newline, "") .strip ); zip_arc_member_file.expandedData = zip_data.toBytes(); zip.addMember(zip_arc_member_file); createZipFile!()(fn_odt, zip.build()); } try { if (!exists(pth_odt.base_pth)) { // check pth_odt.base_pth.mkdirRecurse; } string fn; File f; { fn = pth_odt.mimetype("zip"); ODTzip(odt_content.mimetype, fn); } { fn = pth_odt.manifest_rdf("zip"); ODTzip(odt_content.manifest_rdf, fn); } { fn = pth_odt.settings_xml("zip"); ODTzip(odt_content.settings_xml, fn); } { fn = pth_odt.styles_xml("zip"); ODTzip(odt_content.styles_xml, fn); } { fn = pth_odt.content_xml("zip"); ODTzip(odt_content.content_xml, fn); } { fn = pth_odt.manifest_xml("zip"); ODTzip(odt_content.manifest_xml, fn); } { fn = pth_odt.meta_xml("zip"); ODTzip(odt_content.meta_xml, fn); } { /+ (images) +/ foreach (image; doc_matters.srcs.image_list) { auto fn_src = doc_matters.src.image_dir_path ~ "/" ~ image; auto fn_out = pth_odt.image_dir("zip") ~ "/" ~ image; if (exists(fn_src)) { { auto zip_arc_member_file = new ArchiveMember(); zip_arc_member_file.name = fn_out; auto zip_data = new OutBuffer(); zip_data.write(cast(char[]) ((fn_src).read)); zip_arc_member_file.expandedData = zip_data.toBytes(); zip.addMember(zip_arc_member_file); createZipFile!()(fn_odt, zip.build()); } } } } if (!(doc_matters.opt.action.quiet)) { writeln(" ", pth_odt.odt_file); } } catch (ErrnoException ex) { // Handle error } if (doc_matters.opt.action.debug_do) { pth_odt.mimetype("fs"); /+ (mimetype) +/ pth_odt.manifest_rdf("fs"); /+ (manifest.rdf) +/ pth_odt.settings_xml("fs"); /+ (settings.xml) +/ pth_odt.styles_xml("fs"); /+ (styles_xml) +/ pth_odt.content_xml("fs"); pth_odt.manifest_xml("fs"); pth_odt.meta_xml("fs"); } } #+END_SRC ** odt output hub [#A] :odf:odt:out: #+name: output_odt #+BEGIN_SRC d void outputODT(D,I)( const D doc_abstraction, I doc_matters, ) { struct ODT { /+ fixed output +/ string mimetype; string manifest_rdf; string settings_xml; string styles_xml; /+ variable output +/ string content_xml; // substantive content string manifest_xml; // image list changes string meta_xml; // time stamp } // auto pth_odt = spinePathsODT!()(doc_matters); auto odt = ODT(); odt.mimetype = mimetype; odt.manifest_rdf = manifest_rdf; odt.settings_xml = settings_xml; odt.styles_xml = styles_xml; odt.content_xml = content_xml(doc_abstraction, doc_matters); odt.manifest_xml = manifest_xml(doc_matters); odt.meta_xml = meta_xml(doc_matters); odt.writeOutputODT(doc_matters); dirtree(doc_matters); images_cp(doc_matters); // copy images } #+END_SRC * stuff ** shared *** output imports #+name: output_imports #+BEGIN_SRC d import doc_reform.io_out; import std.digest.sha, std.file, std.outbuffer, std.uri, std.zip, std.conv : to; import doc_reform.io_out.create_zip_file, doc_reform.io_out.xmls, doc_reform.io_out.xmls_css; #+END_SRC *** make directory tree #+name: output_odt_fixed_dirtree #+BEGIN_SRC d void dirtree(I)( I doc_matters, ) { auto pth_odt = spinePathsODT!()(doc_matters); if (doc_matters.opt.action.debug_do) { /+ (dir tree) +/ if (!exists(pth_odt.meta_inf_dir("fs"))) { pth_odt.meta_inf_dir("fs").mkdirRecurse; } if (!exists(pth_odt.image_dir("fs"))) { pth_odt.image_dir("fs").mkdirRecurse; } } if (!exists(pth_odt.base_pth)) { pth_odt.base_pth.mkdirRecurse; } // return 0; } #+END_SRC ** fixed items *** mimetype :mimetype: #+name: output_odt_fixed_mimetype #+BEGIN_SRC d string mimetype() { string mimetype_ = format(q"┃application/vnd.oasis.opendocument.text┃"); return mimetype_; } #+END_SRC *** manifest.rdf :manifest_rdf: #+name: output_odt_fixed_manifest_rdf #+BEGIN_SRC d string manifest_rdf() { string _manifest_rdf = format(q"┃ #+END_SRC #+name: output_odt_fixed_manifest_rdf #+BEGIN_SRC xml #+END_SRC #+name: output_odt_fixed_manifest_rdf #+BEGIN_SRC d ┃"); return _manifest_rdf; } #+END_SRC *** settings.xml :settings: #+name: output_odt_fixed_settings_xml #+BEGIN_SRC d string settings_xml() { string _settings_xml = format(q"┃ #+END_SRC #+name: output_odt_fixed_settings_xml #+BEGIN_SRC xml 0 0 0 0 true false view2 0 0 0 0 0 0 0 2 true 100 false true false false true true false true false false false false false true true 0 false false false false true false false false false true true false false true false true false high-resolution 1 0 true false true false true false true false true false true true false true true true false false false 0 false false true true #+END_SRC #+name: output_odt_fixed_settings_xml #+BEGIN_SRC d ┃"); return _settings_xml; } #+END_SRC *** styles.xml :styles_xml: #+name: output_odt_fixed_styles_xml #+BEGIN_SRC d string styles_xml() { string _styles_xml = format(q"┃ #+END_SRC #+name: output_odt_fixed_styles_xml #+BEGIN_SRC xml #+END_SRC #+name: output_odt_fixed_styles_xml #+BEGIN_SRC d ┃"); return _styles_xml; } #+END_SRC ** moving parts *** ↻ content.xml TODO :content_xml: **** content head ***** head open #+name: output_odt_variable_content_xml #+BEGIN_SRC d string odt_head(I)(I doc_matters) { string _has_tables = format(q"┃ #+END_SRC ***** if table include within head #+name: output_odt_variable_content_xml #+BEGIN_SRC xml #+END_SRC ***** head #+name: output_odt_variable_content_xml #+BEGIN_SRC d ┃",); string _odt_head = format(q"┃ #+END_SRC ***** head xml #+name: output_odt_variable_content_xml #+BEGIN_SRC xml %s #+END_SRC ***** head close #+name: output_odt_variable_content_xml #+BEGIN_SRC d ┃", (doc_matters.has.tables > 0) ? _has_tables : "", ); return _odt_head; } #+END_SRC **** ↻ content body ***** body open #+name: output_odt_variable_content_xml #+BEGIN_SRC d string odt_body(D,I)( const D doc_abstraction, I doc_matters, ) { mixin formatODT; auto odt_format = formatODT(); string delimit = ""; string doc_odt = ""; string _txt = ""; #+END_SRC ***** ↻ the loop & outer switch (sections & objects) format output #+name: output_odt_variable_content_xml #+BEGIN_SRC d foreach (part; doc_matters.has.keys_seq.scroll) { foreach (obj; doc_abstraction[part]) { switch (obj.metainfo.is_of_part) { #+END_SRC ****** frontmatter #+name: output_odt_variable_content_xml #+BEGIN_SRC d case "frontmatter": assert(part == "head" || "toc"); switch (obj.metainfo.is_of_type) { case "para": switch (obj.metainfo.is_a) { case "heading": _txt = delimit ~ odt_format.heading(obj, doc_matters); goto default; case "toc": _txt = odt_format.para(obj, doc_matters); goto default; default: doc_odt ~= _txt; _txt = ""; break; } break; default: break; } break; #+END_SRC ****** body #+name: output_odt_variable_content_xml #+BEGIN_SRC d case "body": assert(part == "body" || "head"); // surprise switch (obj.metainfo.is_of_type) { case "para": switch (obj.metainfo.is_a) { case "heading": _txt = delimit ~ odt_format.heading(obj, doc_matters); goto default; case "para": _txt = odt_format.para(obj, doc_matters); goto default; default: doc_odt ~= _txt; _txt = ""; break; } break; case "block": switch (obj.metainfo.is_a) { case "quote": _txt = odt_format.quote(obj, doc_matters); goto default; case "group": _txt = odt_format.group(obj, doc_matters); goto default; case "block": _txt = odt_format.block(obj, doc_matters); goto default; case "verse": _txt = odt_format.verse(obj, doc_matters); goto default; case "code": _txt = odt_format.code(obj, doc_matters); goto default; case "table": _txt = odt_format.table(obj, doc_matters); goto default; default: doc_odt ~= _txt; _txt = ""; break; } break; default: break; } break; #+END_SRC ****** backmatter #+name: output_odt_variable_content_xml #+BEGIN_SRC d case "backmatter": assert(part == "endnotes" || "glossary" || "bibliography" || "bookindex" || "blurb" || "tail"); switch (obj.metainfo.is_of_type) { case "para": switch (obj.metainfo.is_a) { case "heading": _txt = delimit ~ odt_format.heading(obj, doc_matters); goto default; case "endnote": assert(part == "endnotes"); _txt = odt_format.para(obj, doc_matters); goto default; case "glossary": assert(part == "glossary"); _txt = odt_format.para(obj, doc_matters); goto default; case "bibliography": assert(part == "bibliography"); _txt = odt_format.para(obj, doc_matters); goto default; case "bookindex": assert(part == "bookindex"); _txt = odt_format.para(obj, doc_matters); goto default; case "blurb": assert(part == "blurb"); _txt = odt_format.para(obj, doc_matters); goto default; default: doc_odt ~= _txt; _txt = ""; break; } break; default: break; } break; case "comment": break; default: { /+ debug +/ if (doc_matters.opt.action.debug_do && doc_matters.opt.action.verbose) { writeln(__FILE__, ":", __LINE__, ": ", obj.metainfo.is_of_part); writeln(__FILE__, ":", __LINE__, ": ", obj.metainfo.is_a); writeln(__FILE__, ":", __LINE__, ": ", obj.text); } } break; #+END_SRC ***** closings & post loop #+name: output_odt_variable_content_xml #+BEGIN_SRC d } } } return doc_odt; } #+END_SRC **** content book index? #+name: output_odt_variable_content_xml #+BEGIN_SRC d #+END_SRC **** content tail #+name: output_odt_variable_content_xml #+BEGIN_SRC d string odt_tail() { string _odt_tail = format(q"┃spine: <www.doc_reform.org> and <www.sisudoc.org> ┃",); return _odt_tail; } #+END_SRC **** hub #+name: output_odt_variable_content_xml #+BEGIN_SRC d string content_xml(D,I)( const D doc_abstraction, I doc_matters, ) { string _content_xml; string break_line = (doc_matters.opt.action.debug_do) ? "\n" : ""; string odt_break_page = format(q"┃┃",); string br_pg = format(q"┃┃",); _content_xml ~= odt_head(doc_matters); _content_xml ~= odt_body(doc_abstraction, doc_matters); _content_xml ~= odt_tail; return _content_xml; } #+END_SRC *** manifest.xml (images list changes) :manifest_xml: - META-INF/manifest.xml - image list changes #+name: output_odt_variable_manifest_xml #+BEGIN_SRC d string manifest_xml(M)( auto ref M doc_matters, ) { string _bullet = format(q"┃┃"); string[] _images = [ _bullet ]; foreach (image; doc_matters.srcs.image_list) { _images ~= format(q"┃ ┃", image); } string _manifest_xml = format(q"┃ #+END_SRC #+name: output_odt_variable_manifest_xml #+BEGIN_SRC xml %s #+END_SRC #+name: output_odt_variable_manifest_xml #+BEGIN_SRC d ┃", _images.join("\n"), ); return _manifest_xml; } #+END_SRC *** meta.xml (time stamp) :meta_xml: #+name: output_odt_variable_meta_xml #+BEGIN_SRC d string meta_xml(M)( auto ref M doc_matters, ) { /+ (meta_xml includes output time-stamp) +/ string _meta_xml = format(q"┃ #+END_SRC #+name: output_odt_variable_meta_xml #+BEGIN_SRC xml %s %s %s en-US #+END_SRC #+name: output_odt_variable_meta_xml #+BEGIN_SRC d ┃", doc_matters.generator_program.name_and_version, doc_matters.generated_time, doc_matters.generated_time, ); return _meta_xml; } #+END_SRC *** copy images :images: #+name: output_odt_variable_copy_odt_images #+BEGIN_SRC d void images_cp(M)( auto ref M doc_matters, ) { { /+ (copy odt images) +/ auto pth_odt = spinePathsODT!()(doc_matters); foreach (image; doc_matters.srcs.image_list) { auto fn_src_in = doc_matters.src.image_dir_path ~ "/" ~ image; auto fn_src_out_file = pth_odt.image_dir("fs") ~ "/" ~ image; auto fn_src_out_zip = pth_odt.image_dir("zip") ~ "/" ~ image; if (exists(fn_src_in)) { if (doc_matters.opt.action.debug_do) { if (doc_matters.opt.action.debug_do) { fn_src_in.copy(fn_src_out_file); } } } } } // return 0; } #+END_SRC