Code

Mnemonics in "Fill and stroke", "Align and distribute", and "Transform" dialogs ...
[inkscape.git] / src / libnrtype / Layout-TNG-Input.cpp
old mode 100755 (executable)
new mode 100644 (file)
index 8b695af..d16c645
@@ -8,12 +8,24 @@
  *
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
+
+#define PANGO_ENABLE_ENGINE
+
+#include <gtk/gtkversion.h>
 #include "Layout-TNG.h"
 #include "style.h"
 #include "svg/svg-length.h"
 #include "sp-object.h"
+#include "sp-string.h"
 #include "FontFactory.h"
 
+#if !PANGO_VERSION_CHECK(1,24,0)
+#define PANGO_WEIGHT_THIN       static_cast<PangoWeight>(100)
+#define PANGO_WEIGHT_BOOK       static_cast<PangoWeight>(380)
+#define PANGO_WEIGHT_MEDIUM     static_cast<PangoWeight>(500)
+#define PANGO_WEIGHT_ULTRAHEAVY static_cast<PangoWeight>(1000)
+#endif
+
 namespace Inkscape {
 namespace Text {
 
@@ -50,6 +62,14 @@ void Layout::appendText(Glib::ustring const &text, SPStyle *style, void *source_
         _copyInputVector(optional_attributes->dx, optional_attributes_offset, &new_source->dx, new_source->text_length);
         _copyInputVector(optional_attributes->dy, optional_attributes_offset, &new_source->dy, new_source->text_length);
         _copyInputVector(optional_attributes->rotate, optional_attributes_offset, &new_source->rotate, new_source->text_length);
+        if (!optional_attributes->rotate.empty() && optional_attributes_offset >= optional_attributes->rotate.size()) {
+            SVGLength last_rotate;
+            last_rotate = 0.f;
+            for (std::vector<SVGLength>::const_iterator it = optional_attributes->rotate.begin() ; it != optional_attributes->rotate.end() ; ++it)
+                if (it->_set)
+                    last_rotate = *it;
+            new_source->rotate.resize(1, last_rotate);
+        }
     }
     
     _input_stream.push_back(new_source);
@@ -138,7 +158,7 @@ float Layout::InputStreamTextSource::styleComputeFontSize() const
                 }
             }
         }
-        if (this_style->object->parent == NULL) break;
+        if (this_style->object == NULL || this_style->object->parent == NULL) break;
         this_style = this_style->object->parent->style;
         if (this_style == NULL) break;
     }
@@ -167,7 +187,7 @@ Layout::Direction Layout::InputStreamTextSource::styleGetBlockProgression() cons
             return (Layout::Direction)_enum_converter(this_style->block_progression.computed, enum_convert_spstyle_block_progression_to_direction, sizeof(enum_convert_spstyle_block_progression_to_direction)/sizeof(enum_convert_spstyle_block_progression_to_direction[0]));
         if (this_style->writing_mode.set)
             return (Layout::Direction)_enum_converter(this_style->writing_mode.computed, enum_convert_spstyle_writing_mode_to_direction, sizeof(enum_convert_spstyle_writing_mode_to_direction)/sizeof(enum_convert_spstyle_writing_mode_to_direction[0]));
-        if (this_style->object->parent == NULL) break;
+        if (this_style->object == NULL || this_style->object->parent == NULL) break;
         this_style = this_style->object->parent->style;
         if (this_style == NULL) break;
     }
@@ -175,13 +195,13 @@ Layout::Direction Layout::InputStreamTextSource::styleGetBlockProgression() cons
 
 }
 
