Changeset e275a84b1e164b543ad6bc2d139b84410e06693b

Show
Ignore:
Timestamp:
10/25/09 15:53:08 (4 years ago)
Author:
Armin Burgmeier <armin@…>
Parents:
3adef45a9e5b72edb4099a59e2a3e00f544ef7e0
Children:
e861d030b244626b06e7d2409ebfb124b466dc59
git-committer:
Armin Burgmeier <armin@arbur.net> / 2009-10-25T15:53:08Z+0100
Message:

Added export HTML command

Location:
code
Files:
2 added
4 modified

Legend:

Unmodified
Added
Removed
  • code/commands/file-commands.cpp

    r3adef45 re275a84  
    222222} 
    223223 
    224 #include <iomanip> 
    225  
    226 #include <ctime> 
    227 #include <cstring> 
    228 #include <cmath> 
    229  
    230 #include <gtkmm/textbuffer.h> 
    231 #include <libxml++/libxml++.h> 
    232  
    233 #include <libinftextgtk/inf-text-gtk-buffer.h> 
    234 #include "util/i18n.hpp" 
    235  
    236 struct TagComparator 
    237 { 
    238   bool operator()(GtkTextTag* first, GtkTextTag* second) const 
    239   { 
    240     return gtk_text_tag_get_priority(first) < 
    241            gtk_text_tag_get_priority(second); 
    242   } 
    243 }; 
    244  
    245 // Sort tags by priority, so that we declare them in order 
    246 typedef std::set<GtkTextTag*, TagComparator> priority_tag_set; 
    247  
    248 // We don't use Glib::ustring::compose for now because 
    249 // it's formatting support does not compile properly under 
    250 // Windows. See https://bugzilla.gnome.org/show_bug.cgi?id=599340 
    251 Glib::ustring uprintf(gchar const* fmt, ...) 
    252 { 
    253         va_list args; 
    254         va_start(args, fmt); 
    255         gchar* str = g_strdup_vprintf(fmt, args); 
    256         va_end(args); 
    257         Glib::ustring result; 
    258         try 
    259         { 
    260                 result = str; 
    261         } 
    262         catch (...) 
    263         { 
    264                 g_free(str); 
    265                 throw; 
    266         } 
    267         g_free(str); 
    268         return result; 
    269 } 
    270  
    271 unsigned int color_to_rgb(GdkColor* color) 
    272 { 
    273         return ((color->red   & 0xff00) << 8) 
    274              |  (color->green & 0xff00) 
    275              | ((color->blue  & 0xff00) >> 8); 
    276 } 
    277  
    278 // write the Gtk::TextBuffer from document into content, inserting <span/>s for 
    279 // line breaks and authorship of chunks of text, also save all users 
    280 // encountered and the total number of lines dumped 
    281 void dump_buffer(Gobby::DocWindow& document, 
    282                  xmlpp::Element* content, 
    283                  std::set<InfTextUser*>& users, 
    284                  priority_tag_set& tags, 
    285                  unsigned int& line_counter) 
    286 { 
    287         using namespace Gobby; 
    288         users.clear(); 
    289         tags.clear(); 
    290         line_counter = 1; 
    291         xmlpp::Element* last_node = content; 
    292         xmlpp::Element* line_no = last_node->add_child("span"); 
    293         line_no->set_attribute("class", "line_no"); 
    294         line_no->set_attribute("id", "line_1"); 
    295  
    296         GtkTextBuffer* buffer = GTK_TEXT_BUFFER(document.get_text_buffer()); 
    297         InfTextGtkBuffer* inf_buffer 
    298                 = INF_TEXT_GTK_BUFFER( 
    299                         inf_session_get_buffer(INF_SESSION(document.get_session()))); 
    300  
    301         GtkTextIter begin; 
    302         gtk_text_buffer_get_start_iter(buffer, &begin); 
    303         { 
    304                 GtkTextIter end; 
    305                 gtk_text_buffer_get_end_iter(buffer, &end); 
    306                 gtk_source_buffer_ensure_highlight( 
    307                         GTK_SOURCE_BUFFER(buffer), 
    308                         &begin, 
    309                         &end); 
    310         } 
    311  
    312         while(!gtk_text_iter_is_end(&begin)) 
    313         { 
    314                 GSList* current_tags = gtk_text_iter_get_tags(&begin); 
    315                 Glib::SListHandle<Glib::RefPtr<Gtk::TextTag> > handle( 
    316                         current_tags, Glib::OWNERSHIP_SHALLOW); 
    317                 Glib::ustring classes; 
    318                 for(GSList* tag = current_tags; tag != 0; tag = tag->next) 
    319                 { 
    320                         if(!classes.empty()) 
    321                                 classes += ' '; 
    322                         gchar* tag_name = g_strdup_printf( 
    323                                 "tag_%p", static_cast<void*>(tag->data)); 
    324                         classes += tag_name; 
    325                         g_free(tag_name); 
    326                         tags.insert(GTK_TEXT_TAG(tag->data)); 
    327                 } 
    328  
    329                 last_node = last_node->add_child("span"); 
    330                 if (!classes.empty()) 
    331                         last_node->set_attribute("class", classes); 
    332  
    333                 InfTextUser* new_user 
    334                         = inf_text_gtk_buffer_get_author(inf_buffer, &begin); 
    335                 if (new_user) { 
    336                         last_node->set_attribute( 
    337                                 "title", 
    338                                 uprintf(_("written by: %s"), 
    339                                         inf_user_get_name(INF_USER(new_user)))); 
    340                         users.insert(new_user); 
    341                 } 
    342  
    343                 GtkTextIter next = begin; 
    344                 gtk_text_iter_forward_to_tag_toggle(&next, 0); 
    345  
    346                 // split text by newlines so we can insert line number elements 
    347                 gchar* text = gtk_text_iter_get_text(&begin, &next); 
    348                 try 
    349                 { 
    350                         gchar const* last_pos = text; 
    351                         for (gchar const* i = last_pos; *i; ++i) { 
    352                                 if (*i != '\n') 
    353                                         continue; 
    354  
    355                                 ++line_counter; 
    356  
    357                                 gchar const* next_pos = i; 
    358                                 ++next_pos; 
    359                                 last_node->add_child_text(Glib::ustring(last_pos, next_pos)); 
    360                                 last_pos = next_pos; 
    361  
    362                                 // drop author <span/> for a moment for the line number <span/> 
    363                                 line_no = last_node->add_child("span"); 
    364                                 line_no->set_attribute("class", "line_no"); 
    365                                 line_no->set_attribute( 
    366                                   "id", 
    367                                   uprintf("line_%d", line_counter)); 
    368                         } 
    369  
    370                         last_node->add_child_text(Glib::ustring(last_pos)); 
    371                 } 
    372                 catch(...) 
    373                 { 
    374                         g_free(text); 
    375                         throw; 
    376                 } 
    377                 g_free(text); 
    378                 last_node = last_node->get_parent(); 
    379  
    380                 begin = next; 
    381         } 
    382 } 
    383  
    384 // some random interesting information/advertisement to be put at the end of 
    385 // the html output 
    386 void dump_info(xmlpp::Element* node, Gobby::DocWindow& document) { 
    387         using namespace Gobby; 
    388         // put current time 
    389         char const* time_str; 
    390         int const n = 128; 
    391         char buf[n]; 
    392   { 
    393     std::time_t now; 
    394           std::time(&now); 
    395           // TODO: localtime is not threadsafe, use boost.date_time!! 
    396                 if (std::strftime(buf, n, "%c", localtime(&now))) 
    397                         time_str = buf; 
    398                 else 
    399                         time_str = _("<unable to print date>"); 
    400         } 
    401  
    402         // put document metadata, like path, hostname of infinoted 
    403         // TODO: figure out what interesting info we can pull out of the session 
    404         char session_info[] = "<placeholder for session info and path>"; 
    405  
    406         // %1$s is information about the document's location, session name, 
    407         // %2$s is current date as formatted by %c, 
    408         // %3$s is a link to the gobby site 
    409         char const* translated = 
    410                 _("Document generated from %1$s at %2$s by %3$s"); 
    411         char const* p = std::strstr(translated, "%3$s"); 
    412         g_assert(p); 
    413         node->add_child_text( 
    414                 uprintf(Glib::ustring(translated, p).c_str(), session_info, time_str)); 
    415  
    416         xmlpp::Element* link = node->add_child("a"); 
    417         link->set_attribute("href", "http://gobby.0x539.de/"); 
    418         link->add_child_text(PACKAGE_STRING); 
    419  
    420         if (*p != '\0') 
    421                 node->add_child_text( 
    422                   uprintf(p+4 , session_info, time_str)); 
    423 } 
    424  
    425 // list each author before the actual text 
    426 void dump_user_list(xmlpp::Element* list, 
    427                     const std::set<InfTextUser*>& users) { 
    428         for(std::set<InfTextUser*>::const_iterator i = users.begin(); 
    429             i != users.end(); 
    430             ++i) 
    431         { 
    432                 gdouble hue = inf_text_user_get_hue(*i); 
    433                 hue = std::fmod(hue, 1); 
    434  
    435                 Gdk::Color c; 
    436                 c.set_hsv(360.0 * hue, 0.35, 1.0); 
    437                 gchar const* name = inf_user_get_name(INF_USER(*i)); 
    438                 const unsigned int rgb = color_to_rgb(c.gobj()); 
    439  
    440                 xmlpp::Element* item = list->add_child("li"); 
    441                 item->add_child_text(name); 
    442                 item->set_attribute( 
    443                         "style", 
    444                         uprintf("background-color: #%06x;\n", rgb)); 
    445         } 
    446 } 
    447  
    448 void dump_tags_style(xmlpp::Element* css, 
    449                      const priority_tag_set& tags) 
    450 { 
    451         for(priority_tag_set::const_iterator i = tags.begin(); i != tags.end(); ++i) 
    452         { 
    453                 GdkColor* fg, * bg; 
    454                 gint weight; 
    455                 gboolean underline; 
    456                 PangoStyle style; 
    457                 gboolean fg_set, bg_set, weight_set, underline_set, style_set; 
    458                 g_object_get(G_OBJECT(*i), 
    459                         "background-gdk", &bg, 
    460                         "foreground-gdk", &fg, 
    461                         "weight",         &weight, 
    462                         "underline",      &underline, 
    463                         "style",          &style, 
    464                         "background-set", &bg_set, 
    465                         "foreground-set", &fg_set, 
    466                         "weight-set",     &weight_set, 
    467                         "underline-set",  &underline_set, 
    468                         "style-set",      &style_set, 
    469                         NULL); 
    470                 const unsigned int bg_rgb = color_to_rgb(bg); 
    471                 const unsigned int fg_rgb = color_to_rgb(fg); 
    472                 gdk_color_free(fg); 
    473                 gdk_color_free(bg); 
    474                 css->add_child_text( 
    475                         uprintf(".tag_%p {\n", static_cast<void*>(*i))); 
    476                 if(fg_set) 
    477                         css->add_child_text(uprintf( 
    478                                 "  color:                  #%06x;\n", 
    479                                 fg_rgb)); 
    480                 if(bg_set) 
    481                         css->add_child_text(uprintf( 
    482                                 "  background-color:       #%06x;\n", 
    483                                 bg_rgb)); 
    484                 if(weight_set) 
    485                         css->add_child_text(uprintf( 
    486                                 "  font-weight:            %d;\n", 
    487                                 weight)); 
    488                 if(underline_set) 
    489                         css->add_child_text(uprintf( 
    490                                 "  text-decoration:        %s;\n", 
    491                                 underline ? "underline" : "none")); 
    492                 css->add_child_text("}\n"); 
    493         } 
    494 } 
    495  
    496 // generate xhtml representation of the document and write it to the 
    497 // specified location in the filesystem 
    498 void export_html(Gobby::DocWindow& document, const Glib::ustring& output_path) { 
    499         using namespace Gobby; 
    500         xmlpp::Document output; 
    501  
    502   output.set_internal_subset("html", 
    503     "-//W3C//DTD XHTML 1.1//EN", 
    504     "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"); 
    505  
    506         xmlpp::Element 
    507                 * root      = output.create_root_node("html", "http://www.w3.org/1999/xhtml"), 
    508                 * head      = root->add_child("head"), 
    509                 * body      = root->add_child("body"), 
    510                 * title     = head->add_child("title"), 
    511                 * style     = head->add_child("style"), 
    512                 * h1        = body->add_child("h1"), 
    513                 * h2        = body->add_child("h2"), 
    514                 * user_list = body->add_child("ul"), 
    515                 * content   = body->add_child("pre"), 
    516                 * info      = body->add_child("p"); 
    517  
    518         const Glib::ustring& document_name = document.get_title(); 
    519         title->add_child_text(document_name + " - infinote document"); 
    520  
    521         h1->add_child_text(document_name); 
    522  
    523         content->set_attribute("class", "document"); 
    524  
    525         std::set<InfTextUser*> users; 
    526         priority_tag_set tags; 
    527         unsigned int line_counter; 
    528         dump_buffer(document, content, users, tags, line_counter); 
    529  
    530         h2->add_child_text(_("Participants")); 
    531  
    532         info->set_attribute("class", "info"); 
    533   dump_info(info, document); 
    534  
    535         style->set_attribute("type", "text/css"); 
    536         dump_user_list(user_list, users); 
    537         dump_tags_style(style, tags); 
    538         if (!user_list->cobj()->children) { 
    539                 body->remove_child(h2); 
    540                 body->remove_child(user_list); 
    541         } 
    542  
    543         style->add_child_text( 
    544                         "h1 {\n" 
    545                         "  font-family:\n" 
    546                         "}\n" 
    547                         ".document {\n" 
    548                         "  border-top:             1px solid gray;\n" 
    549                         "  border-bottom:          1px solid black;\n" 
    550                         "  padding-bottom:         1.2em;\n" 
    551                         "  counter-reset:          line;\n" 
    552                         "}\n" 
    553                         ".line_no:before {\n" 
    554                         "  content:                counter(line);\n" 
    555                         "  counter-increment:      line;\n" 
    556                         "}\n" 
    557                         ".info {\n" 
    558                         "  font-size:              small;\n" 
    559                         "}\n"); 
    560  
    561         style->add_child_text( 
    562                 uprintf( 
    563                         ".line_no {\n" 
    564                         "  position:               absolute;\n" 
    565                         "  float:                  left;\n" 
    566                         "  clear:                  left;\n" 
    567                         "  margin-left:            -%1$uem;\n" 
    568                         "  color:                  gray;\n" 
    569                         "}\n" 
    570                         ".document {\n" 
    571                         "  padding-left:            %1$uem\n" 
    572                         "}\n", 
    573           static_cast<unsigned int>(std::log(line_counter)/std::log(10))+1)); 
    574  
    575         output.write_to_file(output_path, "utf-8"); 
    576 } 
    577  
    578224void Gobby::FileCommands::on_export_html() { 
    579225        DocWindow* document = m_folder.get_current_document(); 
     
    582228        // TODO: filechooser, possibly remember choice 
    583229        // what is that whole operation/task thing? 
    584         export_html(*document, "output.xhtml"); 
     230        Glib::ustring home = Glib::get_home_dir(); 
     231        m_operations.export_html(*document, "file://" + home + "/output.xhtml"); 
    585232} 
    586233 
  • code/operations/Makefile.am

    r1a96a2b re275a84  
    44        operations.cpp \ 
    55        operation-delete.cpp \ 
     6        operation-export-html.cpp \ 
    67        operation-new.cpp \ 
    78        operation-open.cpp \ 
     
    1213        operations.hpp \ 
    1314        operation-delete.hpp \ 
     15        operation-export-html.hpp \ 
    1416        operation-new.hpp \ 
    1517        operation-open.hpp \ 
  • code/operations/operations.cpp

    rfd2f104 re275a84  
    2222#include "operations/operation-save.hpp" 
    2323#include "operations/operation-delete.hpp" 
     24#include "operations/operation-export-html.hpp" 
    2425 
    2526#include "operations/operations.hpp" 
     
    129130} 
    130131 
     132Gobby::OperationExportHtml* 
     133Gobby::Operations::export_html(DocWindow& document, 
     134                               const std::string& uri) 
     135{ 
     136        OperationExportHtml* op = 
     137                new OperationExportHtml(*this, document, uri); 
     138        m_operations.insert(op); 
     139        return op; 
     140} 
     141 
    131142Gobby::OperationSave* 
    132143Gobby::Operations::get_save_operation_for_document(DocWindow& document) 
  • code/operations/operations.hpp

    rfd2f104 re275a84  
    3838class OperationSave; 
    3939class OperationDelete; 
     40class OperationExportHtml; 
    4041 
    4142class Operations: public sigc::trackable 
     
    117118                                     const InfcBrowserIter* iter); 
    118119 
     120        OperationExportHtml* export_html(DocWindow& document, 
     121                                         const std::string& uri); 
     122 
    119123        OperationSave* get_save_operation_for_document(DocWindow& window); 
    120124