From bdcfaab8885074b192d237d863e0b02b437e0da2 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 7 Feb 2010 14:48:41 +0000 Subject: [PATCH] Basic html generator. [SVN r59570] --- Jamfile.v2 | 2 + boostbook.cpp | 40 +---- encoder_impl.cpp | 17 +++ encoder_impl.hpp | 67 ++++++++ html.cpp | 390 +++++++++++++++++++++++++++++++++++++++++++++++ quickbook.cpp | 21 ++- 6 files changed, 491 insertions(+), 46 deletions(-) create mode 100644 encoder_impl.cpp create mode 100644 encoder_impl.hpp create mode 100644 html.cpp diff --git a/Jamfile.v2 b/Jamfile.v2 index 6e0ca84..b8a8cb9 100644 --- a/Jamfile.v2 +++ b/Jamfile.v2 @@ -40,6 +40,8 @@ exe quickbook code_snippet_grammar.cpp syntax_highlight.cpp boostbook.cpp + html.cpp + encoder_impl.cpp /boost//program_options /boost//filesystem : #QUICKBOOK_NO_DATES diff --git a/boostbook.cpp b/boostbook.cpp index ed11b72..1e72024 100644 --- a/boostbook.cpp +++ b/boostbook.cpp @@ -1,47 +1,9 @@ #include #include -#include "fwd.hpp" -#include "encoder.hpp" -#include "phrase.hpp" -#include "state.hpp" +#include "encoder_impl.hpp" namespace quickbook { - struct boostbook_encoder : encoder { - virtual void operator()(quickbook::state&, doc_info const&) const; - virtual void operator()(quickbook::state&, doc_info_post const&) const; - - // Note: char is a plain quickbook character, string is an encoded - // boostbook string. Oops. - virtual void operator()(quickbook::state&, char) const; - virtual void operator()(quickbook::state&, std::string const&) const; - virtual void operator()(quickbook::state&, anchor const&) const; - virtual void operator()(quickbook::state&, link const&) const; - virtual void operator()(quickbook::state&, formatted const&) const; - virtual void operator()(quickbook::state&, break_ const&) const; - virtual void operator()(quickbook::state&, image2 const&) const; - - virtual void operator()(quickbook::state&, hr) const; - virtual void operator()(quickbook::state&, begin_section2 const&) const; - virtual void operator()(quickbook::state&, end_section2 const&) const; - virtual void operator()(quickbook::state&, heading2 const&) const; - virtual void operator()(quickbook::state&, variablelist const&) const; - virtual void operator()(quickbook::state&, table2 const&) const; - virtual void operator()(quickbook::state&, xinclude2 const&) const; - virtual void operator()(quickbook::state&, list2 const&) const; - - virtual void operator()(quickbook::state&, code_token const&) const; - - virtual std::string encode(std::string const&) const; - virtual std::string encode(char) const; - virtual std::string encode(char const*) const; - }; - - encoder_ptr create_encoder(std::string const&) - { - return boost::shared_ptr(new boostbook_encoder()); - } - template std::string encode_impl(Iter first, Iter last) { diff --git a/encoder_impl.cpp b/encoder_impl.cpp new file mode 100644 index 0000000..7240ce8 --- /dev/null +++ b/encoder_impl.cpp @@ -0,0 +1,17 @@ +#include "encoder_impl.hpp" + +namespace quickbook +{ + encoder_ptr create_encoder(std::string const& name) + { + if(name == "html") { + return boost::shared_ptr(new html_encoder()); + } + else if(name == "boostbook") { + return boost::shared_ptr(new boostbook_encoder()); + } + else { + BOOST_ASSERT(false); + } + } +} \ No newline at end of file diff --git a/encoder_impl.hpp b/encoder_impl.hpp new file mode 100644 index 0000000..963bc3d --- /dev/null +++ b/encoder_impl.hpp @@ -0,0 +1,67 @@ +#include "fwd.hpp" +#include "encoder.hpp" +#include "phrase.hpp" +#include "state.hpp" + +namespace quickbook +{ + struct boostbook_encoder : encoder { + virtual void operator()(quickbook::state&, doc_info const&) const; + virtual void operator()(quickbook::state&, doc_info_post const&) const; + + // Note: char is a plain quickbook character, string is an encoded + // boostbook string. Oops. + virtual void operator()(quickbook::state&, char) const; + virtual void operator()(quickbook::state&, std::string const&) const; + virtual void operator()(quickbook::state&, anchor const&) const; + virtual void operator()(quickbook::state&, link const&) const; + virtual void operator()(quickbook::state&, formatted const&) const; + virtual void operator()(quickbook::state&, break_ const&) const; + virtual void operator()(quickbook::state&, image2 const&) const; + + virtual void operator()(quickbook::state&, hr) const; + virtual void operator()(quickbook::state&, begin_section2 const&) const; + virtual void operator()(quickbook::state&, end_section2 const&) const; + virtual void operator()(quickbook::state&, heading2 const&) const; + virtual void operator()(quickbook::state&, variablelist const&) const; + virtual void operator()(quickbook::state&, table2 const&) const; + virtual void operator()(quickbook::state&, xinclude2 const&) const; + virtual void operator()(quickbook::state&, list2 const&) const; + + virtual void operator()(quickbook::state&, code_token const&) const; + + virtual std::string encode(std::string const&) const; + virtual std::string encode(char) const; + virtual std::string encode(char const*) const; + }; + + struct html_encoder : encoder { + virtual void operator()(quickbook::state&, doc_info const&) const; + virtual void operator()(quickbook::state&, doc_info_post const&) const; + + // Note: char is a plain quickbook character, string is an encoded + // html string. Oops. + virtual void operator()(quickbook::state&, char) const; + virtual void operator()(quickbook::state&, std::string const&) const; + virtual void operator()(quickbook::state&, anchor const&) const; + virtual void operator()(quickbook::state&, link const&) const; + virtual void operator()(quickbook::state&, formatted const&) const; + virtual void operator()(quickbook::state&, break_ const&) const; + virtual void operator()(quickbook::state&, image2 const&) const; + + virtual void operator()(quickbook::state&, hr) const; + virtual void operator()(quickbook::state&, begin_section2 const&) const; + virtual void operator()(quickbook::state&, end_section2 const&) const; + virtual void operator()(quickbook::state&, heading2 const&) const; + virtual void operator()(quickbook::state&, variablelist const&) const; + virtual void operator()(quickbook::state&, table2 const&) const; + virtual void operator()(quickbook::state&, xinclude2 const&) const; + virtual void operator()(quickbook::state&, list2 const&) const; + + virtual void operator()(quickbook::state&, code_token const&) const; + + virtual std::string encode(std::string const&) const; + virtual std::string encode(char) const; + virtual std::string encode(char const*) const; + }; +} \ No newline at end of file diff --git a/html.cpp b/html.cpp new file mode 100644 index 0000000..e162715 --- /dev/null +++ b/html.cpp @@ -0,0 +1,390 @@ +#include +#include +#include "encoder_impl.hpp" + +namespace quickbook +{ + 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) const { + return encode_impl(x.begin(), x.end()); + } + + std::string html_encoder::encode(char const* x) const { + char const* end = x; + while(*end) ++end; + return encode_impl(x, end); + } + + std::string html_encoder::encode(char c) const { + 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) const + { + state.phrase << x; + } + + void html_encoder::operator()(quickbook::state& state, char x) const + { + state.phrase << encode(x); + } + + void html_encoder::operator()(quickbook::state& state, anchor const& x) const { + state.phrase << "
    \n"; + } + + void html_encoder::operator()(quickbook::state& state, link const& x) const { + 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) const { + 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) const { + html_markup m = markup_map.at("break"); + state.phrase << m.pre; + } + + void html_encoder::operator()(quickbook::state& state, image2 const& x) const { + 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) const { + state.phrase << markup_map.at("hr").pre; + } + + void html_encoder::operator()(quickbook::state& state, begin_section2 const& x) const { + // 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" + ; + } + } + + void html_encoder::operator()(quickbook::state& state, end_section2 const& x) const { + state.phrase << "
    "; + } + + void html_encoder::operator()(quickbook::state& state, heading2 const& x) const { + state.phrase + << "" + ; + + if(!x.linkend.empty()) { + state.phrase + << "
    " + ; + } + state.phrase << x.content; + + state.phrase << ""; + } + + void html_encoder::operator()(quickbook::state& state, variablelist const& x) const + { + // 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) const + { + 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) const + { + // TODO: ????? + //state.phrase << "\n\n"; + } + + void html_encoder::operator()(quickbook::state& state, list2 const& x) const + { + 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, code_token const& x) const + { + 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) const + { + // 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()) + { + state.phrase << "

    \n"; + BOOST_FOREACH(doc_info::author const& author, info.doc_authors) { + state.phrase + << "
    \n" + << author.first + << " " + << author.second + << "
    \n"; + } + state.phrase << "

    \n"; + } + + if(!info.doc_copyrights.empty()) + { + state.phrase + << "

    \n"; + + BOOST_FOREACH(doc_info::copyright_entry const& copyright, + info.doc_copyrights) + { + state.phrase << "

    \nCopyright © "; + + bool previous = false; + BOOST_FOREACH(std::string const& year, copyright.first) { + if(previous) state.phrase << ", "; + state.phrase << year; + previous = true; + } + + state.phrase + << " " + << copyright.second + << "
    \n" + ; + } + + state.phrase + << "

    \n"; + } + + state.phrase + << "
    " + ; + } + + void html_encoder::operator()(quickbook::state& state, doc_info_post const& x) const + { + // if we're ignoring the document info, do nothing. + if (x.info.ignore) return; + + // We've finished generating our output. Here's what we'll do + // *after* everything else. + state.phrase << ""; + } +} diff --git a/quickbook.cpp b/quickbook.cpp index 46b7b48..dc86c20 100644 --- a/quickbook.cpp +++ b/quickbook.cpp @@ -109,9 +109,9 @@ namespace quickbook } static int - parse(char const* filein_, fs::path const& outdir, string_stream& out, bool ignore_docinfo = false) + parse(char const* filein_, fs::path const& outdir, string_stream& out, std::string const& encoder) { - quickbook::state state(filein_, outdir, out, create_encoder("boostbook")); + quickbook::state state(filein_, outdir, out, create_encoder(encoder)); bool r = parse(filein_, state); if (state.section_level != 0) detail::outwarn(filein_) @@ -126,7 +126,8 @@ namespace quickbook , char const* fileout_ , int indent , int linewidth - , bool pretty_print) + , bool pretty_print + , std::string const& encoder) { int result = 0; std::ofstream fileout(fileout_); @@ -136,7 +137,7 @@ namespace quickbook if (pretty_print) { string_stream buffer; - result = parse(filein_, outdir, buffer); + result = parse(filein_, outdir, buffer, encoder); if (result == 0) { result = post_process(buffer.str(), fileout, indent, linewidth); @@ -145,7 +146,7 @@ namespace quickbook else { string_stream buffer; - result = parse(filein_, outdir, buffer); + result = parse(filein_, outdir, buffer, encoder); fileout << buffer.str(); } return result; @@ -186,6 +187,8 @@ main(int argc, char* argv[]) ("debug", "debug mode (for developers)") ("ms-errors", "use Microsoft Visual Studio style error & warn message format") ("include-path,I", value< std::vector >(), "include path") + ("boostbook", "generate boostbook (default)") + ("html", "generate html") ; positional_options_description p; @@ -198,6 +201,9 @@ main(int argc, char* argv[]) store(command_line_parser(argc, argv).options(desc).positional(p).run(), vm); notify(vm); + // TODO: Allow overwritten options. + std::string encoder = vm.count("html") ? "html" : "boostbook"; + if (vm.count("help")) { std::cout << desc << "\n"; @@ -269,14 +275,15 @@ main(int argc, char* argv[]) else { fileout = quickbook::detail::remove_extension(filein.c_str()); - fileout += ".xml"; + // TODO: More generic here: + fileout += encoder == "html" ? ".html" : ".xml"; } std::cout << "Generating Output File: " << fileout << std::endl; - return quickbook::parse(filein.c_str(), fileout.c_str(), indent, linewidth, pretty_print); + return quickbook::parse(filein.c_str(), fileout.c_str(), indent, linewidth, pretty_print, encoder); } else {