-static Layout::Alignment text_anchor_to_alignment(unsigned anchor, Layout::Direction para_direction)
+static Layout::Alignment text_anchor_to_alignment(unsigned anchor, Layout::Direction /*para_direction*/)
 {
     switch (anchor) {
         default:
-        case SP_CSS_TEXT_ANCHOR_START:  return para_direction == Layout::LEFT_TO_RIGHT ? Layout::LEFT : Layout::RIGHT;
+        case SP_CSS_TEXT_ANCHOR_START:  return Layout::LEFT;
         case SP_CSS_TEXT_ANCHOR_MIDDLE: return Layout::CENTER;
-        case SP_CSS_TEXT_ANCHOR_END:    return para_direction == Layout::LEFT_TO_RIGHT ? Layout::RIGHT : Layout::LEFT;
+        case SP_CSS_TEXT_ANCHOR_END:    return Layout::RIGHT;
     }
 }
 
@@ -211,7 +231,7 @@ Layout::Alignment Layout::InputStreamTextSource::styleGetAlignment(Layout::Direc
         }
         if (this_style->text_anchor.set)
             return text_anchor_to_alignment(this_style->text_anchor.computed, para_direction);
-        if (this_style->object->parent == NULL) break;
+        if (this_style->object == NULL || this_style->object->parent == NULL) break;
         this_style = this_style->object->parent->style;
         if (this_style == NULL) break;
     }
@@ -224,14 +244,16 @@ static const Layout::EnumConversionItem enum_convert_spstyle_style_to_pango_styl
     {SP_CSS_FONT_STYLE_OBLIQUE, PANGO_STYLE_OBLIQUE}};
 
 static const Layout::EnumConversionItem enum_convert_spstyle_weight_to_pango_weight[] = {
+  // NB: The Pango web page calls 500 "the normal font" but both CSS2 and the Pango
+  // enumeration define 400 as normal.
     {SP_CSS_FONT_WEIGHT_NORMAL, PANGO_WEIGHT_NORMAL},
-    {SP_CSS_FONT_WEIGHT_100, PANGO_WEIGHT_ULTRALIGHT},
+    {SP_CSS_FONT_WEIGHT_BOLD,PANGO_WEIGHT_BOLD},
+    {SP_CSS_FONT_WEIGHT_100, PANGO_WEIGHT_THIN},
     {SP_CSS_FONT_WEIGHT_200, PANGO_WEIGHT_ULTRALIGHT},
     {SP_CSS_FONT_WEIGHT_300, PANGO_WEIGHT_LIGHT},
     {SP_CSS_FONT_WEIGHT_400, PANGO_WEIGHT_NORMAL},
-    {SP_CSS_FONT_WEIGHT_500, PANGO_WEIGHT_SEMIBOLD},
-    {SP_CSS_FONT_WEIGHT_600, PANGO_WEIGHT_BOLD},
-    {SP_CSS_FONT_WEIGHT_BOLD,PANGO_WEIGHT_BOLD},
+    {SP_CSS_FONT_WEIGHT_500, PANGO_WEIGHT_MEDIUM},
+    {SP_CSS_FONT_WEIGHT_600, PANGO_WEIGHT_SEMIBOLD},
     {SP_CSS_FONT_WEIGHT_700, PANGO_WEIGHT_BOLD},
     {SP_CSS_FONT_WEIGHT_800, PANGO_WEIGHT_ULTRABOLD},
     {SP_CSS_FONT_WEIGHT_900, PANGO_WEIGHT_HEAVY}};
@@ -252,13 +274,52 @@ static const Layout::EnumConversionItem enum_convert_spstyle_variant_to_pango_va
     {SP_CSS_FONT_VARIANT_SMALL_CAPS, PANGO_VARIANT_SMALL_CAPS}};
 
 font_instance *Layout::InputStreamTextSource::styleGetFontInstance() const
