/// // Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING /// #include "pptout/widget/usererror.h" /// \todo fix this #include #include #include #include #include #include "document.h" #include "fileerrors.h" #include "page.h" #include "defines.h" // VERSION #include "textstream.h" #include "xml2ps/psstream.hh" #include "util/stringutil.h" #include "util/filesys.h" #include "util/warning.h" #include "util/os.h" #include "fonts/fontmanager.hh" #include "ps/pfb2pfa.h" #include "ps/type42.h" #include "ps/pdf.h" sigc::signal Document::changed_signal; sigc::signal Document::selection_changed_signal; sigc::signal Document::streams_changed_signal; namespace { // local class NameOrder { public: bool operator() (const TextStream* x, const TextStream* y) const { return x && y && (x->get_name() < y->get_name()); } }; } DocRef Document::create() { DocRef tmp(new Document()); tmp->reference(); return tmp; } // It is very important not to emit signals from the constructors. Document::Document() : first_page_num(1), stream_num(1), doublesided(true), is_template(false), paper_name("A4"), orientation(Papers::PORTRAIT), the_template(0) { pages.push_back(new Page(*this)); } DocRef Document::create(const std::string &filename, bool is_template) { DocRef tmp(new Document(filename, is_template)); tmp->reference(); return tmp; } Document::Document(const std::string &filename, bool is_template_) : stream_num(1), is_template(is_template_), the_template(0) { open(expand_path(filename)); } DocRef Document::create(const std::string &template_file_) { DocRef tmp(new Document(template_file_)); tmp->reference(); return tmp; } Document::Document(const std::string &template_file_) : first_page_num(1), stream_num(1), is_template(false), the_template(0) { set_template(template_file_); } Document::~Document() { debug << "destroying Document" << std::endl; // delete the streams before the pages, or the textframes will try // to run the typesetter threads from their destructors while(!text_streams.empty()) { TextStream *text_stream = *text_streams.begin(); text_streams.pop_front(); delete text_stream; } // delete pages while(!pages.empty()) { Page *page = *pages.begin(); pages.pop_front(); delete page; } } std::string Document::make_up_new_name() { while(stream_num < INT_MAX) { std::string tmp = "stream" + tostr(stream_num); if(!get_text_stream(tmp)) return tmp; // Up the number only afterwards if no match, so if the user creates a // stream, renames it, and creates another one, he doesn't lose numbers. ++stream_num; } /// \todo Try again from 1, in case a stream was deleted or renamed, but /// only once, so we don't get stuck in an eternal loop. Don't start from 1 /// each time, to avoid confusing the user throw std::runtime_error("Out of automatical stream names"); } int Document::count_selected() const { return selection.size(); } const Document::Selection& Document::selected() const { return selection; } void Document::select_all(bool select) { selection.clear(); if(select) for(PageVec::const_iterator p = pages.begin(); p != pages.end(); ++p) for(Group::ChildVec::const_iterator i = (*p)->pbegin(); i != (*p)->pend(); ++i) selection.push_back(*i); selection_changed_signal(self()); } void Document::select_all_on_page(Page *page, bool select) { if(!page) return; selection.clear(); if(select) for(Group::ChildVec::const_iterator i = page->pbegin(); i != page->pend(); ++i) selection.push_back(*i); selection_changed_signal(self()); } void Document::deselect(Pagent* obj) { Selection::iterator i = find(selection.begin(), selection.end(), obj); if(i != selection.end()) { selection.erase(i); selection_changed_signal(self()); } } void Document::select(Pagent* obj, bool deselect_old) { if(deselect_old) selection.clear(); selection.push_back(obj); selection_changed_signal(self()); } void Document::delete_selected() { for(Selection::const_iterator i = selection.begin(); i != selection.end(); ++i) { Pagent* obj = (*i)->get_parent().ungroup(*i); if(!obj) throw std::logic_error("Tried to remove pagent from bad parent."); delete obj; } selection.clear(); selection_changed_signal(self()); } Document::StreamVec Document::get_text_streams() { StreamVec tmp = text_streams; tmp.sort(NameOrder()); return tmp; } void Document::rename_text_stream(const std::string &old_name, const std::string &new_name) { TextStream *stream = get_text_stream(old_name); if(!stream) throw Error::TextStreamName("A stream with the name \"" + old_name + "\" does not exist."); if(old_name == new_name) return; //not an error if(new_name.empty()) throw Error::TextStreamName("The stream must have a name"); if(get_text_stream(new_name)) throw Error::TextStreamName("A stream with the name \"" + new_name + "\" already exists"); stream->set_name(new_name); streams_changed_signal(self()); } void Document::add_text_stream(TextStream* new_stream) { _add_text_stream(new_stream); streams_changed_signal(self()); } void Document::_add_text_stream(TextStream* new_stream) { const std::string& name = new_stream->get_name(); if(name.empty()) throw Error::TextStreamName("The stream must have a name"); if(get_text_stream(name)) throw Error::TextStreamName("A stream with the name \"" + name + "\" already exists"); text_streams.push_back(new_stream); } namespace { template struct NameIs { std::string _name; NameIs(const std::string &name) : _name(name) {} bool operator() (A *a) { return a->get_name() == _name; } }; } TextStream* Document::get_text_stream(const std::string &name) { StreamVec::iterator i = std::find_if(text_streams.begin(), text_streams.end(), NameIs(name)); return i != text_streams.end() ? *i : 0; } void Document::remove_text_stream(const std::string &name) { StreamVec::iterator i = std::find_if(text_streams.begin(), text_streams.end(), NameIs(name)); if(i != text_streams.end()) { delete *i; text_streams.erase(i); streams_changed_signal(self()); } } unsigned int Document::get_num_of_pages() const { return pages.size(); } void Document::delete_page(int page_num) { int num_of_pages = get_num_of_pages(); if(page_num >= first_page_num && page_num < first_page_num + num_of_pages) { int j = first_page_num; PageVec::iterator i = pages.begin(); while(j < page_num && j < first_page_num + num_of_pages) { i++; j++; } delete *i; pages.erase(i); } else throw Error::InvalidPageNum(page_num); /// \todo temporary fix to make sure deleted objects /// are not still selected: select_all(false); changed_signal(self()); } Page *Document::new_page(int page_num, Page *original) { std::auto_ptr the_new_page; if(original) { xmlpp::Document tmpdoc; tmpdoc.create_root_node("template"); FileContext context; the_new_page.reset (new Page(ElementWrap("no-file", *original->save(*tmpdoc.get_root_node(), context)), *this)); the_new_page->set_name(""); // the name should not be inherited } else { the_new_page.reset(new Page(*this)); } Page *result = the_new_page.get(); int num_of_pages = get_num_of_pages(); if(num_of_pages == 0) { pages.push_front(the_new_page.release()); } else if(page_num >= first_page_num && page_num <= first_page_num + num_of_pages) { int j = first_page_num; PageVec::iterator i = pages.begin(); while(j < page_num) { i++; j++; } pages.insert(i, the_new_page.release()); } else { throw Error::InvalidPageNum(page_num); } changed_signal(self()); return result; } int Document::get_page_num_of_page(const Page *page) const { int j = get_first_page_num(); for(PageVec::const_iterator i = pages.begin(); i != pages.end(); i++, j++) { if(page == *i) return j; } throw Error::InvalidPageNum(); } Page *Document::get_page(int page_num) { int j = first_page_num; PageVec::iterator i = pages.begin(); int num_of_pages = get_num_of_pages(); if(num_of_pages == 0) return 0; while(j < page_num && j < first_page_num + num_of_pages - 1) { i++; j++; } return *i; } Page *Document::get_page(const std::string &page_name) { PageVec::iterator i = std::find_if(pages.begin(), pages.end(), NameIs(page_name)); return i != pages.end() ? *i : 0; } std::list Document::get_template_pages() { std::list tmp; if(the_template) { DocRef &t = the_template; for(int i = t->get_first_page_num(); i < t->get_first_page_num() + int(t->get_num_of_pages()); i++) tmp.push_back(t->get_page(i)->get_name()); } return tmp; } namespace { class OpenFailed : public UserError { public: OpenFailed(const std::string& filename, const std::exception& err) : UserError("Failed to open passepartout file " + filename, err) {} }; } void Document::open(const std::string &filename) { try { xmlpp::DomParser tree(filename); /// \todo Check if get_document is guaranteed to not return 0. xmlpp::Element *rootnode = tree.get_document()->get_root_node(); if(rootnode) xml_open(ElementWrap(filename, *rootnode)); else throw std::runtime_error("No such file or no rootnode"); } catch(const xmlpp::exception& e) { throw OpenFailed(filename, e); /// \todo Signify the "bad xml" level? } catch(const std::exception& e) { throw OpenFailed(filename, e); } } void Document::save(const std::string &filename) { try { std::auto_ptr tree(xml_save(FileContext(filename))); tree->write_to_file_formatted(filename); } catch (const xmlpp::exception& err) { throw UserError("Failed to write " + filename, err); } } DocRef Document::self() { DocRef tmp(this); tmp->reference(); return tmp; } void Document::xml_open(const ElementWrap& xml) { /// \todo this function is too long and unreadable std::string temp_template = xml.get_filename("template"); if(xml.get_element_name() != "document") throw Error::Read("Root node is not "); //default values if attribute is not encountered: doublesided = true; orientation = Papers::PORTRAIT; first_page_num = 1; paper_name = "A4"; //read document attributes: xmlpp::Element::AttributeList attributes = xml.element().get_attributes(); for(xmlpp::Element::AttributeList::iterator i = attributes.begin(); i != attributes.end(); i++) { const std::string name = (*i)->get_name(); if(name == "template"); // handled elsewhere else if(name == "doublesided") doublesided = to((*i)->get_value()); else if(name == "landscape") { if(to((*i)->get_value())) orientation = Papers::LANDSCAPE; } else if(name == "paper_name") { /// \todo Use a proper type that can be converted for the paper type. try { paper_name = (*i)->get_value(); } catch (Error::PaperName e) { throw Error::Read("There is no paper called \"" + e.name + "\"."); } } else if(name == "first_page_num") { first_page_num = to((*i)->get_value()); } else warning << "Unknown attribute \"" << (*i)->get_name() << "\" ignored in ." << std::endl; } if(!is_template) // templates can't have templates set_template(temp_template); // the template overrides anything explicitly stated in the document // read text streams and pages: xmlpp::Element::NodeList children = xml.element().get_children(); for(xmlpp::Node::NodeList::iterator i = children.begin(); i != children.end(); i++) { if(xmlpp::Element *elem = dynamic_cast(*i)) { std::string name = elem->get_name(); if(name == "text_stream") { std::auto_ptr stream (new TextStream(ElementWrap(xml, *elem))); const std::string &name = stream->get_name(); if(name.empty()) throw Error::Read("Text stream has no name."); // template streams override document streams // if two streams have the same name, the second will be ignored if(!get_text_stream(name)) _add_text_stream(stream.release()); // no signal } else if(name == "page") { pages.push_back(new Page(ElementWrap(xml, *elem), *this)); } else warning << "Unknown node <" << name << "> ignored in " << std::endl; } } } void Document::set_template(const std::string &template_file_) { template_file = template_file_; if(!template_file.empty()) { the_template = create(template_file, true); doublesided = the_template->is_doublesided(); orientation = the_template->get_orientation(); paper_name = the_template->get_paper_name(); StreamVec ts = the_template->get_text_streams(); for(StreamVec::iterator i = ts.begin(); i != ts.end(); i++) { try { TextStream* tmp = get_text_stream((*i)->get_name()); // don't emit signal in case this is called from the // constructor if(!tmp) _add_text_stream(new TextStream((*i)->get_name(), (*i)->get_association(), (*i)->get_transform())); else { // override tmp->set_association((*i)->get_association()); tmp->set_transform((*i)->get_transform()); } } catch (Error::TextStreamName e) { warning << e.what() << std::endl; } } } } void Document::set_doublesided(bool ds) { doublesided = ds; changed_signal(self()); } void Document::set_first_page_num(int num) { first_page_num = num; changed_signal(self()); } void Document::set_orientation(Papers::Orientation _orientation) { orientation = _orientation; changed_signal(self()); size_changed_signal(); } void Document::set_paper_name(const std::string &_paper_name) { if(papers.sizes.find(_paper_name) == papers.sizes.end()) throw Error::PaperName(_paper_name); paper_name = _paper_name; changed_signal(self()); size_changed_signal(); } xmlpp::Document *Document::xml_save(const FileContext &context) { xmlpp::Document *tree = new xmlpp::Document(); xmlpp::Element *root = tree->create_root_node("document"); root->set_attribute("paper_name", get_paper_name()); if(the_template) root->set_attribute("template", context.to(template_file)); root->set_attribute("doublesided", tostr(is_doublesided())); root->set_attribute("landscape", tostr(get_orientation() == Papers::LANDSCAPE)); root->set_attribute("first_page_num", tostr(get_first_page_num())); for(StreamVec::iterator i = text_streams.begin(); i != text_streams.end(); i++) (*i)->save(*root, context); for(PageVec::iterator i = pages.begin(); i != pages.end(); i++) (*i)->save(*root, context); return tree; } void Document::print(std::ostream& out, int first_page, int last_page, bool eps, bool include_fonts, bool grayscale) const { const font::FontManager &fm = font::FontManager::instance(); // merge required fonts from all streams font::Fonts used_fonts; for(StreamVec::const_iterator j = text_streams.begin(); j != text_streams.end(); j++) { const font::Fonts &fonts = (*j)->get_used_fonts(); for(font::Fonts::const_iterator i = fonts.begin(); i != fonts.end(); i++) { used_fonts.insert(*i); } } using std::endl; time_t the_time = std::time(0); if(!(first_page >= first_page_num && first_page <= last_page && last_page <= first_page_num + int(get_num_of_pages()) - 1)) throw Error::Print("Bad page interval"); // ignore request to print multible-page EPS eps = eps && first_page == last_page; int w = int(get_width() + 0.5); int h = int(get_height() + 0.5); if(eps) { out << "%!PS-Adobe-3.0 EPSF-3.0\n" << "%%BoundingBox: 0 0 " << w << " " << h << '\n'; } else { out << "%!PS-Adobe-3.0\n"; } //out << "%%DocumentData: Clean8Bit" << endl // <<"%%LanguageLevel: 2"<" << " (" << os::machine() << ", " << os::sysname() << " " << os::release() << ")\n"; out << "%%EndComments\n\n" << "%%BeginProlog\n"; xml2ps::PsStream::psProlog(out); out << "%%EndProlog\n\n" << "%%BeginSetup" << endl; // Resource comments int line = 0; for(font::Fonts::const_iterator i = used_fonts.begin(); i != used_fonts.end(); i++) { out << (line++ ? "%%+ " : (include_fonts ? "%%DocumentSuppliedResources: " : "%%DocumentNeededResources: ")) << "font " << fm.unalias(*i) << std::endl; } // %%IncludeResource comments if(!include_fonts) { for(font::Fonts::const_iterator i = used_fonts.begin(); i != used_fonts.end(); i++) { out << "%%IncludeResource: font " << fm.unalias(*i) << std::endl; } } else { // include fonts for(font::Fonts::const_iterator i = used_fonts.begin(); i != used_fonts.end(); i++) { std::string fontfile = fm.getFontFile(*i); if(fontfile.empty()) { warning << "Couldn't find font file for " << *i << std::endl; continue; } std::ifstream in(fontfile.c_str()); if(!in) { warning << "Couldn't read " << fontfile << std::endl; continue; } out << "%%BeginResource: font " << fm.unalias(*i) << std::endl; std::string ext = suffix(fontfile); if(ext == "pfa") { // ascii out << in.rdbuf(); } else if(ext == "pfb") { try { PS::pfb2pfa(in, out); } catch(const std::runtime_error &e) { warning << "error in " << fontfile << " : " << e.what() << std::endl; } } else if(ext == "ttf") { PS::truetype2type42(fontfile, out); } else { out << "% bad fontfile: " << fontfile << std::endl; warning << "unknown font format \"" << ext << "\" in " << fontfile << std::endl; } out << "%%EndResource" << std::endl; } } out << "%%EndSetup" << endl << endl; int page_num = first_page_num; for(PageVec::const_iterator i = pages.begin(); i != pages.end(); i++) { if(page_num >= first_page && page_num <= last_page) { out << endl << "%%Page: \"" <<(*i)->get_name()<<"\" "<print(out, grayscale); if(eps) out << "\ngrestore\n"; } page_num++; } out << "%%EOF" << endl; } void Document::print_pdf(PDF::Document::Ptr pdf, int first_page, int last_page) { for(StreamVec::iterator i = text_streams.begin(); i != text_streams.end(); i++) (*i)->print_pdf(pdf); int page_num = first_page_num; for(PageVec::const_iterator i = pages.begin(); i != pages.end(); ++i, ++page_num) { if(page_num >= first_page && page_num <= last_page) { (*i)->print_pdf(pdf); } } } Document& Document::containing(Pagent& obj) { try { Page& page = Page::containing(obj); return page.document; } catch(const Error::NoParent& err) { throw std::logic_error ("Tried to get Document containing pagent that was not in a Document."); } } const Document& Document::containing(const Pagent& obj) { try { const Page& page = Page::containing(obj); return page.document; } catch(const Error::NoParent& err) { throw std::logic_error ("Tried to get Document containing pagent that was not in a Document."); } }