#include #include #include #include "encoder_impl.hpp" namespace quickbook { html_encoder::html_encoder() : footnote_id(0) {} html_encoder::~html_encoder() {} template std::string encode_impl(Iter first, Iter last) { std::string r; for(;first != last; ++first) { switch (*first) { case '<': r += "<"; break; case '>': r += ">"; break; case '&': r += "&"; break; case '"': r += """; break; default: r += *first; break; } } return r; } std::string html_encoder::encode(std::string const& x) { return encode_impl(x.begin(), x.end()); } std::string html_encoder::encode(char const* x) { char const* end = x; while(*end) ++end; return encode_impl(x, end); } std::string html_encoder::encode(char c) { return encode_impl(&c, &c + 1); } namespace { struct html_markup { char const* quickbook; char const* pre; char const* post; }; html_markup markups[] = { { "", "", "" }, { "comment", "" }, { "paragraph", "

\n", "

\n" }, { "blurb", "
\n", "
\n" }, { "blockquote", "
", "
" }, { "preformatted", "
", "
" }, { "warning", "
", "
" }, { "caution", "
", "
" }, { "important", "
", "
" }, { "note", "
", "
" }, { "tip", "
", "
" }, { "list_item", "
  • \n", "\n
  • " }, { "bold", "", "" }, // TODO: Or ? Should this be semantically meaningfull? { "italic", "", "" }, // TODO: Or ? { "underline", "", "" }, { "teletype", "", "" }, { "strikethrough", "", "" }, { "quote", "", "" }, { "url", "" }, { "link", "" }, { "funcref", "", "" }, { "classref", "", "" }, { "memberref", "", "" }, { "enumref", "", "" }, { "macroref", "", "" }, { "headerref", "", "" }, { "conceptref", "", "" }, { "globalref", "", "" }, // Will need to deal with this explicitly { "footnote", "

    ", "

    " }, { "escape", "", "" }, { "replaceable", "", "" }, // TODO: Is it possible to have an entry with a term, but no items. { "varlistentry", "", "" }, { "varlistterm", "
    ", "
    " }, { "varlistitem", "
    ", "
    " }, { "header", "", "\n" }, { "row", "", "\n" }, { "cell", "", "" }, { "programlisting", "
    ", "
    \n" }, { "code", "", "" }, { "hr", "
    ", "" }, { "break", "
    ", "" }, }; std::map markup_map; struct initialize { initialize() { BOOST_FOREACH(html_markup m, markups) { markup_map[m.quickbook] = m; } } } initialize_instance; } void html_encoder::operator()(quickbook::state& state, std::string const& x) { state.phrase << x; } void html_encoder::operator()(quickbook::state& state, char x) { state.phrase << encode(x); } void html_encoder::operator()(quickbook::state& state, anchor const& x) { state.phrase << "
    \n"; } void html_encoder::operator()(quickbook::state& state, link const& x) { html_markup m = markup_map.at(x.type); if(*m.pre) { state.phrase << m.pre; state.phrase << encode(x.destination); state.phrase << "\">"; state.phrase << x.content; state.phrase << m.post; } else { state.phrase << x.content; } } void html_encoder::operator()(quickbook::state& state, formatted const& x) { std::string type = x.type; if(type == "footnote") { int id = ++footnote_id; footnote_stack.top().push_back(footnote(id, x.content)); // TODO: Maybe get section id from the state? state.phrase << "" << "" << "[" << id << "]" << "" << "" ; } else { html_markup m = markup_map.at(x.type); state.phrase << m.pre << x.content << m.post; } } void html_encoder::operator()(quickbook::state& state, break_ const& x) { html_markup m = markup_map.at("break"); state.phrase << m.pre; } void html_encoder::operator()(quickbook::state& state, image2 const& x) { std::map translate; translate["alt"] = "alt"; translate["fileref"] = "src"; translate["width"] = "width"; translate["height"] = "height"; state.phrase << "first]; if(!html_attribute) continue; state.phrase << " " << html_attribute << "=\"" << encode(attr_first->second) << "\""; } state.phrase << "/>"; } void html_encoder::operator()(quickbook::state& state, hr) { state.phrase << markup_map.at("hr").pre; } void html_encoder::operator()(quickbook::state& state, begin_section2 const& x) { // TODO: Should this be stored in the 'token', or at least have a nicer interface. int level = state.section_level + 1; if (level > 6) level = 6; state.phrase << "\n
    \n"; if(x.linkend.empty()) { state.phrase << "" << x.content << "\n" ; } else { state.phrase << "" << x.content << "\n" ; } push_footnotes(state); } void html_encoder::operator()(quickbook::state& state, end_section2 const& x) { pop_footnotes(state); state.phrase << "
    "; } void html_encoder::operator()(quickbook::state& state, heading2 const& x) { state.phrase << "" ; if(!x.linkend.empty()) { state.phrase << "" ; } state.phrase << x.content; state.phrase << ""; } void html_encoder::operator()(quickbook::state& state, variablelist const& x) { // TODO: What should I do for the title? state.phrase << "

    "; state.phrase << encode(x.title); state.phrase << "

    \n"; state.phrase << "
    \n"; html_markup m = markup_map.at("varlistentry"); for(std::vector::const_iterator it = x.entries.begin(); it != x.entries.end(); ++it) { state.phrase << m.pre; std::for_each(it->begin(), it->end(), encode_action(state, *this)); state.phrase << m.post; } state.phrase << "
    \n"; } void html_encoder::operator()(quickbook::state& state, table2 const& x) { if (x.title) { state.phrase << "\n"; state.phrase << ""; state.phrase << encode(*x.title); state.phrase << ""; } else { state.phrase << "\n"; } html_markup m = markup_map.at("row"); if (x.head) { state.phrase << ""; state.phrase << m.pre; std::for_each(x.head->begin(), x.head->end(), encode_action(state, *this)); state.phrase << m.post; state.phrase << "\n"; } state.phrase << "\n"; for(std::vector::const_iterator it = x.rows.begin(); it != x.rows.end(); ++it) { state.phrase << m.pre; std::for_each(it->begin(), it->end(), encode_action(state, *this)); state.phrase << m.post; } state.phrase << "\n"; state.phrase << "\n"; } void html_encoder::operator()(quickbook::state& state, xinclude2 const& x) { // TODO: ????? //state.phrase << "\n\n"; } void html_encoder::operator()(quickbook::state& state, list2 const& x) { state.phrase << std::string(x.mark == '#' ? "
      \n" : "
        \n"); for(std::vector::const_iterator it = x.items.begin(), end = x.items.end(); it != end; ++it) { state.phrase << "
      • \n" << it->content; if(!it->sublist.items.empty()) (*this)(state, it->sublist); state.phrase << std::string("\n
      • "); } state.phrase << std::string(x.mark == '#' ? "\n
    " : "\n"); } void html_encoder::operator()(quickbook::state& state, callout_link const& x) { state.phrase << "" << "" // TODO: Get correct number // TODO: Better style << "(c)" << "" << "" ; } void html_encoder::operator()(quickbook::state& state, callout_list const& x) { state.phrase << "
    "; unsigned int count = 0; BOOST_FOREACH(callout_item const& c, x) { state.phrase << "
    " << "" << "callout " << ++count << "" << "
    " << "
    " << c.content << "
    " ; } state.phrase << ""; } void html_encoder::operator()(quickbook::state& state, code_token const& x) { std::string type = x.type; if(type == "space") { state.phrase << x.text; } else { state.phrase << "" << encode(x.text) << ""; } } void html_encoder::operator()(quickbook::state& state, doc_info const& info) { // if we're ignoring the document info, we're done. if (info.ignore) return; state.phrase << "" << "" << "" << info.doc_title << "" << "" << "" << "" << "
    " << "

    " << info.doc_title << "

    " ; if(!info.doc_authors.empty() || !info.doc_copyrights.empty() || !info.doc_license.empty()) { state.phrase << "
    \n"; if(!info.doc_authors.empty()) { state.phrase << "
    " << (info.doc_authors.size() == 1 ? "Author:" : "Authors:") << "
    \n" ; BOOST_FOREACH(doc_info::author const& author, info.doc_authors) { state.phrase << "
    " << author.first << " " << author.second << "
    \n"; } } if(!info.doc_copyrights.empty()) { state.phrase << "
    Copyright:
    \n" ; BOOST_FOREACH(doc_info::copyright_entry const& copyright, info.doc_copyrights) { state.phrase << "
    © "; unsigned int range_state = 0; unsigned int previous = 0; BOOST_FOREACH(unsigned int year, copyright.first) { switch(range_state) { case 0: // Start state.phrase << year; range_state = 1; break; case 1: // Printed a year in last iteration if(year == previous + 1) { range_state = 2; } else { state.phrase << ", " << year; range_state = 1; } break; case 2: // In the middle of a range if(year != previous + 1) { state.phrase << " - " << previous << ", " << year; range_state = 1; } break; } previous = year; } if(range_state == 2) state.phrase << " - " << previous; state.phrase << " " << copyright.second << "
    \n" ; } } if (!info.doc_license.empty()) { state.phrase << "
    License:
    \n" << "
    " << info.doc_license << "
    \n" ; } state.phrase << "
    \n"; } state.phrase << "
    \n" ; push_footnotes(state); } void html_encoder::operator()(quickbook::state& state, doc_info_post const& x) { // if we're ignoring the document info, do nothing. if (x.info.ignore) return; pop_footnotes(state); // We've finished generating our output. Here's what we'll do // *after* everything else. state.phrase << ""; } void html_encoder::push_footnotes(quickbook::state& state) { footnote_stack.push(footnotes()); } void html_encoder::pop_footnotes(quickbook::state& state) { BOOST_ASSERT(!footnote_stack.empty()); footnotes notes = footnote_stack.top(); footnote_stack.pop(); if(!notes.empty()) { state.phrase << "
    \n"; BOOST_FOREACH(footnote const& x, notes) { state.phrase << "
    " << "" << "Footnote" << "" << "
    " << "
    " << x.content << "
    " ; } state.phrase << "
    \n"; } } }