+{
+    PangoFontDescription *descr = styleGetFontDescription();
+    if (descr == NULL) return NULL;
+    font_instance *res = (font_factory::Default())->Face(descr);
+    pango_font_description_free(descr);
+    return res;
+}
+
+PangoFontDescription *Layout::InputStreamTextSource::styleGetFontDescription() const
 {
     if (style->text == NULL) return NULL;
-    return (font_factory::Default())->Face(style->text->font_family.value,
-                                           _enum_converter(style->font_variant.computed, enum_convert_spstyle_variant_to_pango_variant, sizeof(enum_convert_spstyle_variant_to_pango_variant)/sizeof(enum_convert_spstyle_variant_to_pango_variant[0])),
-                                           _enum_converter(style->font_style.computed,   enum_convert_spstyle_style_to_pango_style,     sizeof(enum_convert_spstyle_style_to_pango_style)/sizeof(enum_convert_spstyle_style_to_pango_style[0])),
-                                           _enum_converter(style->font_weight.computed,  enum_convert_spstyle_weight_to_pango_weight,   sizeof(enum_convert_spstyle_weight_to_pango_weight)/sizeof(enum_convert_spstyle_weight_to_pango_weight[0])),
-                                           _enum_converter(style->font_stretch.computed, enum_convert_spstyle_stretch_to_pango_stretch, sizeof(enum_convert_spstyle_stretch_to_pango_stretch)/sizeof(enum_convert_spstyle_stretch_to_pango_stretch[0])));
+    PangoFontDescription *descr = pango_font_description_new();
+    // Pango can't cope with spaces before or after the commas - let's remove them.
+    // this code is not exactly unicode-safe, but it's similar to what's done in
+    // pango, so it's not the limiting factor
+    Glib::ustring family;
+    if (style->text->font_family.value == NULL) {
+        family = "Sans";
+    } else {
+        gchar **families = g_strsplit(style->text->font_family.value, ",", -1);
+        if (families) {
+            for (gchar **f = families ; *f ; ++f) {
+                g_strstrip(*f);
+                if (!family.empty()) family += ',';
+                family += *f;
+            }
+        }
+        g_strfreev(families);
+    }
+
+    pango_font_description_set_family(descr,family.c_str());
+    pango_font_description_set_weight(descr,(PangoWeight)_enum_converter(style->font_weight.computed,  enum_convert_spstyle_weight_to_pango_weight,   sizeof(enum_convert_spstyle_weight_to_pango_weight)/sizeof(enum_convert_spstyle_weight_to_pango_weight[0])));
+    pango_font_description_set_style(descr,(PangoStyle)_enum_converter(style->font_style.computed,   enum_convert_spstyle_style_to_pango_style,     sizeof(enum_convert_spstyle_style_to_pango_style)/sizeof(enum_convert_spstyle_style_to_pango_style[0])));
+    pango_font_description_set_variant(descr,(PangoVariant)_enum_converter(style->font_variant.computed, enum_convert_spstyle_variant_to_pango_variant, sizeof(enum_convert_spstyle_variant_to_pango_variant)/sizeof(enum_convert_spstyle_variant_to_pango_variant[0])));
+#ifdef USE_PANGO_WIN32
+    // damn Pango fudges the size, so we need to unfudge. See source of pango_win32_font_map_init()
+    pango_font_description_set_size(descr, (int) ((font_factory::Default())->fontSize*PANGO_SCALE*72/GetDeviceCaps(pango_win32_get_dc(),LOGPIXELSY))); // mandatory huge size (hinting workaround)
+    // we don't set stretch on Win32, because pango-win32 has no concept of it
+    // (Windows doesn't really provide any useful field it could use).
+    // If we did set stretch, then any text with a font-stretch attribute would
+    // end up falling back to Arial.
+#else
+    pango_font_description_set_size(descr, (int) ((font_factory::Default())->fontSize*PANGO_SCALE)); // mandatory huge size (hinting workaround)
+    pango_font_description_set_stretch(descr,(PangoStretch)_enum_converter(style->font_stretch.computed, enum_convert_spstyle_stretch_to_pango_stretch, sizeof(enum_convert_spstyle_stretch_to_pango_stretch)/sizeof(enum_convert_spstyle_stretch_to_pango_stretch[0])));
+#endif
+    return descr;
 }
 
 Layout::InputStreamTextSource::~InputStreamTextSource()