From e4dea66a338824037b6c35b262aa8db4004b6581 Mon Sep 17 00:00:00 2001 From: Max Gaukler Date: Sun, 11 Jun 2017 12:04:56 +0200 Subject: [Bug #1417470] Fix PDF+TeX output for text inside groups with clip/mask Fixes Bug: - https://launchpad.net/bugs/1417470 --- src/extension/internal/cairo-render-context.cpp | 35 ++++++++++++++++++++++--- src/extension/internal/cairo-render-context.h | 1 + 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp index 21902db7d..c513744a8 100644 --- a/src/extension/internal/cairo-render-context.cpp +++ b/src/extension/internal/cairo-render-context.cpp @@ -128,7 +128,8 @@ CairoRenderContext::CairoRenderContext(CairoRenderer *parent) : _renderer(parent), _render_mode(RENDER_MODE_NORMAL), _clip_mode(CLIP_MODE_MASK), - _omittext_state(EMPTY) + _omittext_state(EMPTY), + _omittext_missing_pages(0) { } @@ -884,6 +885,13 @@ CairoRenderContext::finish(void) if (_vector_based_target) cairo_show_page(_cr); + // PDF+TeX Output, see CairoRenderContext::_prepareRenderGraphic() + while (_omittext_missing_pages > 0) { + _omittext_missing_pages--; + g_warning("PDF+TeX output: issuing blank PDF page at end (workaround for previous error)"); + cairo_show_page(_cr); + } + cairo_destroy(_cr); cairo_surface_finish(_surface); cairo_status_t status = cairo_surface_status(_surface); @@ -1435,8 +1443,29 @@ CairoRenderContext::_prepareRenderGraphic() // Only PDFLaTeX supports importing a single page of a graphics file, // so only PDF backend gets interleaved text/graphics if (_is_omittext && _target == CAIRO_SURFACE_TYPE_PDF) { - if (_omittext_state == NEW_PAGE_ON_GRAPHIC) - cairo_show_page(_cr); + if (_omittext_state == NEW_PAGE_ON_GRAPHIC) { + if (cairo_get_group_target(_cr) != cairo_get_target(_cr)) { + // we are in the middle of a group, i. e., between cairo_push_group() and cairo_pop_group(). + // cairo_show_page() has no effect here! + // To ensure that the the generated TeX source doesn't try to include non-existing pages, + // we will later output an extra blank page. + // This is a workaround for bug #1417470. + g_warning("PDF+TeX output: Found text inside a clipped/masked group. This is not supported, the Z-order will be incorrect. Blank pages will be added to the PDF output to work around bug #1417470."); + _omittext_missing_pages++; + } else { + // no group is active, create new page + cairo_show_page(_cr); + // Output missing pages (workaround for the 'if' case above). + // With this solution, the Z-order is more wrong than necessary. + // It would be better to print the blank pages first, and then the actual current page. + // However, this isn't easily possible with cairo. + while (_omittext_missing_pages > 0) { + _omittext_missing_pages--; + g_warning("PDF+TeX output: issuing blank PDF page (workaround for previous error)"); + cairo_show_page(_cr); + } + } + } _omittext_state = GRAPHIC_ON_TOP; } } diff --git a/src/extension/internal/cairo-render-context.h b/src/extension/internal/cairo-render-context.h index dfa6084d1..9b976fd6d 100644 --- a/src/extension/internal/cairo-render-context.h +++ b/src/extension/internal/cairo-render-context.h @@ -207,6 +207,7 @@ protected: CairoClipMode _clip_mode; CairoOmitTextPageState _omittext_state; + int _omittext_missing_pages; cairo_pattern_t *_createPatternForPaintServer(SPPaintServer const *const paintserver, Geom::OptRect const &pbox, float alpha); -- cgit v1.2.3 From 6ef8265ccdc27bcfd78bed3357580a24cdd98d37 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Wed, 14 Jun 2017 11:23:35 +0200 Subject: Use Glib::ustring to simplify title string code. --- src/widgets/desktop-widget.cpp | 94 ++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index d8c27f5b7..ce4c5936c 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -66,6 +66,7 @@ #include #include #include +#include "inkscape-version.h" using Inkscape::UI::Widget::UnitTracker; using Inkscape::UI::UXManager; @@ -761,69 +762,52 @@ SPDesktopWidget::updateTitle(gchar const* uri) Gtk::Window *window = static_cast(g_object_get_data(G_OBJECT(this), "window")); if (window) { - gchar const *fname = uri; - GString *name = g_string_new (""); - - gchar const *grayscalename = N_("grayscale"); - gchar const *grayscalenamecomma = N_(", grayscale"); - gchar const *printcolorsname = N_("print colors preview"); - gchar const *printcolorsnamecomma = N_(", print colors preview"); - gchar const *outlinename = N_("outline"); - gchar const *nofiltersname = N_("no filters"); - gchar const *colormodename = NULL; - gchar const *colormodenamecomma = NULL; - gchar const *rendermodename = NULL; - gchar const *modifiedname = ""; + SPDocument *doc = this->desktop->doc(); + + std::string Name; if (doc->isModifiedSinceSave()) { - modifiedname = "*"; + Name += "*"; } - if (this->desktop->getColorMode() == Inkscape::COLORMODE_GRAYSCALE) { - colormodename = grayscalename; - colormodenamecomma = grayscalenamecomma; - } else if (this->desktop->getColorMode() == Inkscape::COLORMODE_PRINT_COLORS_PREVIEW) { - colormodename = printcolorsname; - colormodenamecomma = printcolorsnamecomma; + Name += uri; + + if (desktop->number > 1) { + Name += ": "; + Name += std::to_string(desktop->number); } - if (this->desktop->getMode() == Inkscape::RENDERMODE_OUTLINE) { - rendermodename = outlinename; - } else if (this->desktop->getMode() == Inkscape::RENDERMODE_NO_FILTERS) { - rendermodename = nofiltersname; + Name += " ("; + + if (desktop->getMode() == Inkscape::RENDERMODE_OUTLINE) { + Name += N_("outline"); + } else if (desktop->getMode() == Inkscape::RENDERMODE_NO_FILTERS) { + Name += N_("no filters"); } - - - if (this->desktop->number > 1) { - if (rendermodename) { - if (colormodenamecomma) { - g_string_printf (name, _("%s%s: %d (%s%s) - Inkscape"), modifiedname, fname, this->desktop->number, _(rendermodename), _(colormodenamecomma)); - } else { - g_string_printf (name, _("%s%s: %d (%s) - Inkscape"), modifiedname, fname, this->desktop->number, _(rendermodename)); - } - } else { - if (colormodename) { - g_string_printf (name, _("%s%s: %d (%s) - Inkscape"), modifiedname, fname, this->desktop->number, _(colormodename)); - } else { - g_string_printf (name, _("%s%s: %d - Inkscape"), modifiedname, fname, this->desktop->number); - } - } + + if (desktop->getColorMode() != Inkscape::COLORMODE_NORMAL && + desktop->getMode() != Inkscape::RENDERMODE_NORMAL) { + Name += ", "; + } + + if (desktop->getColorMode() == Inkscape::COLORMODE_GRAYSCALE) { + Name += N_("grayscale"); + } else if (desktop->getColorMode() == Inkscape::COLORMODE_PRINT_COLORS_PREVIEW) { + Name += N_("print colors preview"); + } + + if (*Name.rbegin() == '(') { // Can not use C++11 .back() or .pop_back() with ustring! + Name.erase(Name.size() - 2); } else { - if (rendermodename) { - if (colormodenamecomma) { - g_string_printf (name, _("%s%s (%s%s) - Inkscape"), modifiedname, fname, _(rendermodename), _(colormodenamecomma)); - } else { - g_string_printf (name, _("%s%s (%s) - Inkscape"), modifiedname, fname, _(rendermodename)); - } - } else { - if (colormodename) { - g_string_printf (name, _("%s%s (%s) - Inkscape"), modifiedname, fname, _(colormodename)); - } else { - g_string_printf (name, _("%s%s - Inkscape"), modifiedname, fname); - } - } + Name += ")"; } - window->set_title (name->str); - g_string_free (name, TRUE); + + Name += " - Inkscape"; + + // Name += " ("; + // Name += Inkscape::version_string; + // Name += ")"; + + window->set_title (Name); } } -- cgit v1.2.3 From e07479cf0a1bf8262a5c1b682774f054d32448ea Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Wed, 14 Jun 2017 13:14:55 +0200 Subject: Prevent crash due to clones. --- src/ui/dialog/styledialog.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/ui/dialog/styledialog.cpp b/src/ui/dialog/styledialog.cpp index b1bcddd45..5a1ebc532 100644 --- a/src/ui/dialog/styledialog.cpp +++ b/src/ui/dialog/styledialog.cpp @@ -407,6 +407,7 @@ void StyleDialog::_readStyleElement() // Add as children, objects that match selector. for (auto& obj: objVec) { + if (obj->cloned) continue; // Skip cloned objects (they also don't have 'id'). Gtk::TreeModel::Row childrow = *(_store->append(row->children())); childrow[_mColumns._colSelector] = "#" + Glib::ustring(obj->getId()); childrow[_mColumns._colIsSelector] = false; -- cgit v1.2.3 From 73fcfc9f34395148fceda89b892886291b509cb2 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Wed, 14 Jun 2017 14:33:02 +0200 Subject: Update dialog when object is added, deleted, or changed. --- src/ui/dialog/styledialog.cpp | 137 ++++++++++++++++++++++++++++++++++++++++++ src/ui/dialog/styledialog.h | 12 ++++ 2 files changed, 149 insertions(+) (limited to 'src') diff --git a/src/ui/dialog/styledialog.cpp b/src/ui/dialog/styledialog.cpp index 5a1ebc532..8679659ce 100644 --- a/src/ui/dialog/styledialog.cpp +++ b/src/ui/dialog/styledialog.cpp @@ -45,6 +45,7 @@ namespace Inkscape { namespace UI { namespace Dialog { +// Keeps a watch on style element class StyleDialog::NodeObserver : public Inkscape::XML::NodeObserver { public: NodeObserver(StyleDialog* styleDialog) : @@ -78,6 +79,93 @@ StyleDialog::NodeObserver::notifyContentChanged( } +// Keeps a watch for new/removed/changed nodes +// (Must update objects that selectors match.) +class StyleDialog::NodeWatcher : public Inkscape::XML::NodeObserver { +public: + NodeWatcher(StyleDialog* styleDialog, Inkscape::XML::Node *repr) : + _styleDialog(styleDialog), + _repr(repr) + { +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::NodeWatcher: Constructor" << std::endl; +#endif + }; + + virtual void notifyChildAdded( Inkscape::XML::Node &/*node*/, + Inkscape::XML::Node &child, + Inkscape::XML::Node */*prev*/ ) + { + if ( _styleDialog && _repr ) { + _styleDialog->_nodeAdded( child ); + } + } + + virtual void notifyChildRemoved( Inkscape::XML::Node &/*node*/, + Inkscape::XML::Node &child, + Inkscape::XML::Node */*prev*/ ) + { + if ( _styleDialog && _repr ) { + _styleDialog->_nodeRemoved( child ); + } + } + + virtual void notifyAttributeChanged( Inkscape::XML::Node &node, + GQuark qname, + Util::ptr_shared /*old_value*/, + Util::ptr_shared /*new_value*/ ) { + if ( _styleDialog && _repr ) { + + // For the moment only care about attributes that are directly used in selectors. + const gchar * cname = g_quark_to_string (qname ); + Glib::ustring name; + if (cname) { + name = cname; + } + + if ( name == "id" || name == "class" ) { + _styleDialog->_nodeChanged( node ); + } + } + } + + StyleDialog * _styleDialog; + Inkscape::XML::Node * _repr; // Need to track if document changes. +}; + +void +StyleDialog::_nodeAdded( Inkscape::XML::Node &node ) { + + StyleDialog::NodeWatcher *w = new StyleDialog::NodeWatcher (this, &node); + node.addObserver (*w); + _nodeWatchers.push_back(w); + + _readStyleElement(); + _selectRow(); +} + +void +StyleDialog::_nodeRemoved( Inkscape::XML::Node &repr ) { + + for (auto it = _nodeWatchers.begin(); it != _nodeWatchers.end(); ++it) { + if ( (*it)->_repr == &repr ) { + (*it)->_repr->removeObserver (**it); + _nodeWatchers.erase( it ); + break; + } + } + + _readStyleElement(); + _selectRow(); +} + +void +StyleDialog::_nodeChanged( Inkscape::XML::Node &object ) { + + _readStyleElement(); + _selectRow(); +} + StyleDialog::TreeStore::TreeStore() { } @@ -248,6 +336,9 @@ StyleDialog::StyleDialog() : _selection_changed_connection = getDesktop()->getSelection()->connectChanged( sigc::hide(sigc::mem_fun(this, &StyleDialog::_handleSelectionChanged))); + // Add watchers + _updateWatchers(); + // Load tree _readStyleElement(); _selectRow(); @@ -447,6 +538,49 @@ void StyleDialog::_writeStyleElement() } +void StyleDialog::_addWatcherRecursive(Inkscape::XML::Node *node) { + +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_addWatcherRecursive()" << std::endl; +#endif + + StyleDialog::NodeWatcher *w = new StyleDialog::NodeWatcher(this, node); + node->addObserver(*w); + _nodeWatchers.push_back(w); + + for (unsigned i = 0; i < node->childCount(); ++i) { + _addWatcherRecursive(node->nthChild(i)); + } +} + +/** + * @brief StyleDialog::_updateWatchers + * Update the watchers on objects. + */ +void StyleDialog::_updateWatchers() +{ + _updating = true; + + // Remove old document watchers + while (!_nodeWatchers.empty()) { + StyleDialog::NodeWatcher *w = _nodeWatchers.back(); + w->_repr->removeObserver(*w); + _nodeWatchers.pop_back(); + delete w; + } + + // Recursively add new watchers + Inkscape::XML::Node *root = SP_ACTIVE_DOCUMENT->getReprRoot(); + _addWatcherRecursive(root); + +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_updateWatchers(): " << _nodeWatchers.size() << std::endl; +#endif + + _updating = false; +} + + /** * @brief StyleDialog::_addToSelector * @param row @@ -712,6 +846,7 @@ void StyleDialog::_selectObjects(int eventX, int eventY) Gtk::TreeModel::Row row = *iter; Gtk::TreeModel::Children children = row.children(); std::vector objVec = row[_mColumns._colObj]; + for (unsigned i = 0; i < objVec.size(); ++i) { SPObject *obj = objVec[i]; getDesktop()->selection->add(obj); @@ -1047,6 +1182,7 @@ StyleDialog::_handleDocumentReplaced(SPDesktop *desktop, SPDocument * /* documen _selection_changed_connection = desktop->getSelection()->connectChanged( sigc::hide(sigc::mem_fun(this, &StyleDialog::_handleSelectionChanged))); + _updateWatchers(); _readStyleElement(); _selectRow(); } @@ -1077,6 +1213,7 @@ StyleDialog::_handleDesktopChanged(SPDesktop* desktop) { _document_replaced_connection = desktop->connectDocumentReplaced( sigc::mem_fun(this, &StyleDialog::_handleDocumentReplaced)); + _updateWatchers(); _readStyleElement(); _selectRow(); } diff --git a/src/ui/dialog/styledialog.h b/src/ui/dialog/styledialog.h index e84489e66..dbbc1e480 100644 --- a/src/ui/dialog/styledialog.h +++ b/src/ui/dialog/styledialog.h @@ -58,6 +58,14 @@ private: // Monitor