From: tavmjong-free Date: Fri, 4 Jun 2010 19:41:52 +0000 (+0200) Subject: Add support for baseline-shift attribute (all possible values). X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=23fd33c63d847126f5d742a18c0eb31681056c8f;p=inkscape.git Add support for baseline-shift attribute (all possible values). Add superscript and subscript support to Text toolbar. Missing icons for superscript and subscript. Known bug: adding a character to a superscript or subscript text string resets baseline-shift attribute. --- diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp index 2225be5ee..2c2a99aee 100644 --- a/src/desktop-style.cpp +++ b/src/desktop-style.cpp @@ -1051,6 +1051,101 @@ objects_query_fontstyle (GSList *objects, SPStyle *style_res) } } +/** + * Write to style_res the baseline numbers. + */ +int +objects_query_baselines (GSList *objects, SPStyle *style_res) +{ + bool different = false; + + // Only baseline-shift at the moment + // We will return: + // If baseline-shift is same for all objects: + // The full baseline-shift data (used for subscripts and superscripts) + // If baseline-shift is different: + // The average baseline-shift (not implemented at the moment as this is complicated June 2010) + SPIBaselineShift old; + old.value = 0.0; + old.computed = 0.0; + + // double baselineshift = 0.0; + bool set = false; + + int texts = 0; + + for (GSList const *i = objects; i != NULL; i = i->next) { + SPObject *obj = SP_OBJECT (i->data); + + if (!SP_IS_TEXT(obj) && !SP_IS_FLOWTEXT(obj) + && !SP_IS_TSPAN(obj) && !SP_IS_TREF(obj) && !SP_IS_TEXTPATH(obj) + && !SP_IS_FLOWDIV(obj) && !SP_IS_FLOWPARA(obj) && !SP_IS_FLOWTSPAN(obj)) + continue; + + SPStyle *style = SP_OBJECT_STYLE (obj); + if (!style) continue; + + texts ++; + + SPIBaselineShift current; + if(style->baseline_shift.set) { + + current.set = style->baseline_shift.set; + current.inherit = style->baseline_shift.inherit; + current.type = style->baseline_shift.type; + current.literal = style->baseline_shift.literal; + current.value = style->baseline_shift.value; + current.computed = style->baseline_shift.computed; + + if( set ) { + if( current.set != old.set || + current.inherit != old.inherit || + current.type != old.type || + current.literal != old.literal || + current.value != old.value || + current.computed != old.computed ) { + // Maybe this needs to be better thought out. + different = true; + } + } + + set = true; + + old.set = current.set; + old.inherit = current.inherit; + old.type = current.type; + old.literal = current.literal; + old.value = current.value; + old.computed = current.computed; + } + } + + if (different || !set ) { + style_res->baseline_shift.set = false; + style_res->baseline_shift.computed = 0.0; + } else { + style_res->baseline_shift.set = old.set; + style_res->baseline_shift.inherit = old.inherit; + style_res->baseline_shift.type = old.type; + style_res->baseline_shift.literal = old.literal; + style_res->baseline_shift.value = old.value; + style_res->baseline_shift.computed = old.computed; + } + + if (texts == 0 || !set) + return QUERY_STYLE_NOTHING; + + if (texts > 1) { + if (different) { + return QUERY_STYLE_MULTIPLE_DIFFERENT; + } else { + return QUERY_STYLE_MULTIPLE_SAME; + } + } else { + return QUERY_STYLE_SINGLE; + } +} + /** * Write to style_res the average font family of objects. */ @@ -1346,6 +1441,8 @@ sp_desktop_query_style_from_list (GSList *list, SPStyle *style, int property) return objects_query_fontstyle (list, style); } else if (property == QUERY_STYLE_PROPERTY_FONTNUMBERS) { return objects_query_fontnumbers (list, style); + } else if (property == QUERY_STYLE_PROPERTY_BASELINES) { + return objects_query_baselines (list, style); } else if (property == QUERY_STYLE_PROPERTY_BLEND) { return objects_query_blend (list, style); diff --git a/src/desktop-style.h b/src/desktop-style.h index 40461da72..e1ca5b3e7 100644 --- a/src/desktop-style.h +++ b/src/desktop-style.h @@ -48,6 +48,7 @@ enum { // which property was queried (add when you need more) QUERY_STYLE_PROPERTY_FONTFAMILY, // font-family QUERY_STYLE_PROPERTY_FONTSTYLE, // font style QUERY_STYLE_PROPERTY_FONTNUMBERS, // size, spacings + QUERY_STYLE_PROPERTY_BASELINES, // baseline-shift QUERY_STYLE_PROPERTY_MASTEROPACITY, // opacity QUERY_STYLE_PROPERTY_BLEND, // blend QUERY_STYLE_PROPERTY_BLUR // blur diff --git a/src/libnrtype/FontInstance.cpp b/src/libnrtype/FontInstance.cpp index b958e899d..bdf700346 100644 --- a/src/libnrtype/FontInstance.cpp +++ b/src/libnrtype/FontInstance.cpp @@ -412,7 +412,7 @@ void font_instance::InitTheFace() SetGraphicsMode(daddy->hScreenDC, GM_COMPATIBLE); SelectObject(daddy->hScreenDC,theFace); #else - theFace=pango_ft2_font_get_face(pFont); + theFace=pango_ft2_font_get_face(pFont); // Deprecated, use pango_fc_font_lock_face() instead if ( theFace ) { FT_Select_Charmap(theFace,ft_encoding_unicode) && FT_Select_Charmap(theFace,ft_encoding_symbol); } @@ -662,6 +662,7 @@ bool font_instance::FontMetrics(double &ascent,double &descent,double &leading) ascent=fabs(otm.otmAscent*scale); descent=fabs(otm.otmDescent*scale); leading=fabs(otm.otmLineGap*scale); + //otmSubscriptSize, otmSubscriptOffset, otmSuperscriptSize, otmSuperscriptOffset, #else if ( theFace->units_per_EM == 0 ) { return false; // bitmap font diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index cad2f9b19..192596ecf 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -124,8 +124,9 @@ class Layout::Calculator unsigned input_index; /// index into Layout::_input_stream Glib::ustring::const_iterator input_stream_first_character; double font_size; - LineHeight line_height; + LineHeight line_height; /// This is not the CSS line-height attribute! double line_height_multiplier; /// calculated from the font-height css property + double baseline_shift; /// calculated from the baseline-shift css property unsigned text_bytes; unsigned char_index_in_para; /// the index of the first character in this span in the paragraph, for looking up char_attributes SVGLength x, y, dx, dy, rotate; // these are reoriented copies of the attributes. We change span when we encounter one. @@ -521,7 +522,7 @@ class Layout::Calculator new_span.in_chunk = _flow._chunks.size() - 1; new_span.line_height = unbroken_span.line_height; new_span.in_input_stream_item = unbroken_span.input_index; - new_span.baseline_shift = _y_offset; + new_span.baseline_shift = 0.0; new_span.block_progression = _block_progression; if ((_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) && (new_span.font = para.pango_items[unbroken_span.pango_item_index].font)) { @@ -604,11 +605,16 @@ class Layout::Calculator if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) { new_glyph.x = x + unbroken_span.glyph_string->glyphs[glyph_index].geometry.x_offset * font_size_multiplier + new_span.line_height.ascent; - new_glyph.y = _y_offset + (unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset - unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * 0.5) * font_size_multiplier; + new_glyph.y = _y_offset - + unbroken_span.baseline_shift + + (unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset - + unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * 0.5) * font_size_multiplier; new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[glyph_index].glyph, true); } else { new_glyph.x = x + unbroken_span.glyph_string->glyphs[glyph_index].geometry.x_offset * font_size_multiplier; - new_glyph.y = _y_offset + unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset * font_size_multiplier; + new_glyph.y = _y_offset - + unbroken_span.baseline_shift + + unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset * font_size_multiplier; new_glyph.width = unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * font_size_multiplier; if ((new_glyph.width == 0) && (para.pango_items[unbroken_span.pango_item_index].font)) new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[glyph_index].glyph, false); @@ -984,6 +990,7 @@ void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font *line_height_multiplier = LINE_HEIGHT_NORMAL * font_size / line_height->total(); } + /** * Split the paragraph into spans. Also call pango_shape() on them. * @@ -1130,6 +1137,11 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const } new_span.pango_item_index = pango_item_index; _computeFontLineHeight(para->pango_items[pango_item_index].font, new_span.font_size, text_source->style, &new_span.line_height, &new_span.line_height_multiplier); + + // At some point we may want to calculate baseline_shift here (to take advantage + // of otm features like superscript baseline), but for now we use style baseline_shift. + new_span.baseline_shift = text_source->style->baseline_shift.computed; + // TODO: metrics for vertical text TRACE(("add text span %d \"%s\"\n", para->unbroken_spans.size(), text_source->text->raw().substr(span_start_byte_in_source, new_span.text_bytes).c_str())); TRACE((" %d glyphs\n", new_span.glyph_string->num_glyphs)); diff --git a/src/sp-text.cpp b/src/sp-text.cpp index 0f21b59d1..bae625f58 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -528,6 +528,8 @@ unsigned SPText::_buildLayoutInput(SPObject *root, Inkscape::Text::Layout::Optio } else if (SP_IS_TSPAN(root)) { SPTSpan *tspan = SP_TSPAN(root); + // x, y attributes are stripped from some tspans as we do our own line layout + // This should be checked carefully, as it can undo line layout in imported SVG files. bool use_xy = !in_textpath && (tspan->role == SP_TSPAN_ROLE_UNSPECIFIED || !tspan->attributes.singleXYCoordinates()); tspan->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, use_xy, true); } diff --git a/src/style.cpp b/src/style.cpp index a05cef252..f26862871 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -88,11 +88,13 @@ static void sp_style_read_itextdecoration(SPITextDecoration *val, gchar const *s static void sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document); static void sp_style_read_ipaint(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document); static void sp_style_read_ifontsize(SPIFontSize *val, gchar const *str); +static void sp_style_read_ibaselineshift(SPIBaselineShift *val, gchar const *str); static void sp_style_read_ifilter(gchar const *str, SPStyle *style, SPDocument *document); static void sp_style_read_penum(SPIEnum *val, Inkscape::XML::Node *repr, gchar const *key, SPStyleEnum const *dict, bool can_explicitly_inherit); static void sp_style_read_plength(SPILength *val, Inkscape::XML::Node *repr, gchar const *key); static void sp_style_read_pfontsize(SPIFontSize *val, Inkscape::XML::Node *repr, gchar const *key); +static void sp_style_read_pbaselineshift(SPIBaselineShift *val, Inkscape::XML::Node *repr, gchar const *key); static void sp_style_read_pfloat(SPIFloat *val, Inkscape::XML::Node *repr, gchar const *key); static gint sp_style_write_ifloat(gchar *p, gint len, gchar const *key, SPIFloat const *val, SPIFloat const *base, guint flags); @@ -102,6 +104,7 @@ static gint sp_style_write_istring(gchar *p, gint len, gchar const *key, SPIStri static gint sp_style_write_ilength(gchar *p, gint len, gchar const *key, SPILength const *val, SPILength const *base, guint flags); static gint sp_style_write_ipaint(gchar *b, gint len, gchar const *key, SPIPaint const *paint, SPIPaint const *base, guint flags); static gint sp_style_write_ifontsize(gchar *p, gint len, gchar const *key, SPIFontSize const *val, SPIFontSize const *base, guint flags); +static gint sp_style_write_ibaselineshift(gchar *p, gint len, gchar const *key, SPIBaselineShift const *val, SPIBaselineShift const *base, guint flags); static gint sp_style_write_ilengthornormal(gchar *p, gint const len, gchar const *const key, SPILengthOrNormal const *const val, SPILengthOrNormal const *const base, guint const flags); static gint sp_style_write_itextdecoration(gchar *p, gint const len, gchar const *const key, SPITextDecoration const *const val, SPITextDecoration const *const base, guint const flags); static gint sp_style_write_ifilter(gchar *b, gint len, gchar const *key, SPIFilter const *filter, SPIFilter const *base, guint flags); @@ -119,6 +122,9 @@ static void sp_style_filter_clear(SPStyle *style); #define SPS_READ_IFONTSIZE_IF_UNSET(v,s) if (!(v)->set) {sp_style_read_ifontsize((v), (s));} #define SPS_READ_PFONTSIZE_IF_UNSET(v,r,k) if (!(v)->set) {sp_style_read_pfontsize((v), (r), (k));} +#define SPS_READ_IBASELINE_SHIFT_IF_UNSET(v,s) if (!(v)->set) {sp_style_read_ibaselineshift((v), (s));} +#define SPS_READ_PBASELINE_SHIFT_IF_UNSET(v,r,k) if (!(v)->set) {sp_style_read_pbaselineshift((v), (r), (k));} + static void sp_style_merge_from_object_stylesheet(SPStyle *, SPObject const *); struct SPStyleEnum { @@ -259,6 +265,13 @@ static SPStyleEnum const enum_writing_mode[] = { {NULL, -1} }; +static SPStyleEnum const enum_baseline_shift[] = { + {"baseline", SP_CSS_BASELINE_SHIFT_BASELINE}, + {"sub", SP_CSS_BASELINE_SHIFT_SUB}, + {"super", SP_CSS_BASELINE_SHIFT_SUPER}, + {NULL, -1} +}; + static SPStyleEnum const enum_visibility[] = { {"hidden", SP_CSS_VISIBILITY_HIDDEN}, {"collapse", SP_CSS_VISIBILITY_COLLAPSE}, @@ -628,6 +641,7 @@ sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr) enum_writing_mode, true); SPS_READ_PENUM_IF_UNSET(&style->text_anchor, repr, "text-anchor", enum_text_anchor, true); + SPS_READ_PBASELINE_SHIFT_IF_UNSET(&style->baseline_shift, repr, "baseline-shift"); /* opacity */ if (!style->opacity.set) { @@ -945,6 +959,9 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val) case SP_PROP_TEXT_ANCHOR: SPS_READ_IENUM_IF_UNSET(&style->text_anchor, val, enum_text_anchor, true); break; + case SP_PROP_BASELINE_SHIFT: + SPS_READ_IBASELINE_SHIFT_IF_UNSET(&style->baseline_shift, val); + break; /* Text (unimplemented) */ case SP_PROP_TEXT_RENDERING: { /* Ignore the hint. */ @@ -955,9 +972,6 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val) case SP_PROP_ALIGNMENT_BASELINE: g_warning("Unimplemented style property SP_PROP_ALIGNMENT_BASELINE: value: %s", val); break; - case SP_PROP_BASELINE_SHIFT: - g_warning("Unimplemented style property SP_PROP_BASELINE_SHIFT: value: %s", val); - break; case SP_PROP_DOMINANT_BASELINE: g_warning("Unimplemented style property SP_PROP_DOMINANT_BASELINE: value: %s", val); break; @@ -1342,6 +1356,48 @@ sp_style_merge_font_size_from_parent(SPIFontSize &child, SPIFontSize const &pare } } +// Some shifts are defined relative to parent. +static void +sp_style_merge_baseline_shift_from_parent(SPIBaselineShift &child, SPIBaselineShift const &parent, + SPIFontSize const &pfont_size) +{ + /* 'baseline-shift' */ + if (!child.set || child.inherit) { + /* Inherit the computed value. Reference: http://www.w3.org/TR/SVG11/styling.html#Inheritance */ + child.computed = parent.computed; // Does this make sense (applying a shift a second time)? + } else if (child.type == SP_BASELINE_SHIFT_LITERAL) { + if( child.literal == SP_CSS_BASELINE_SHIFT_BASELINE ) { + child.computed = 0; // No change + } else if (child.literal == SP_CSS_BASELINE_SHIFT_SUB ) { + // Should use subscript position from font relative to alphabetic baseline + // In mean time use values from OpenOffice and Adobe + child.computed = -0.33 * pfont_size.computed; + } else if (child.literal == SP_CSS_BASELINE_SHIFT_SUPER ) { + // Should use superscript position from font relative to alphabetic baseline + // In mean time use values from OpenOffice and Adobe + child.computed = 0.33 * pfont_size.computed; + } else { + /* Illegal value */ + } + } else if (child.type == SP_BASELINE_SHIFT_PERCENTAGE) { + // Percentage for baseline shift is relative to computed "line-height" + // which is just font-size (see SVG1.1 'font'). + child.computed = pfont_size.computed * child.value; + } else if (child.type == SP_BASELINE_SHIFT_LENGTH) { + switch (child.unit) { + case SP_CSS_UNIT_EM: + child.computed = child.value * pfont_size.computed; + break; + case SP_CSS_UNIT_EX: + child.computed = child.value * 0.5 * pfont_size.computed; + break; + default: + /* No change */ + break; + } + } +} + /** * Sets computed values in \a style, which may involve inheriting from (or in some other way * calculating from) corresponding computed values of \a parent. @@ -1483,6 +1539,10 @@ sp_style_merge_from_parent(SPStyle *const style, SPStyle const *const parent) style->text_anchor.computed = parent->text_anchor.computed; } + /* Baseline Shift... Some shifts are relative to parent. */ + sp_style_merge_baseline_shift_from_parent(style->baseline_shift, parent->baseline_shift, + parent->font_size); + if (style->opacity.inherit) { style->opacity.value = parent->opacity.value; } @@ -2269,6 +2329,8 @@ sp_style_write_string(SPStyle const *const style, guint const flags) p += sp_style_write_ienum(p, c + BMAX - p, "writing-mode", enum_writing_mode, &style->writing_mode, NULL, flags); p += sp_style_write_ienum(p, c + BMAX - p, "text-anchor", enum_text_anchor, &style->text_anchor, NULL, flags); + p += sp_style_write_ibaselineshift(p, c + BMAX - p, "baseline-shift", &style->baseline_shift, NULL, flags); + /// \todo fixme: Per type methods need default flag too (lauris) @@ -2433,6 +2495,7 @@ sp_style_write_difference(SPStyle const *const from, SPStyle const *const to) p += sp_style_write_ienum(p, c + BMAX - p, "writing-mode", enum_writing_mode, &from->writing_mode, &to->writing_mode, SP_STYLE_FLAG_IFDIFF); p += sp_style_write_ienum(p, c + BMAX - p, "text-anchor", enum_text_anchor, &from->text_anchor, &to->text_anchor, SP_STYLE_FLAG_IFDIFF); + p += sp_style_write_ibaselineshift(p, c + BMAX - p, "baseline-shift", &from->baseline_shift, &to->baseline_shift, SP_STYLE_FLAG_IFDIFF); /// \todo fixme: Per type methods need default flag too if (from->opacity.set && from->opacity.value != SP_SCALE24_MAX) { @@ -2650,6 +2713,13 @@ sp_style_clear(SPStyle *style) style->word_spacing.normal = TRUE; style->word_spacing.value = style->word_spacing.computed = 0.0; + style->baseline_shift.set = FALSE; + style->baseline_shift.type = SP_BASELINE_SHIFT_LITERAL; + style->baseline_shift.unit = SP_CSS_UNIT_NONE; + style->baseline_shift.literal = SP_CSS_BASELINE_SHIFT_BASELINE; + style->baseline_shift.value = 0.0; + style->baseline_shift.computed = 0.0; + style->text_transform.set = FALSE; style->text_transform.value = style->text_transform.computed = SP_CSS_TEXT_TRANSFORM_NONE; @@ -2662,7 +2732,6 @@ sp_style_clear(SPStyle *style) style->writing_mode.set = FALSE; style->writing_mode.value = style->writing_mode.computed = SP_CSS_WRITING_MODE_LR_TB; - style->text_anchor.set = FALSE; style->text_anchor.value = style->text_anchor.computed = SP_CSS_TEXT_ANCHOR_START; @@ -3216,6 +3285,7 @@ sp_style_read_ifontsize(SPIFontSize *val, gchar const *str) val->set = TRUE; val->inherit = TRUE; } else if ((*str == 'x') || (*str == 's') || (*str == 'm') || (*str == 'l')) { + // xx-small, x-small, etc. for (unsigned i = 0; enum_font_size[i].key; i++) { if (!strcmp(str, enum_font_size[i].key)) { val->set = TRUE; @@ -3271,6 +3341,45 @@ sp_style_read_ifontsize(SPIFontSize *val, gchar const *str) } +/** + * Set SPIBaselineShift object from string. + */ +static void +sp_style_read_ibaselineshift(SPIBaselineShift *val, gchar const *str) +{ + if (!strcmp(str, "inherit")) { + val->set = TRUE; + val->inherit = TRUE; + } else if ((*str == 'b') || (*str == 's')) { + // baseline or sub or super + for (unsigned i = 0; enum_baseline_shift[i].key; i++) { + if (!strcmp(str, enum_baseline_shift[i].key)) { + val->set = TRUE; + val->inherit = FALSE; + val->type = SP_BASELINE_SHIFT_LITERAL; + val->literal = enum_baseline_shift[i].value; + return; + } + } + /* Invalid */ + return; + } else { + SPILength length; + sp_style_read_ilength(&length, str); + val->set = length.set; + val->inherit = length.inherit; + val->unit = length.unit; + val->value = length.value; + val->computed = length.computed; + if( val->unit == SP_CSS_UNIT_PERCENT ) { + val->type = SP_BASELINE_SHIFT_PERCENTAGE; + } else { + val->type = SP_BASELINE_SHIFT_LENGTH; + } + return; + } +} + /** * Set SPIFilter object from string. @@ -3368,6 +3477,19 @@ sp_style_read_pfontsize(SPIFontSize *val, Inkscape::XML::Node *repr, gchar const } +/** + * Set SPIBaselineShift object from repr attribute. + */ +static void +sp_style_read_pbaselineshift(SPIBaselineShift *val, Inkscape::XML::Node *repr, gchar const *key) +{ + gchar const *str = repr->attribute(key); + if (str) { + sp_style_read_ibaselineshift(val, str); + } +} + + /** * Set SPIFloat object from repr attribute. */ @@ -3828,6 +3950,77 @@ sp_style_write_ifontsize(gchar *p, gint const len, gchar const *key, } +/** + * + */ +static bool +sp_baseline_shift_differ(SPIBaselineShift const *const a, SPIBaselineShift const *const b) +{ + if (a->type != b->type) + return true; + if (a->type == SP_BASELINE_SHIFT_LITERAL ) { + if (a->literal != b->literal) + return true; + } + if (a->type == SP_BASELINE_SHIFT_LENGTH) { + if (a->unit == SP_CSS_UNIT_EM || a->unit == SP_CSS_UNIT_EX ) { + if( a->value != b->value ) + return true; + } else { + if (a->computed != b->computed) + return true; + } + } + if (a->type == SP_BASELINE_SHIFT_PERCENTAGE) { + if (a->value != b->value) + return true; + } + return false; +} + + +/** + * Write SPIBaselineShift object into string. + */ +static gint +sp_style_write_ibaselineshift(gchar *p, gint const len, gchar const *key, + SPIBaselineShift const *const val, SPIBaselineShift const *const base, + guint const flags) +{ + if ((flags & SP_STYLE_FLAG_ALWAYS) + || ((flags & SP_STYLE_FLAG_IFSET) && val->set) + || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set + && (!base->set || sp_baseline_shift_differ(val, base)))) + { + if (val->inherit) { + return g_snprintf(p, len, "%s:inherit;", key); + } else if (val->type == SP_BASELINE_SHIFT_LITERAL) { + for (unsigned i = 0; enum_baseline_shift[i].key; i++) { + if (enum_baseline_shift[i].value == static_cast< gint > (val->value) ) { + return g_snprintf(p, len, "%s:%s;", key, enum_baseline_shift[i].key); + } + } + } else if (val->type == SP_BASELINE_SHIFT_LENGTH) { + if( val->unit == SP_CSS_UNIT_EM || val->unit == SP_CSS_UNIT_EX ) { + Inkscape::CSSOStringStream os; + os << key << ":" << val->value << (val->unit == SP_CSS_UNIT_EM ? "em;" : "ex;"); + return g_strlcpy(p, os.str().c_str(), len); + } else { + Inkscape::CSSOStringStream os; + os << key << ":" << val->computed << "px;"; // must specify px, see inkscape bug 1221626, mozilla bug 234789 + return g_strlcpy(p, os.str().c_str(), len); + } + } else if (val->type == SP_BASELINE_SHIFT_PERCENTAGE) { + Inkscape::CSSOStringStream os; + os << key << ":" << (val->value * 100.0) << "%;"; + return g_strlcpy(p, os.str().c_str(), len); + } + } + return 0; +} + + + /** * Write SPIFilter object into string. */ @@ -4052,7 +4245,7 @@ sp_css_attr_unset_text(SPCSSAttr *css) sp_repr_css_set_property(css, "kerning", NULL); // not implemented yet sp_repr_css_set_property(css, "dominant-baseline", NULL); // not implemented yet sp_repr_css_set_property(css, "alignment-baseline", NULL); // not implemented yet - sp_repr_css_set_property(css, "baseline-shift", NULL); // not implemented yet + sp_repr_css_set_property(css, "baseline-shift", NULL); return css; } diff --git a/src/style.h b/src/style.h index f1b5ec534..02ef41bf8 100644 --- a/src/style.h +++ b/src/style.h @@ -43,6 +43,7 @@ class SPIString; class SPILength; class SPIPaint; class SPIFontSize; +class SPIBaselineShift; /// Float type internal to SPStyle. struct SPIFloat { @@ -190,6 +191,12 @@ enum { SP_FONT_SIZE_PERCENTAGE }; +enum { + SP_BASELINE_SHIFT_LITERAL, + SP_BASELINE_SHIFT_LENGTH, + SP_BASELINE_SHIFT_PERCENTAGE +}; + #define SP_FONT_SIZE ((1 << 24) - 1) #define SP_F8_16_TO_FLOAT(v) ((gdouble) (v) / (1 << 16)) @@ -208,6 +215,17 @@ struct SPIFontSize { float computed; }; +/// Baseline shift type internal to SPStyle. +struct SPIBaselineShift { + unsigned set : 1; + unsigned inherit : 1; + unsigned type : 2; + unsigned unit : 4; + unsigned literal: 2; + float value; // Can be negative + float computed; +}; + /// Text decoration type internal to SPStyle. struct SPITextDecoration { unsigned set : 1; @@ -287,6 +305,8 @@ struct SPStyle { SPIEnum block_progression; /** Writing mode (css3 text 3.2 and svg1.1 10.7.2) */ SPIEnum writing_mode; + /** Baseline shift (svg1.1 10.9.2) */ + SPIBaselineShift baseline_shift; /* SVG */ /** Anchor of the text (svg1.1 10.9.1) */ @@ -500,6 +520,12 @@ enum SPTextAnchor { SP_CSS_TEXT_ANCHOR_END }; +enum SPCSSBaselineShift { + SP_CSS_BASELINE_SHIFT_BASELINE, + SP_CSS_BASELINE_SHIFT_SUB, + SP_CSS_BASELINE_SHIFT_SUPER +}; + enum SPVisibility { SP_CSS_VISIBILITY_HIDDEN, SP_CSS_VISIBILITY_COLLAPSE, diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index f3cfe621d..502de27df 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -489,6 +489,9 @@ static gchar const * ui_descr = " " " " " " + " " + " " + " " " " " " " " @@ -6610,6 +6613,79 @@ static void sp_text_style_changed( InkToggleAction* act, GObject *tbl ) g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); } +// Handles both Superscripts and Subscripts +static void sp_text_script_changed( InkToggleAction* act, GObject *tbl ) +{ + // quit if run by the _changed callbacks + if (g_object_get_data(G_OBJECT(tbl), "freeze")) { + return; + } + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); + + // Called by Superscript or Subscript button? + const gchar* name = gtk_action_get_name( GTK_ACTION( act ) ); + gint prop = (strcmp(name, "TextSuperscriptAction") == 0) ? 0 : 1; + +#ifdef DEBUG_TEXT + std::cout << "sp_text_script_changed: " << prop << std::endl; +#endif + + // Query baseline + SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT); + int result_baseline = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_BASELINES); + + bool setSuper = false; + bool setSub = false; + + if(result_baseline == QUERY_STYLE_NOTHING || result_baseline == QUERY_STYLE_MULTIPLE_DIFFERENT ) { + // If not set or mixed, turn on superscript or subscript + if( prop == 0 ) { + setSuper = true; + } else { + setSub = true; + } + } else { + // Superscript + gboolean superscriptSet = (query->baseline_shift.set && + query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL && + query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUPER ); + + // Subscript + gboolean subscriptSet = (query->baseline_shift.set && + query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL && + query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUB ); + + setSuper = !superscriptSet && prop == 0; + setSub = !subscriptSet && prop == 1; + } + + // Set css properties + SPCSSAttr *css = sp_repr_css_attr_new (); + if( setSuper || setSub ) { + // Openoffice 2.3 and Adobe use 58%, Microsoft Word 2002 uses 65%. + sp_repr_css_set_property (css, "font-size", "58%"); + } else { + sp_repr_css_set_property (css, "font-size", ""); + } + if( setSuper ) { + sp_repr_css_set_property (css, "baseline-shift", "super"); + } else if( setSub ) { + sp_repr_css_set_property (css, "baseline-shift", "sub"); + } else { + sp_repr_css_set_property (css, "baseline-shift", "baseline"); + } + + // Apply css to selected objects. + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + sp_desktop_set_style (desktop, css, true, true); + + // Save for undo + sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:script", SP_VERB_NONE, + _("Text: Change superscript or subscript")); + + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); +} + static void sp_text_align_mode_changed( EgeSelectOneAction *act, GObject *tbl ) { // quit if run by the _changed callbacks @@ -7090,6 +7166,8 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY); int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE); int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS); + int result_baseline = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_BASELINES); + // Used later: sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION); @@ -7161,8 +7239,32 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ InkToggleAction* textItalicAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextItalicAction" ) ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(textItalicAction), italicSet ); - EgeSelectOneAction* textAlignAction = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextAlignAction" ) ); + + // Superscript + gboolean superscriptSet = + ((result_baseline == QUERY_STYLE_SINGLE || result_baseline == QUERY_STYLE_MULTIPLE_SAME ) && + query->baseline_shift.set && + query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL && + query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUPER ); + + InkToggleAction* textSuperscriptAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextSuperscriptAction" ) ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(textSuperscriptAction), superscriptSet ); + + + // Subscript + gboolean subscriptSet = + ((result_baseline == QUERY_STYLE_SINGLE || result_baseline == QUERY_STYLE_MULTIPLE_SAME ) && + query->baseline_shift.set && + query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL && + query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUB ); + + InkToggleAction* textSubscriptAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextSubscriptAction" ) ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(textSubscriptAction), subscriptSet ); + + // Alignment + EgeSelectOneAction* textAlignAction = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextAlignAction" ) ); + // Note: SVG 1.1 doesn't include text-align, SVG 1.2 Tiny doesn't include text-align="justify" // text-align="justify" was a draft SVG 1.2 item (along with flowed text). // Only flowed text can be left and right justified at the same time. @@ -7432,6 +7534,32 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions g_object_set_data( holder, "TextItalicAction", act ); } + /* Style - Superscript */ + { + InkToggleAction* act = ink_toggle_action_new( "TextSuperscriptAction", // Name + _("Toggle Superscript"), // Label + _("Toggle superscript"), // Tooltip + GTK_STOCK_ITALIC, // Icon (inkId) + secondarySize ); // Icon size + gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_script_changed), holder ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/super", false) ); + g_object_set_data( holder, "TextSuperscriptAction", act ); + } + + /* Style - Subscript */ + { + InkToggleAction* act = ink_toggle_action_new( "TextSubscriptAction", // Name + _("Toggle Subscript"), // Label + _("Toggle subscript"), // Tooltip + GTK_STOCK_ITALIC, // Icon (inkId) + secondarySize ); // Icon size + gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_script_changed), holder ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/sub", false) ); + g_object_set_data( holder, "TextSubscriptAction", act ); + } + /* Alignment */ { GtkListStore* model = gtk_list_store_new( 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN );