summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLiam P. White <inkscapebronyat-signgmaildotcom>2014-06-06 16:12:41 +0000
committerLiam P. White <inkscapebronyat-signgmaildotcom>2014-06-06 16:12:41 +0000
commitfa06e2deb21ba6d362ebd7d34b0ae59a614b5e3c (patch)
tree84bb8f6071e403525d68a04c6589eb633176e774 /src
parentUpdate to trunk (r13379) (diff)
parentupdate clang format spec for latest clangformat version (diff)
downloadinkscape-fa06e2deb21ba6d362ebd7d34b0ae59a614b5e3c.tar.gz
inkscape-fa06e2deb21ba6d362ebd7d34b0ae59a614b5e3c.zip
Update to experimental r13389
(bzr r13090.1.81)
Diffstat (limited to 'src')
-rw-r--r--src/extension/internal/gdkpixbuf-input.cpp8
-rw-r--r--src/extension/internal/pdfinput/pdf-parser.cpp54
-rw-r--r--src/ink-comboboxentry-action.cpp28
-rw-r--r--src/libnrtype/font-lister.cpp81
-rw-r--r--src/live_effects/CMakeLists.txt3
-rw-r--r--src/live_effects/Makefile_insert4
-rw-r--r--src/live_effects/effect-enum.h1
-rw-r--r--src/live_effects/effect.cpp6
-rw-r--r--src/live_effects/lpe-envelope-perspective.cpp414
-rw-r--r--src/live_effects/lpe-envelope-perspective.h76
-rw-r--r--src/style-internal.cpp17
-rw-r--r--src/style.cpp52
-rw-r--r--src/style.h4
-rw-r--r--src/ui/dialog/spellcheck.cpp74
-rw-r--r--src/widgets/text-toolbar.cpp1
-rw-r--r--src/xml/repr-css.cpp20
16 files changed, 705 insertions, 138 deletions
diff --git a/src/extension/internal/gdkpixbuf-input.cpp b/src/extension/internal/gdkpixbuf-input.cpp
index a384c7bde..da179bee0 100644
--- a/src/extension/internal/gdkpixbuf-input.cpp
+++ b/src/extension/internal/gdkpixbuf-input.cpp
@@ -87,11 +87,11 @@ GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri)
ir = new ImageResolution(uri);
}
if (ir && ir->ok()) {
- xscale = 900.0 / floor(10.*ir->x() + .5); // round-off to 0.1 dpi
- yscale = 900.0 / floor(10.*ir->y() + .5);
+ xscale = 960.0 / floor(10.*ir->x() + .5); // round-off to 0.1 dpi
+ yscale = 960.0 / floor(10.*ir->y() + .5);
} else {
- xscale = 90.0 / defaultxdpi;
- yscale = 90.0 / defaultxdpi;
+ xscale = 96.0 / defaultxdpi;
+ yscale = 96.0 / defaultxdpi;
}
width *= xscale;
diff --git a/src/extension/internal/pdfinput/pdf-parser.cpp b/src/extension/internal/pdfinput/pdf-parser.cpp
index b398486e6..c5f03e5aa 100644
--- a/src/extension/internal/pdfinput/pdf-parser.cpp
+++ b/src/extension/internal/pdfinput/pdf-parser.cpp
@@ -866,7 +866,9 @@ void PdfParser::opSetExtGState(Object args[], int /*numArgs*/)
GBool isolated = gFalse;
GBool knockout = gFalse;
if (!obj4.dictLookup(const_cast<char*>("CS"), &obj5)->isNull()) {
-#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
+#if defined(POPPLER_EVEN_NEWER_COLOR_SPACE_API)
+ blendingColorSpace = GfxColorSpace::parse(&obj5, NULL, NULL);
+#elif defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
blendingColorSpace = GfxColorSpace::parse(&obj5, NULL);
#else
blendingColorSpace = GfxColorSpace::parse(&obj5);
@@ -1100,7 +1102,13 @@ void PdfParser::opSetFillColorSpace(Object args[], int /*numArgs*/)
res->lookupColorSpace(args[0].getName(), &obj);
GfxColorSpace *colorSpace = 0;
-#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
+#if defined(POPPLER_EVEN_NEWER_COLOR_SPACE_API)
+ if (obj.isNull()) {
+ colorSpace = GfxColorSpace::parse(&args[0], NULL, NULL);
+ } else {
+ colorSpace = GfxColorSpace::parse(&obj, NULL, NULL);
+ }
+#elif defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
if (obj.isNull()) {
colorSpace = GfxColorSpace::parse(&args[0], NULL);
} else {
@@ -1137,7 +1145,13 @@ void PdfParser::opSetStrokeColorSpace(Object args[], int /*numArgs*/)
state->setStrokePattern(NULL);
res->lookupColorSpace(args[0].getName(), &obj);
-#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
+#if defined(POPPLER_EVEN_NEWER_COLOR_SPACE_API)
+ if (obj.isNull()) {
+ colorSpace = GfxColorSpace::parse(&args[0], NULL, NULL);
+ } else {
+ colorSpace = GfxColorSpace::parse(&obj, NULL, NULL);
+ }
+#elif defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
if (obj.isNull()) {
colorSpace = GfxColorSpace::parse(&args[0], NULL);
} else {
@@ -1231,7 +1245,13 @@ void PdfParser::opSetFillColorN(Object args[], int numArgs) {
builder->updateStyle(state);
}
GfxPattern *pattern;
-#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
+#if defined(POPPLER_EVEN_NEWER_COLOR_SPACE_API)
+ if (args[numArgs-1].isName() &&
+ (pattern = res->lookupPattern(args[numArgs-1].getName(), NULL, NULL))) {
+ state->setFillPattern(pattern);
+ builder->updateStyle(state);
+ }
+#elif defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
if (args[numArgs-1].isName() &&
(pattern = res->lookupPattern(args[numArgs-1].getName(), NULL))) {
state->setFillPattern(pattern);
@@ -1291,7 +1311,13 @@ void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
builder->updateStyle(state);
}
GfxPattern *pattern;
-#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
+#if defined(POPPLER_EVEN_NEWER_COLOR_SPACE_API)
+ if (args[numArgs-1].isName() &&
+ (pattern = res->lookupPattern(args[numArgs-1].getName(), NULL, NULL))) {
+ state->setStrokePattern(pattern);
+ builder->updateStyle(state);
+ }
+#elif defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
if (args[numArgs-1].isName() &&
(pattern = res->lookupPattern(args[numArgs-1].getName(), NULL))) {
state->setStrokePattern(pattern);
@@ -1746,7 +1772,11 @@ void PdfParser::opShFill(Object args[], int /*numArgs*/)
double *matrix = NULL;
GBool savedState = gFalse;
-#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
+#if defined(POPPLER_EVEN_NEWER_COLOR_SPACE_API)
+ if (!(shading = res->lookupShading(args[0].getName(), NULL, NULL))) {
+ return;
+ }
+#elif defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
if (!(shading = res->lookupShading(args[0].getName(), NULL))) {
return;
}
@@ -2817,7 +2847,9 @@ void PdfParser::doImage(Object * /*ref*/, Stream *str, GBool inlineImg)
}
}
if (!obj1.isNull()) {
-#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
+#if defined(POPPLER_EVEN_NEWER_COLOR_SPACE_API)
+ colorSpace = GfxColorSpace::parse(&obj1, NULL, NULL);
+#elif defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
colorSpace = GfxColorSpace::parse(&obj1, NULL);
#else
colorSpace = GfxColorSpace::parse(&obj1);
@@ -2909,7 +2941,9 @@ void PdfParser::doImage(Object * /*ref*/, Stream *str, GBool inlineImg)
obj2.free();
}
}
-#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
+#if defined(POPPLER_EVEN_NEWER_COLOR_SPACE_API)
+ GfxColorSpace *maskColorSpace = GfxColorSpace::parse(&obj1, NULL, NULL);
+#elif defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
GfxColorSpace *maskColorSpace = GfxColorSpace::parse(&obj1, NULL);
#else
GfxColorSpace *maskColorSpace = GfxColorSpace::parse(&obj1);
@@ -3099,7 +3133,9 @@ void PdfParser::doForm(Object *str) {
if (obj1.dictLookup(const_cast<char*>("S"), &obj2)->isName(const_cast<char*>("Transparency"))) {
transpGroup = gTrue;
if (!obj1.dictLookup(const_cast<char*>("CS"), &obj3)->isNull()) {
-#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
+#if defined(POPPLER_EVEN_NEWER_COLOR_SPACE_API)
+ blendingColorSpace = GfxColorSpace::parse(&obj3, NULL, NULL);
+#elif defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
blendingColorSpace = GfxColorSpace::parse(&obj3, NULL);
#else
blendingColorSpace = GfxColorSpace::parse(&obj3);
diff --git a/src/ink-comboboxentry-action.cpp b/src/ink-comboboxentry-action.cpp
index 06ccc3739..ebd238edc 100644
--- a/src/ink-comboboxentry-action.cpp
+++ b/src/ink-comboboxentry-action.cpp
@@ -38,7 +38,7 @@ static GtkWidget* create_tool_item( GtkAction* action );
static GtkWidget* create_menu_item( GtkAction* action );
// Internal
-static gint get_active_row_from_text( Ink_ComboBoxEntry_Action* action, const gchar* target_text, gboolean exclude = false );
+static gint get_active_row_from_text( Ink_ComboBoxEntry_Action* action, const gchar* target_text, gboolean exclude = false, gboolean ignore_case = false );
static Glib::ustring check_comma_separated_text( Ink_ComboBoxEntry_Action* action );
// Callbacks
@@ -732,7 +732,8 @@ void ink_comboboxentry_action_set_altx_name( Ink_ComboBoxEntry_Action* actio
// use 3d colunm if available to exclude row from checking (useful to
// skip rows added for font-families included in doc and not on
// system)
-gint get_active_row_from_text( Ink_ComboBoxEntry_Action* action, const gchar* target_text, gboolean exclude ) {
+gint get_active_row_from_text( Ink_ComboBoxEntry_Action* action, const gchar* target_text,
+ gboolean exclude, gboolean ignore_case ) {
// Check if text in list
gint row = 0;
@@ -752,10 +753,23 @@ gint get_active_row_from_text( Ink_ComboBoxEntry_Action* action, const gchar* ta
gchar* text = 0;
gtk_tree_model_get( action->model, &iter, 0, &text, -1 ); // Column 0
- // Check for match
- if( strcmp( target_text, text ) == 0 ){
- found = true;
- break;
+ if( !ignore_case ) {
+ // Case sensitive compare
+ if( strcmp( target_text, text ) == 0 ){
+ found = true;
+ break;
+ }
+ } else {
+ // Case insensitive compare
+ gchar* target_text_casefolded = g_utf8_casefold( target_text, -1 );
+ gchar* text_casefolded = g_utf8_casefold( text, -1 );
+ gboolean equal = (strcmp( target_text_casefolded, text_casefolded ) == 0 );
+ g_free( text_casefolded );
+ g_free( target_text_casefolded );
+ if( equal ) {
+ found = true;
+ break;
+ }
}
}
@@ -794,7 +808,7 @@ static Glib::ustring check_comma_separated_text( Ink_ComboBoxEntry_Action* actio
// Remove any surrounding white space.
g_strstrip( tokens[i] );
- if( get_active_row_from_text( action, tokens[i], true ) == -1 ) {
+ if( get_active_row_from_text( action, tokens[i], true, true ) == -1 ) {
missing += tokens[i];
missing += ", ";
}
diff --git a/src/libnrtype/font-lister.cpp b/src/libnrtype/font-lister.cpp
index 98589d9d7..8ce5eccfc 100644
--- a/src/libnrtype/font-lister.cpp
+++ b/src/libnrtype/font-lister.cpp
@@ -25,6 +25,13 @@
//#define DEBUG_FONT
+// CSS dictates that font family names are case insensitive.
+// This should really implement full Unicode case unfolding.
+bool familyNamesAreEqual( const Glib::ustring &a, const Glib::ustring &b ) {
+
+ return( a.casefold().compare( b.casefold() ) == 0 );
+}
+
namespace Inkscape
{
FontLister::FontLister ()
@@ -83,6 +90,15 @@ namespace Inkscape
font_list_store->thaw_notify();
style_list_store = Gtk::ListStore::create (FontStyleList);
+
+ // Initialize style store with defaults
+ style_list_store->freeze_notify();
+ style_list_store->clear();
+ for (GList *l=default_styles; l; l = l->next) {
+ Gtk::TreeModel::iterator treeModelIter = style_list_store->append();
+ (*treeModelIter)[FontStyleList.styles] = (char*)l->data;
+ }
+ style_list_store->thaw_notify();
}
// Example of how to use "foreach_iter"
@@ -111,7 +127,7 @@ namespace Inkscape
Gtk::TreeModel::iterator iter2 = font_list_store->get_iter( "0" );
while( iter2 != font_list_store->children().end() ) {
Gtk::TreeModel::Row row = *iter2;
- if( row[FontList.onSystem] && tokens[0].compare( row[FontList.family] ) == 0 ) {
+ if( row[FontList.onSystem] && familyNamesAreEqual( tokens[0], row[FontList.family] ) ) {
styles = row[FontList.styles];
break;
}
@@ -188,7 +204,7 @@ namespace Inkscape
Gtk::TreeModel::iterator iter2 = font_list_store->get_iter( "0" );
while( iter2 != font_list_store->children().end() ) {
Gtk::TreeModel::Row row = *iter2;
- if( row[FontList.onSystem] && tokens[0].compare( row[FontList.family] ) == 0 ) {
+ if( row[FontList.onSystem] && familyNamesAreEqual( tokens[0], row[FontList.family] ) ) {
styles = row[FontList.styles];
break;
}
@@ -219,7 +235,7 @@ namespace Inkscape
path.push_back( row );
Gtk::TreeModel::iterator iter = font_list_store->get_iter( path );
if( iter ) {
- if( current_family.compare( (*iter)[FontList.family] ) == 0 ) {
+ if( familyNamesAreEqual( current_family, (*iter)[FontList.family] ) ) {
current_family_row = row;
break;
}
@@ -231,37 +247,12 @@ namespace Inkscape
font_list_store->thaw_notify();
}
- // FIXME: why do we parse the style attribute instead of the object's SPStyle?
void
FontLister::update_font_list_recursive( SPObject *r, std::list<Glib::ustring> *l ) {
- const gchar *style = r->getRepr()->attribute("style");
- if( style != NULL ) {
-
- std::vector<Glib::ustring> tokens = Glib::Regex::split_simple(";", style );
- for( size_t i=0; i < tokens.size(); ++i ) {
-
- Glib::ustring token = tokens[i];
- size_t found = token.find("font-family:");
-
- if( found != Glib::ustring::npos ) {
-
- // Remove "font-family:"
- token.erase(found,12);
-
- // Remove any leading single or double quote
- if( token[0] == '\'' || token[0] == '"' ) {
- token.erase(0,1);
- }
-
- // Remove any trailing single or double quote
- if( token[token.length()-1] == '\'' || token[token.length()-1] == '"' ) {
- token.erase(token.length()-1);
- }
-
- l->push_back( token );
- }
- }
+ const gchar *font_family = r->style->font_family.value;
+ if( font_family ) {
+ l->push_back( Glib::ustring( font_family ) );
}
for (SPObject *child = r->firstChild(); child; child = child->getNext()) {
@@ -400,6 +391,7 @@ namespace Inkscape
std::pair<Glib::ustring, Glib::ustring> ui = ui_from_fontspec( current_fontspec );
set_font_family( ui.first );
+ set_font_style( ui.second );
#ifdef DEBUG_FONT
std::cout << " family_row: :" << current_family_row << ":" << std::endl;
@@ -440,7 +432,7 @@ std::pair<Glib::ustring, Glib::ustring> FontLister::new_font_family (Glib::ustri
#endif
// No need to do anything if new family is same as old family.
- if ( new_family.compare( current_family ) == 0 ) {
+ if ( familyNamesAreEqual( new_family, current_family ) ) {
#ifdef DEBUG_FONT
std::cout << "FontLister::new_font_family: exit: no change in family." << std::endl;
std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl;
@@ -459,7 +451,7 @@ std::pair<Glib::ustring, Glib::ustring> FontLister::new_font_family (Glib::ustri
Gtk::TreeModel::Row row = *iter;
- if( new_family.compare( row[FontList.family] ) == 0 ) {
+ if( familyNamesAreEqual( new_family, row[FontList.family] ) ) {
styles = row[FontList.styles];
break;
}
@@ -704,18 +696,19 @@ std::pair<Glib::ustring, Glib::ustring> FontLister::new_font_family (Glib::ustri
fontspec = style->font_family.value;
fontspec += ",";
+ // Use weight names as defined by Pango
switch (style->font_weight.computed) {
case SP_CSS_FONT_WEIGHT_100:
- fontspec += " 100";
+ fontspec += " Thin";
break;
case SP_CSS_FONT_WEIGHT_200:
- fontspec += " 200";
+ fontspec += " Ultra-Light";
break;
case SP_CSS_FONT_WEIGHT_300:
- fontspec += " 300";
+ fontspec += " Light";
break;
case SP_CSS_FONT_WEIGHT_400:
@@ -724,24 +717,24 @@ std::pair<Glib::ustring, Glib::ustring> FontLister::new_font_family (Glib::ustri
break;
case SP_CSS_FONT_WEIGHT_500:
- fontspec += " 500";
+ fontspec += " Medium";
break;
case SP_CSS_FONT_WEIGHT_600:
- fontspec += " 600";
+ fontspec += " Semi-Bold";
break;
case SP_CSS_FONT_WEIGHT_700:
case SP_CSS_FONT_WEIGHT_BOLD:
- fontspec += " bold";
+ fontspec += " Bold";
break;
case SP_CSS_FONT_WEIGHT_800:
- fontspec += " 800";
+ fontspec += " Ultra-Bold";
break;
case SP_CSS_FONT_WEIGHT_900:
- fontspec += " 900";
+ fontspec += " Heavy";
break;
case SP_CSS_FONT_WEIGHT_LIGHTER:
@@ -837,7 +830,7 @@ std::pair<Glib::ustring, Glib::ustring> FontLister::new_font_family (Glib::ustri
Gtk::TreeModel::Row row = *iter;
- if( family.compare( row[FontList.family] ) == 0 ) {
+ if( familyNamesAreEqual( family, row[FontList.family] ) ) {
return row;
}
@@ -863,7 +856,7 @@ std::pair<Glib::ustring, Glib::ustring> FontLister::new_font_family (Glib::ustri
Gtk::TreeModel::Row row = *iter;
- if( style.compare( row[FontStyleList.styles] ) == 0 ) {
+ if( familyNamesAreEqual( style, row[FontStyleList.styles] ) ) {
return row;
}
@@ -1051,7 +1044,7 @@ void font_lister_cell_data_func(GtkCellLayout */*cell_layout*/,
valid = gtk_tree_model_iter_next( GTK_TREE_MODEL(model), &iter ) ) {
gtk_tree_model_get(model, &iter, 0, &family, 2, &onSystem, -1);
- if( onSystem && token.compare( family ) == 0 ) {
+ if( onSystem && familyNamesAreEqual( token, family ) ) {
found = true;
break;
}
diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt
index c7547ded3..c3ce31cfc 100644
--- a/src/live_effects/CMakeLists.txt
+++ b/src/live_effects/CMakeLists.txt
@@ -15,6 +15,7 @@ set(live_effects_SRC
lpe-dynastroke.cpp
lpe-ellipse-5pts.cpp
lpe-envelope.cpp
+ lpe-envelope-perspective.cpp
lpe-extrude.cpp
lpe-fill-between-many.cpp
lpe-fill-between-strokes.cpp
@@ -43,6 +44,7 @@ set(live_effects_SRC
lpe-bspline.cpp
lpe-text_label.cpp
lpe-vonkoch.cpp
+ lpe-envelope-perspective.cpp
lpegroupbbox.cpp
lpeobject-reference.cpp
lpeobject.cpp
@@ -113,6 +115,7 @@ set(live_effects_SRC
lpe-bspline.h
lpe-text_label.h
lpe-vonkoch.h
+ lpe-envelope-perspective.h
lpegroupbbox.h
lpeobject-reference.h
lpeobject.h
diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert
index 922a4a966..3d474d00c 100644
--- a/src/live_effects/Makefile_insert
+++ b/src/live_effects/Makefile_insert
@@ -104,4 +104,6 @@ ink_common_sources += \
live_effects/lpe-jointype.cpp \
live_effects/lpe-jointype.h \
live_effects/lpe-taperstroke.cpp \
- live_effects/lpe-taperstroke.h
+ live_effects/lpe-taperstroke.h \
+ live_effects/lpe-envelope-perspective.cpp \
+ live_effects/lpe-envelope-perspective.h
diff --git a/src/live_effects/effect-enum.h b/src/live_effects/effect-enum.h
index 82ce36225..92f35fc72 100644
--- a/src/live_effects/effect-enum.h
+++ b/src/live_effects/effect-enum.h
@@ -60,6 +60,7 @@ enum EffectType {
BOUNDING_BOX,
JOIN_TYPE,
TAPER_STROKE,
+ ENVELOPE_PERSPECTIVE,
INVALID_LPE // This must be last (I made it such that it is not needed anymore I think..., Don't trust on it being last. - johan)
};
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp
index 3c13ee9a9..4157e14d7 100644
--- a/src/live_effects/effect.cpp
+++ b/src/live_effects/effect.cpp
@@ -57,6 +57,7 @@
#include "live_effects/lpe-bounding-box.h"
#include "live_effects/lpe-jointype.h"
#include "live_effects/lpe-taperstroke.h"
+#include "live_effects/lpe-envelope-perspective.h"
#include "xml/node-event-vector.h"
#include "sp-object.h"
@@ -144,6 +145,7 @@ const Util::EnumData<EffectType> LPETypeData[] = {
/* 0.91 */
{SIMPLIFY, N_("Simplify"), "simplify"},
{LATTICE2, N_("Lattice Deformation 2"), "lattice2"},
+ {ENVELOPE_PERSPECTIVE, N_("Envelope-Perspective"), "envelope-perspective"},
};
const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
@@ -290,12 +292,16 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
break;
case TAPER_STROKE:
neweffect = static_cast<Effect*> ( new LPETaperStroke(lpeobj) );
+ break;
case SIMPLIFY:
neweffect = static_cast<Effect*> ( new LPESimplify(lpeobj) );
break;
case LATTICE2:
neweffect = static_cast<Effect*> ( new LPELattice2(lpeobj) );
break;
+ case ENVELOPE_PERSPECTIVE:
+ neweffect = static_cast<Effect*> ( new LPEEnvelopePerspective(lpeobj) );
+ break;
default:
g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
neweffect = NULL;
diff --git a/src/live_effects/lpe-envelope-perspective.cpp b/src/live_effects/lpe-envelope-perspective.cpp
new file mode 100644
index 000000000..bfd6e56d7
--- /dev/null
+++ b/src/live_effects/lpe-envelope-perspective.cpp
@@ -0,0 +1,414 @@
+/** \file
+ * LPE <envelope-perspective> implementation
+
+ */
+/*
+ * Authors:
+ * Jabiertxof Code migration from python extensions envelope and perspective
+ * Aaron Spike, aaron@ekips.org from envelope and perspective phyton code
+ * Dmitry Platonov, shadowjack@mail.ru, 2006 perspective approach & math
+ * Jose Hevia (freon) Transform algorithm from envelope
+ *
+ * Copyright (C) 2007-2014 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/lpe-envelope-perspective.h"
+#include "helper/geom.h"
+#include "display/curve.h"
+#include "svg/svg.h"
+#include <gtkmm/separator.h>
+#include <gtkmm/stock.h>
+#include <tools-switch.h>
+#include <gsl/gsl_linalg.h>
+#include "desktop.h"
+
+using namespace Geom;
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+LPEEnvelopePerspective::LPEEnvelopePerspective(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ // initialise your parameters here:
+ perspective(_("Perspective"), _("Perspective"), "Perspective", &wr, this, false),
+ Up_Left_Point(_("Top Left:"), _("Top Left - Ctrl+Alt+Click to reset"), "Up_Left_Point", &wr, this),
+ Up_Right_Point(_("Top Right:"), _("Top Right - Ctrl+Alt+Click to reset"), "Up_Right_Point", &wr, this),
+ Down_Left_Point(_("Down Left:"), _("Down Left - Ctrl+Alt+Click to reset"), "Down_Left_Point", &wr, this),
+ Down_Right_Point(_("Down Right:"), _("Down Right - Ctrl+Alt+Click to reset"), "Down_Right_Point", &wr, this)
+{
+ // register all your parameters here, so Inkscape knows which parameters this effect has:
+ registerParameter( dynamic_cast<Parameter *>(&perspective));
+ registerParameter( dynamic_cast<Parameter *>(&Up_Left_Point) );
+ registerParameter( dynamic_cast<Parameter *>(&Up_Right_Point) );
+ registerParameter( dynamic_cast<Parameter *>(&Down_Left_Point) );
+ registerParameter( dynamic_cast<Parameter *>(&Down_Right_Point) );
+}
+
+LPEEnvelopePerspective::~LPEEnvelopePerspective()
+{
+}
+
+void LPEEnvelopePerspective::doEffect(SPCurve *curve) {
+ using Geom::X;
+ using Geom::Y;
+ double projmatrix[3][3];
+ if(perspective){
+ std::vector<Geom::Point> handles(4);
+ handles[0] = Down_Left_Point;
+ handles[1] = Up_Left_Point;
+ handles[2] = Up_Right_Point;
+ handles[3] = Down_Right_Point;
+ std::vector<Geom::Point> sourceHandles(4);
+ sourceHandles[0] = Geom::Point(boundingbox_X.min(), boundingbox_Y.max());
+ sourceHandles[1] = Geom::Point(boundingbox_X.min(), boundingbox_Y.min());
+ sourceHandles[2] = Geom::Point(boundingbox_X.max(), boundingbox_Y.min());
+ sourceHandles[3] = Geom::Point(boundingbox_X.max(), boundingbox_Y.max());
+ double solmatrix[8][8] = {{0}};
+ double free_term[8] = {0};
+ double gslSolmatrix[64];
+ for(unsigned int i = 0; i < 4; ++i){
+ solmatrix[i][0] = sourceHandles[i][X];
+ solmatrix[i][1] = sourceHandles[i][Y];
+ solmatrix[i][2] = 1;
+ solmatrix[i][6] = -handles[i][X] * sourceHandles[i][X];
+ solmatrix[i][7] = -handles[i][X] * sourceHandles[i][Y];
+ solmatrix[i+4][3] = sourceHandles[i][X];
+ solmatrix[i+4][4] = sourceHandles[i][Y];
+ solmatrix[i+4][5] = 1;
+ solmatrix[i+4][6] = -handles[i][Y] * sourceHandles[i][X];
+ solmatrix[i+4][7] = -handles[i][Y] * sourceHandles[i][Y];
+ free_term[i] = handles[i][X];
+ free_term[i+4] = handles[i][Y];
+ }
+ int h = 0;
+ for( int i = 0; i < 8; i++ ) {
+ for( int j = 0; j < 8; j++ ) {
+ gslSolmatrix[h] = solmatrix[i][j];
+ h++;
+ }
+ }
+ //this is get by this page:
+ //http://www.gnu.org/software/gsl/manual/html_node/Linear-Algebra-Examples.html#Linear-Algebra-Examples
+ gsl_matrix_view m = gsl_matrix_view_array (gslSolmatrix, 8, 8);
+ gsl_vector_view b = gsl_vector_view_array (free_term, 8);
+ gsl_vector *x = gsl_vector_alloc (8);
+ int s;
+ gsl_permutation * p = gsl_permutation_alloc (8);
+ gsl_linalg_LU_decomp (&m.matrix, p, &s);
+ gsl_linalg_LU_solve (&m.matrix, p, &b.vector, x);
+ h = 0;
+ for( int i = 0; i < 3; i++ ) {
+ for( int j = 0; j < 3; j++ ) {
+ if(h==8){
+ projmatrix[2][2] = 1.0;
+ continue;
+ }
+ projmatrix[i][j] = gsl_vector_get(x, h);
+ h++;
+ }
+ }
+ gsl_permutation_free (p);
+ gsl_vector_free (x);
+ }
+ Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(curve->get_pathvector());
+ curve->reset();
+ Geom::CubicBezier const *cubic = NULL;
+ Geom::Point pointAt1(0, 0);
+ Geom::Point pointAt2(0, 0);
+ Geom::Point pointAt3(0, 0);
+ for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
+ //Si está vacío...
+ if (path_it->empty())
+ continue;
+ //Itreadores
+ SPCurve *nCurve = new SPCurve();
+ Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
+ Geom::Path::const_iterator curve_it2 =
+ ++(path_it->begin()); // outgoing curve
+ Geom::Path::const_iterator curve_endit =
+ path_it->end_default(); // this determines when the loop has to stop
+
+ if (path_it->closed()) {
+ // if the path is closed, maybe we have to stop a bit earlier because the
+ // closing line segment has zerolength.
+ const Geom::Curve &closingline =
+ path_it->back_closed(); // the closing line segment is always of type
+ // Geom::LineSegment.
+ if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
+ // closingline.isDegenerate() did not work, because it only checks for
+ // *exact* zero length, which goes wrong for relative coordinates and
+ // rounding errors...
+ // the closing line segment has zero-length. So stop before that one!
+ curve_endit = path_it->end_open();
+ }
+ }
+ if(perspective){
+ nCurve->moveto(project_point(curve_it1->initialPoint(),projmatrix));
+ }else{
+ nCurve->moveto(project_point(curve_it1->initialPoint()));
+ }
+ while (curve_it2 != curve_endit) {
+ cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
+ if (cubic) {
+ pointAt1 = (*cubic)[1];
+ pointAt2 = (*cubic)[2];
+ } else {
+ pointAt1 = curve_it1->initialPoint();
+ pointAt2 = curve_it1->finalPoint();
+ }
+ pointAt3 = curve_it1->finalPoint();
+ if(perspective){
+ pointAt1 = project_point(pointAt1,projmatrix);
+ pointAt2 = project_point(pointAt2,projmatrix);
+ pointAt3 = project_point(pointAt3,projmatrix);
+ }else{
+ pointAt1 = project_point(pointAt1);
+ pointAt2 = project_point(pointAt2);
+ pointAt3 = project_point(pointAt3);
+ }
+ nCurve->curveto(pointAt1, pointAt2, pointAt3);
+ ++curve_it1;
+ ++curve_it2;
+ }
+ cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
+ if (cubic) {
+ pointAt1 = (*cubic)[1];
+ pointAt2 = (*cubic)[2];
+ } else {
+ pointAt1 = curve_it1->initialPoint();
+ pointAt2 = curve_it1->finalPoint();
+ }
+ pointAt3 = curve_it1->finalPoint();
+ if(perspective){
+ pointAt1 = project_point(pointAt1,projmatrix);
+ pointAt2 = project_point(pointAt2,projmatrix);
+ pointAt3 = project_point(pointAt3,projmatrix);
+ }else{
+ pointAt1 = project_point(pointAt1);
+ pointAt2 = project_point(pointAt2);
+ pointAt3 = project_point(pointAt3);
+ }
+ nCurve->curveto(pointAt1, pointAt2, pointAt3);
+ if(perspective){
+ nCurve->move_endpoints(project_point(path_it->begin()->initialPoint(),projmatrix), pointAt3);
+ }else{
+ nCurve->move_endpoints(project_point(path_it->begin()->initialPoint()), pointAt3);
+ }
+ //y cerramos la curva
+ if (path_it->closed()) {
+ nCurve->closepath_current();
+ }
+ curve->append(nCurve, false);
+ nCurve->reset();
+ delete nCurve;
+ }
+}
+
+Geom::Point
+LPEEnvelopePerspective::project_point(Geom::Point p){
+ double width = boundingbox_X.extent();
+ double height = boundingbox_Y.extent();
+ Geom::Coord xratio = abs(Geom::Point(boundingbox_X.min(), boundingbox_Y.max())[X]-p[X])/width;
+ Geom::Coord yratio = abs(Geom::Point(boundingbox_X.min(), boundingbox_Y.max())[Y]-p[Y])/height;
+ Geom::Line* horiz = new Geom::Line();
+ Geom::Line* vert = new Geom::Line();
+ vert->setPoints (pointAtRatio(yratio,Down_Left_Point,Up_Left_Point),pointAtRatio(yratio,Down_Right_Point,Up_Right_Point));
+ horiz->setPoints (pointAtRatio(xratio,Down_Left_Point,Down_Right_Point),pointAtRatio(xratio,Up_Left_Point,Up_Right_Point));
+
+ OptCrossing crossPoint = intersection(*horiz,*vert);
+ if(crossPoint){
+ return horiz->pointAt(Geom::Coord(crossPoint->ta));
+ }else{
+ return p;
+ }
+}
+
+Geom::Point
+LPEEnvelopePerspective::project_point(Geom::Point p, double m[][3]){
+ Geom::Coord x = p[0];
+ Geom::Coord y = p[1];
+ return Geom::Point(
+ Geom::Coord((x*m[0][0] + y*m[0][1] + m[0][2])/(x*m[2][0]+y*m[2][1]+m[2][2])),
+ Geom::Coord((x*m[1][0] + y*m[1][1] + m[1][2])/(x*m[2][0]+y*m[2][1]+m[2][2])));
+}
+
+Geom::Point
+LPEEnvelopePerspective::pointAtRatio(Geom::Coord ratio,Geom::Point A, Geom::Point B){
+ Geom::Coord x = A[X] + (ratio * (B[X]-A[X]));
+ Geom::Coord y = A[Y]+ (ratio * (B[Y]-A[Y]));
+ return Point(x, y);
+}
+
+
+Gtk::Widget *
+LPEEnvelopePerspective::newWidget()
+{
+ // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
+ Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox(Effect::newWidget()) );
+
+ vbox->set_border_width(5);
+ vbox->set_homogeneous(false);
+ vbox->set_spacing(6);
+ Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false,0));
+
+ Gtk::Button* resetButton = Gtk::manage(new Gtk::Button(Gtk::Stock::CLEAR));
+ resetButton->signal_clicked().connect(sigc::mem_fun (*this,&LPEEnvelopePerspective::resetGrid));
+ resetButton->set_size_request(140,45);
+ vbox->pack_start(*hbox, true,true,2);
+ hbox->pack_start(*resetButton, false, false,2);
+ std::vector<Parameter *>::iterator it = param_vector.begin();
+ Gtk::HBox * hboxUpHandles = Gtk::manage(new Gtk::HBox(false,0));
+ Gtk::HBox * hboxDownHandles = Gtk::manage(new Gtk::HBox(false,0));
+ while (it != param_vector.end()) {
+ if ((*it)->widget_is_visible) {
+ Parameter * param = *it;
+ Gtk::Widget * widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget());
+ if (param->param_key == "Up_Left_Point" ||
+ param->param_key == "Up_Right_Point" ||
+ param->param_key == "Down_Left_Point" ||
+ param->param_key == "Down_Right_Point")
+ {
+ Gtk::HBox * pointParameter = dynamic_cast<Gtk::HBox *>(widg);
+ std::vector< Gtk::Widget* > childList = pointParameter->get_children();
+ Gtk::HBox * pointParameterHBox = dynamic_cast<Gtk::HBox *>(childList[0]);
+ std::vector< Gtk::Widget* > childList2 = pointParameterHBox->get_children();
+ pointParameterHBox->remove(childList2[0][0]);
+ Glib::ustring * tip = param->param_getTooltip();
+ if (widg) {
+ if(param->param_key == "Up_Left_Point"){
+ Gtk::Label* handles = Gtk::manage(new Gtk::Label(Glib::ustring(_("Handles:")),Gtk::ALIGN_START));
+ vbox->pack_start(*handles, false, false, 2);
+ hboxUpHandles->pack_start(*widg, true, true, 2);
+ hboxUpHandles->pack_start(*Gtk::manage(new Gtk::VSeparator()), Gtk::PACK_EXPAND_WIDGET);
+ }else if(param->param_key == "Up_Right_Point"){
+ hboxUpHandles->pack_start(*widg, true, true, 2);
+ }else if(param->param_key == "Down_Left_Point"){
+ hboxDownHandles->pack_start(*widg, true, true, 2);
+ hboxDownHandles->pack_start(*Gtk::manage(new Gtk::VSeparator()), Gtk::PACK_EXPAND_WIDGET);
+ }else{
+ hboxDownHandles->pack_start(*widg, true, true, 2);
+ }
+ if (tip) {
+ widg->set_tooltip_text(*tip);
+ } else {
+ widg->set_tooltip_text("");
+ widg->set_has_tooltip(false);
+ }
+ }
+ }else{
+ Glib::ustring * tip = param->param_getTooltip();
+ if (widg) {
+ vbox->pack_start(*widg, true, true, 2);
+ if (tip) {
+ widg->set_tooltip_text(*tip);
+ } else {
+ widg->set_tooltip_text("");
+ widg->set_has_tooltip(false);
+ }
+ }
+ }
+ }
+
+ ++it;
+ }
+ vbox->pack_start(*hboxUpHandles,true, true, 2);
+ Gtk::HBox * hboxMiddle = Gtk::manage(new Gtk::HBox(true,2));
+ hboxMiddle->pack_start(*Gtk::manage(new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET);
+ hboxMiddle->pack_start(*Gtk::manage(new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET);
+ vbox->pack_start(*hboxMiddle, false, true, 2);
+ vbox->pack_start(*hboxDownHandles, true, true, 2);
+ return dynamic_cast<Gtk::Widget *>(vbox);
+}
+
+void
+LPEEnvelopePerspective::doBeforeEffect (SPLPEItem const* lpeitem)
+{
+ original_bbox(lpeitem);
+ setDefaults();
+}
+
+void
+LPEEnvelopePerspective::setDefaults()
+{
+ Geom::Point Up_Left(boundingbox_X.min(), boundingbox_Y.min());
+ Geom::Point Up_Right(boundingbox_X.max(), boundingbox_Y.min());
+ Geom::Point Down_Left(boundingbox_X.min(), boundingbox_Y.max());
+ Geom::Point Down_Right(boundingbox_X.max(), boundingbox_Y.max());
+
+ Up_Left_Point.param_update_default(Up_Left);
+ Up_Right_Point.param_update_default(Up_Right);
+ Down_Right_Point.param_update_default(Down_Right);
+ Down_Left_Point.param_update_default(Down_Left);
+}
+
+void
+LPEEnvelopePerspective::resetGrid()
+{
+ Up_Left_Point.param_set_and_write_default();
+ Up_Right_Point.param_set_and_write_default();
+ Down_Right_Point.param_set_and_write_default();
+ Down_Left_Point.param_set_and_write_default();
+ //todo:this hack is only to reposition the knots on reser grid button
+ //Better update path effect in LPEITEM
+ SPDesktop * desktop = inkscape_active_desktop();
+ tools_switch(desktop, TOOLS_SELECT);
+ tools_switch(desktop, TOOLS_NODES);
+}
+
+void
+LPEEnvelopePerspective::resetDefaults(SPItem const* item)
+{
+ Effect::resetDefaults(item);
+ original_bbox(SP_LPE_ITEM(item));
+ setDefaults();
+ resetGrid();
+}
+
+void
+LPEEnvelopePerspective::calculateCurve(Geom::Point a,Geom::Point b, SPCurve* c, bool horizontal, bool move)
+{
+ using Geom::X;
+ using Geom::Y;
+ if(move) c->moveto(a);
+ Geom::Point cubic1 = a + (1./3)* (b - a);
+ Geom::Point cubic2 = b + (1./3)* (a - b);
+ if(horizontal) c->curveto(Geom::Point(cubic1[X],a[Y]),Geom::Point(cubic2[X],b[Y]),b);
+ else c->curveto(Geom::Point(a[X],cubic1[Y]),Geom::Point(b[X],cubic2[Y]),b);
+}
+
+void
+LPEEnvelopePerspective::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
+{
+ hp_vec.clear();
+
+ SPCurve *c = new SPCurve();
+ c->reset();
+ c->moveto(Up_Left_Point);
+ c->lineto(Up_Right_Point);
+ c->lineto(Down_Right_Point);
+ c->lineto(Down_Left_Point);
+ c->lineto(Up_Left_Point);
+ hp_vec.push_back(c->get_pathvector());
+}
+
+
+/* ######################## */
+
+} //namespace LivePathEffect
+} /* namespace Inkscape */
+
+
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/lpe-envelope-perspective.h b/src/live_effects/lpe-envelope-perspective.h
new file mode 100644
index 000000000..6aae33936
--- /dev/null
+++ b/src/live_effects/lpe-envelope-perspective.h
@@ -0,0 +1,76 @@
+#ifndef INKSCAPE_LPE_ENVELOPE_PERSPECTIVE_H
+#define INKSCAPE_LPE_ENVELOPE_PERSPECTIVE_H
+
+/** \file
+ * LPE <envelope-perspective> implementation , see lpe-envelope-perspective.cpp.
+
+ */
+/*
+ * Authors:
+ * Jabiertxof Code migration from python extensions envelope and perspective
+ * Aaron Spike, aaron@ekips.org from envelope and perspective phyton code
+ * Dmitry Platonov, shadowjack@mail.ru, 2006 perspective approach & math
+ * Jose Hevia (freon) Transform algorithm from envelope
+ *
+ * Copyright (C) 2007-2014 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/parameter/enum.h"
+#include "live_effects/effect.h"
+#include "live_effects/parameter/pointreseteable.h"
+#include "live_effects/lpegroupbbox.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+class LPEEnvelopePerspective : public Effect, GroupBBoxEffect {
+public:
+
+ LPEEnvelopePerspective(LivePathEffectObject *lpeobject);
+ virtual ~LPEEnvelopePerspective();
+
+ virtual void doEffect(SPCurve *curve);
+
+ virtual Geom::Point project_point(Geom::Point p);
+
+ virtual Geom::Point project_point(Geom::Point p, double m[][3]);
+
+ Geom::Point pointAtRatio(Geom::Coord ratio,Geom::Point A, Geom::Point B);
+
+ virtual void resetDefaults(SPItem const* item);
+
+ virtual void doBeforeEffect(SPLPEItem const* lpeitem);
+
+ virtual Gtk::Widget * newWidget();
+
+ virtual void calculateCurve(Geom::Point a,Geom::Point b, SPCurve *c, bool horizontal, bool move);
+
+ virtual void setDefaults();
+
+ virtual void resetGrid();
+
+ //virtual void original_bbox(SPLPEItem const* lpeitem, bool absolute = false);
+
+ //virtual void addCanvasIndicators(SPLPEItem const*/*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/);
+
+ //virtual std::vector<Geom::PathVector> getHelperPaths(SPLPEItem const* lpeitem);
+protected:
+ void addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec);
+private:
+
+ BoolParam perspective;
+ PointReseteableParam Up_Left_Point;
+ PointReseteableParam Up_Right_Point;
+ PointReseteableParam Down_Left_Point;
+ PointReseteableParam Down_Right_Point;
+
+ LPEEnvelopePerspective(const LPEEnvelopePerspective&);
+ LPEEnvelopePerspective& operator=(const LPEEnvelopePerspective&);
+};
+
+} //namespace LivePathEffect
+} //namespace Inkscape
+
+#endif
diff --git a/src/style-internal.cpp b/src/style-internal.cpp
index b15892128..8b4f3c1cd 100644
--- a/src/style-internal.cpp
+++ b/src/style-internal.cpp
@@ -648,9 +648,6 @@ SPIString::read( gchar const *str ) {
if( !str ) return;
- // libcroco puts quotes around some strings... remove
- gchar *str_unquoted = attribute_unquote(str);
-
if (!strcmp(str, "inherit")) {
set = true;
inherit = true;
@@ -658,10 +655,18 @@ SPIString::read( gchar const *str ) {
} else {
set = true;
inherit = false;
- value = g_strdup(str_unquoted);
- }
- g_free( str_unquoted );
+ // libcroco puts quotes around some strings... remove
+ Glib::ustring str_unquoted(str);
+ css_unquote( str_unquoted );
+
+ // Unquote individual family names, Pango always uses unquoted names.
+ if( name.compare( "font-family" ) == 0 ) {
+ css_font_family_unquote( str_unquoted );
+ }
+
+ value = g_strdup(str_unquoted.c_str());
+ }
}
diff --git a/src/style.cpp b/src/style.cpp
index 11b1dc440..8e4c89839 100644
--- a/src/style.cpp
+++ b/src/style.cpp
@@ -58,6 +58,8 @@
#include <2geom/math-utils.h>
+#include <glibmm/regex.h>
+
using Inkscape::CSSOStringStream;
using std::vector;
@@ -1824,31 +1826,43 @@ sp_css_attr_scale(SPCSSAttr *css, double ex)
}
-// Called in style.cpp, xml/repr-css.cpp
+// Called in style-internal.cpp, xml/repr-css.cpp
/**
- * Remove quotes and escapes from a string. Returned value must be g_free'd.
+ * Remove paired single and double quotes from a string, changing string in place.
* Note: in CSS (in style= and in stylesheets), unquoting and unescaping is done
* by libcroco, our CSS parser, though it adds a new pair of "" quotes for the strings
- * it parsed for us. So this function is only used to remove those quotes and for
- * presentation attributes, without any unescaping. (XML unescaping
- * (&amp; etc) is done by XML parser.)
+ * it parsed for us.
*/
-gchar *
-attribute_unquote(gchar const *val)
+void
+css_unquote(Glib::ustring &val)
{
- if (val) {
- if (*val == '\'' || *val == '"') {
- int l = strlen(val);
- if (l >= 2) {
- if ( ( val[0] == '"' && val[l - 1] == '"' ) ||
- ( val[0] == '\'' && val[l - 1] == '\'' ) ) {
- return (g_strndup (val+1, l-2));
- }
- }
- }
- }
+ if( val.size() > 1 &&
+ ( (val[0] == '"' && val[val.size()-1] == '"' ) ||
+ (val[0] == '\'' && val[val.size()-1] == '\'' ) ) ) {
- return (val? g_strdup (val) : NULL);
+ val.erase( 0, 1 );
+ val.erase( val.size()-1 );
+ }
+}
+
+// Called in style-internal.cpp, text-toolbar.cpp
+/**
+ * Remove paired single and double quotes from font names in font-family lists,
+ * changing string in place.
+ * Pango expects unquoted font family names. We use unquoted names in interface.
+ */
+void
+css_font_family_unquote(Glib::ustring &val)
+{
+ std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("\\s*,\\s*", val );
+
+ val.erase();
+ for( unsigned i=0; i < tokens.size(); ++i ) {
+ css_unquote( tokens[i] );
+ val += tokens[i] + ", ";
+ }
+ if( val.size() > 1 )
+ val.erase( val.size() - 2 ); // Remove trailing ", "
}
// Called in style.cpp, xml/repr-css.cpp
diff --git a/src/style.h b/src/style.h
index 9e592b78f..506b90b44 100644
--- a/src/style.h
+++ b/src/style.h
@@ -292,7 +292,9 @@ void sp_style_unset_property_attrs(SPObject *o);
void sp_style_set_property_url (SPObject *item, gchar const *property, SPObject *linked, bool recursive);
-gchar *attribute_unquote(gchar const *val);
+void css_unquote( Glib::ustring &val ); // Remove quotes from CSS values (style-internal.cpp, xml/repr-css.cpp)
+void css_font_family_unquote( Glib::ustring &val ); // style-internal.cpp, text-toolbar.cpp
+
Glib::ustring css2_escape_quote(gchar const *val);
#endif // SEEN_SP_STYLE_H
diff --git a/src/ui/dialog/spellcheck.cpp b/src/ui/dialog/spellcheck.cpp
index 8a4ddc57e..a887a7355 100644
--- a/src/ui/dialog/spellcheck.cpp
+++ b/src/ui/dialog/spellcheck.cpp
@@ -347,7 +347,7 @@ SpellCheck::init(SPDesktop *d)
char *slashPos = strrchr(exeName, '\\');
if (slashPos)
*slashPos = '\0';
- g_print ("%s\n", exeName);
+ //g_print ("Aspell prefix path: %s\n", exeName);
#endif
_stops = 0;
@@ -356,54 +356,54 @@ SpellCheck::init(SPDesktop *d)
#ifdef HAVE_ASPELL
{
- AspellConfig *config = new_aspell_config();
+ AspellConfig *config = new_aspell_config();
#ifdef WIN32
- aspell_config_replace(config, "prefix", exeName);
+ aspell_config_replace(config, "prefix", exeName);
#endif
- aspell_config_replace(config, "lang", _lang.c_str());
- aspell_config_replace(config, "encoding", "UTF-8");
- AspellCanHaveError *ret = new_aspell_speller(config);
- delete_aspell_config(config);
- if (aspell_error(ret) != 0) {
- g_warning("Error: %s\n", aspell_error_message(ret));
- delete_aspell_can_have_error(ret);
- return false;
- }
- _speller = to_aspell_speller(ret);
+ aspell_config_replace(config, "lang", _lang.c_str());
+ aspell_config_replace(config, "encoding", "UTF-8");
+ AspellCanHaveError *ret = new_aspell_speller(config);
+ delete_aspell_config(config);
+ if (aspell_error(ret) != 0) {
+ g_warning("Error: %s\n", aspell_error_message(ret));
+ delete_aspell_can_have_error(ret);
+ return false;
+ }
+ _speller = to_aspell_speller(ret);
}
if (_lang2 != "") {
- AspellConfig *config = new_aspell_config();
+ AspellConfig *config = new_aspell_config();
#ifdef WIN32
- aspell_config_replace(config, "prefix", exeName);
+ aspell_config_replace(config, "prefix", exeName);
#endif
- aspell_config_replace(config, "lang", _lang2.c_str());
- aspell_config_replace(config, "encoding", "UTF-8");
- AspellCanHaveError *ret = new_aspell_speller(config);
- delete_aspell_config(config);
- if (aspell_error(ret) != 0) {
- g_warning("Error: %s\n", aspell_error_message(ret));
- delete_aspell_can_have_error(ret);
- return false;
- }
- _speller2 = to_aspell_speller(ret);
+ aspell_config_replace(config, "lang", _lang2.c_str());
+ aspell_config_replace(config, "encoding", "UTF-8");
+ AspellCanHaveError *ret = new_aspell_speller(config);
+ delete_aspell_config(config);
+ if (aspell_error(ret) != 0) {
+ g_warning("Error: %s\n", aspell_error_message(ret));
+ delete_aspell_can_have_error(ret);
+ return false;
+ }
+ _speller2 = to_aspell_speller(ret);
}
if (_lang3 != "") {
- AspellConfig *config = new_aspell_config();
+ AspellConfig *config = new_aspell_config();
#ifdef WIN32
- aspell_config_replace(config, "prefix", exeName);
+ aspell_config_replace(config, "prefix", exeName);
#endif
- aspell_config_replace(config, "lang", _lang3.c_str());
- aspell_config_replace(config, "encoding", "UTF-8");
- AspellCanHaveError *ret = new_aspell_speller(config);
- delete_aspell_config(config);
- if (aspell_error(ret) != 0) {
- g_warning("Error: %s\n", aspell_error_message(ret));
- delete_aspell_can_have_error(ret);
- return false;
- }
- _speller3 = to_aspell_speller(ret);
+ aspell_config_replace(config, "lang", _lang3.c_str());
+ aspell_config_replace(config, "encoding", "UTF-8");
+ AspellCanHaveError *ret = new_aspell_speller(config);
+ delete_aspell_config(config);
+ if (aspell_error(ret) != 0) {
+ g_warning("Error: %s\n", aspell_error_message(ret));
+ delete_aspell_can_have_error(ret);
+ return false;
+ }
+ _speller3 = to_aspell_speller(ret);
}
#endif /* HAVE_ASPELL */
diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp
index 3a4f315da..64a7cd5e7 100644
--- a/src/widgets/text-toolbar.cpp
+++ b/src/widgets/text-toolbar.cpp
@@ -134,6 +134,7 @@ static void sp_text_fontfamily_value_changed( Ink_ComboBoxEntry_Action *act, GOb
g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
Glib::ustring new_family = ink_comboboxentry_action_get_active_text( act );
+ css_font_family_unquote( new_family ); // Remove quotes around font family names.
// TODO: Think about how to handle handle multiple selections. While
// the font-family may be the same for all, the styles might be different.
diff --git a/src/xml/repr-css.cpp b/src/xml/repr-css.cpp
index 32a1e7f5d..e462f70da 100644
--- a/src/xml/repr-css.cpp
+++ b/src/xml/repr-css.cpp
@@ -12,7 +12,7 @@
* changing an item in the List. Utility functions are provided to go back and forth between the
* two ways of representing properties (by a string or by a list).
*
- * Use sp_repr_write_string to go from a property list to a style string.
+ * Use sp_repr_css_write_string to go from a property list to a style string.
*
*/
@@ -350,9 +350,10 @@ void sp_repr_css_merge(SPCSSAttr *dst, SPCSSAttr *src)
static void sp_repr_css_merge_from_decl(SPCSSAttr *css, CRDeclaration const *const decl)
{
guchar *const str_value_unsigned = cr_term_to_string(decl->value);
- gchar *const str_value = reinterpret_cast<gchar *>(str_value_unsigned);
- gchar *value_unquoted = attribute_unquote (str_value); // libcroco returns strings quoted in ""
- Glib::ustring value_unquoted2 = value_unquoted ? value_unquoted : Glib::ustring();
+
+ Glib::ustring value_unquoted( reinterpret_cast<gchar *>(str_value_unsigned ) );
+ css_unquote( value_unquoted ); // libcroco returns strings quoted in "", remove
+
Glib::ustring units;
/*
@@ -362,11 +363,11 @@ static void sp_repr_css_merge_from_decl(SPCSSAttr *css, CRDeclaration const *con
*
* HACK for now is to strip off em and ex units and add them back at the end
*/
- int le = value_unquoted2.length();
+ int le = value_unquoted.length();
if (le > 2) {
- units = value_unquoted2.substr(le-2, 2);
+ units = value_unquoted.substr(le-2, 2);
if ((units == "em") || (units == "ex")) {
- value_unquoted2 = value_unquoted2.substr(0, le-2);
+ value_unquoted = value_unquoted.substr(0, le-2);
}
else {
units.clear();
@@ -377,7 +378,7 @@ static void sp_repr_css_merge_from_decl(SPCSSAttr *css, CRDeclaration const *con
// CSSOStringStream is used here to write valid CSS (as in sp_style_write_string). This has
// the additional benefit of respecting the numerical precission set in the SVG Output
// preferences. We assume any numerical part comes first (if not, the whole string is copied).
- std::stringstream ss( value_unquoted2 );
+ std::stringstream ss( value_unquoted );
double number = 0;
std::string characters;
std::string temp;
@@ -399,8 +400,7 @@ static void sp_repr_css_merge_from_decl(SPCSSAttr *css, CRDeclaration const *con
//g_message("sp_repr_css_merge_from_decl looks like em or ex units %s --> %s", str_value, os.str().c_str());
}
((Node *) css)->setAttribute(decl->property->stryng->str, os.str().c_str(), false);
- g_free(value_unquoted);
- g_free(str_value);
+ g_free(str_value_unsigned);
}
/**