summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTavmjong Bah <tavmjong@free.fr>2015-01-05 10:17:51 +0000
committertavmjong-free <tavmjong@free.fr>2015-01-05 10:17:51 +0000
commit374aa610cb550cf7dcd283ca93ad0ad844732d32 (patch)
tree067c340e29085c65fc3bde1b680ecfde1161bae1 /src
parentComment out error message about style ref counting and add explanation. (diff)
downloadinkscape-374aa610cb550cf7dcd283ca93ad0ad844732d32.tar.gz
inkscape-374aa610cb550cf7dcd283ca93ad0ad844732d32.zip
Implement SVG 1.1 textLength and textAdjust attributes. Code from Dmitry Kirsanov (buliabyak).
(bzr r13839)
Diffstat (limited to 'src')
-rw-r--r--src/attributes.cpp2
-rw-r--r--src/attributes.h2
-rw-r--r--src/libnrtype/Layout-TNG-Compute.cpp50
-rw-r--r--src/libnrtype/Layout-TNG-Input.cpp5
-rw-r--r--src/libnrtype/Layout-TNG-OutIter.cpp27
-rw-r--r--src/libnrtype/Layout-TNG-Output.cpp32
-rw-r--r--src/libnrtype/Layout-TNG.cpp9
-rw-r--r--src/libnrtype/Layout-TNG.h47
-rw-r--r--src/sp-text.cpp66
-rw-r--r--src/text-tag-attributes.h11
10 files changed, 217 insertions, 34 deletions
diff --git a/src/attributes.cpp b/src/attributes.cpp
index 2b04843a7..f386832c7 100644
--- a/src/attributes.cpp
+++ b/src/attributes.cpp
@@ -195,6 +195,8 @@ static SPStyleProp const props[] = {
{SP_ATTR_DX, "dx"},
{SP_ATTR_DY, "dy"},
{SP_ATTR_ROTATE, "rotate"},
+ {SP_ATTR_TEXTLENGTH, "textLength"},
+ {SP_ATTR_LENGTHADJUST, "lengthAdjust"},
{SP_ATTR_SODIPODI_ROLE, "sodipodi:role"},
/* SPText */
{SP_ATTR_SODIPODI_LINESPACING, "sodipodi:linespacing"},
diff --git a/src/attributes.h b/src/attributes.h
index 8e091df71..97540bc71 100644
--- a/src/attributes.h
+++ b/src/attributes.h
@@ -196,6 +196,8 @@ enum SPAttributeEnum {
SP_ATTR_DX,
SP_ATTR_DY,
SP_ATTR_ROTATE,
+ SP_ATTR_TEXTLENGTH,
+ SP_ATTR_LENGTHADJUST,
SP_ATTR_SODIPODI_ROLE,
/* SPText */
SP_ATTR_SODIPODI_LINESPACING,
diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp
index bd1acbe46..13f4b9ab2 100644
--- a/src/libnrtype/Layout-TNG-Compute.cpp
+++ b/src/libnrtype/Layout-TNG-Compute.cpp
@@ -362,7 +362,7 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
}
// todo: break between chars if necessary (ie no word breaks present) when doing rectangular flowing
- // sum the glyph widths, letter spacing and word spacing to get the character width
+ // sum the glyph widths, letter spacing, word spacing, and textLength adjustment to get the character width
double char_width = 0.0;
while (span->end_glyph_index < (unsigned)span->end.iter_span->glyph_string->num_glyphs
&& span->end.iter_span->glyph_string->log_clusters[span->end_glyph_index] <= (int)span->end.char_byte) {
@@ -373,9 +373,10 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
span->end_glyph_index++;
}
if (char_attributes.is_cursor_position)
- char_width += text_source->style->letter_spacing.computed;
+ char_width += text_source->style->letter_spacing.computed * _flow.getTextLengthMultiplierDue();
if (char_attributes.is_white)
- char_width += text_source->style->word_spacing.computed;
+ char_width += text_source->style->word_spacing.computed * _flow.getTextLengthMultiplierDue();
+ char_width += _flow.getTextLengthIncrementDue();
span->width += char_width;
IFTRACE(char_count++);
@@ -661,6 +662,14 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
new_glyph.in_character = _flow._characters.size();
new_glyph.rotation = glyph_rotate;
+ // We may have scaled font size to fit textLength; now, if
+ // @lengthAdjust=spacingAndGlyphs, this scaling must be only horizontal,
+ // not vertical, so we unscale it back vertically during output
+ if (_flow.lengthAdjust == Inkscape::Text::Layout::LENGTHADJUST_SPACINGANDGLYPHS)
+ new_glyph.vertical_scale = 1.0 / _flow.getTextLengthMultiplierDue();
+ else
+ new_glyph.vertical_scale = 1.0;
+
/* put something like this back in when we do glyph-rotation-horizontal/vertical
if (new_span.block_progression == LEFT_TO_RIGHT || new_span.block_progression == RIGHT_TO_LEFT) {
new_glyph.x += new_span.line_height.ascent;
@@ -743,9 +752,10 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
new_character.in_glyph = _flow._glyphs.size() - 1;
_flow._characters.push_back(new_character);
if (new_character.char_attributes.is_white)
- advance_width += text_source->style->word_spacing.computed + add_to_each_whitespace; // justification
+ advance_width += text_source->style->word_spacing.computed * _flow.getTextLengthMultiplierDue() + add_to_each_whitespace; // justification
if (new_character.char_attributes.is_cursor_position)
- advance_width += text_source->style->letter_spacing.computed;
+ advance_width += text_source->style->letter_spacing.computed * _flow.getTextLengthMultiplierDue();
+ advance_width += _flow.getTextLengthIncrementDue();
iter_source_text++;
char_index_in_unbroken_span++;
char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base();
@@ -1111,8 +1121,8 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const
UnbrokenSpan new_span;
new_span.pango_item_index = -1;
new_span.input_index = input_index;
- new_span.line_height.ascent = control_code->ascent;
- new_span.line_height.descent = control_code->descent;
+ new_span.line_height.ascent = control_code->ascent * _flow.getTextLengthMultiplierDue();
+ new_span.line_height.descent = control_code->descent * _flow.getTextLengthMultiplierDue();
new_span.line_height.leading = 0.0;
new_span.text_bytes = 0;
new_span.char_index_in_para = char_index_in_para;
@@ -1154,13 +1164,13 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const
if (_block_progression == TOP_TO_BOTTOM || _block_progression == BOTTOM_TO_TOP) {
if (text_source->x.size() > char_index_in_source) new_span.x = text_source->x[char_index_in_source];
if (text_source->y.size() > char_index_in_source) new_span.y = text_source->y[char_index_in_source];
- if (text_source->dx.size() > char_index_in_source) new_span.dx = text_source->dx[char_index_in_source];
- if (text_source->dy.size() > char_index_in_source) new_span.dy = text_source->dy[char_index_in_source];
+ if (text_source->dx.size() > char_index_in_source) new_span.dx = text_source->dx[char_index_in_source].computed * _flow.getTextLengthMultiplierDue();
+ if (text_source->dy.size() > char_index_in_source) new_span.dy = text_source->dy[char_index_in_source].computed * _flow.getTextLengthMultiplierDue();
} else {
if (text_source->x.size() > char_index_in_source) new_span.y = text_source->x[char_index_in_source];
if (text_source->y.size() > char_index_in_source) new_span.x = text_source->y[char_index_in_source];
- if (text_source->dx.size() > char_index_in_source) new_span.dy = text_source->dx[char_index_in_source];
- if (text_source->dy.size() > char_index_in_source) new_span.dx = text_source->dy[char_index_in_source];
+ if (text_source->dx.size() > char_index_in_source) new_span.dy = text_source->dx[char_index_in_source].computed * _flow.getTextLengthMultiplierDue();
+ if (text_source->dy.size() > char_index_in_source) new_span.dx = text_source->dy[char_index_in_source].computed * _flow.getTextLengthMultiplierDue();
}
if (text_source->rotate.size() > char_index_in_source) new_span.rotate = text_source->rotate[char_index_in_source];
else if (char_index_in_source == 0) new_span.rotate = 0.f;
@@ -1189,7 +1199,7 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const
}
// now we know the length, do some final calculations and add the UnbrokenSpan to the list
- new_span.font_size = text_source->styleComputeFontSize();
+ new_span.font_size = text_source->styleComputeFontSize() * _flow.getTextLengthMultiplierDue();
if (new_span.text_bytes) {
new_span.glyph_string = pango_glyph_string_new();
/* Some assertions intended to help diagnose bug #1277746. */
@@ -1556,6 +1566,7 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const &para,
return true;
}
+
/** The management function to start the whole thing off. */
bool Layout::Calculator::calculate()
{
@@ -1690,6 +1701,14 @@ bool Layout::Calculator::calculate()
_flow._input_truncated = true;
}
+ if (_flow.textLength._set) {
+ // Calculate the adjustment needed to meet the textLength
+ double actual_length = _flow.getActualLength();
+ double difference = _flow.textLength.computed - actual_length;
+ _flow.textLengthMultiplier = (actual_length + difference) / actual_length;
+ _flow.textLengthIncrement = difference / (_flow._characters.size() == 1? 1 : _flow._characters.size() - 1);
+ }
+
return true;
}
@@ -1739,7 +1758,12 @@ void Layout::_calculateCursorShapeForEmpty()
bool Layout::calculateFlow()
{
- bool result = Calculator(this).calculate();
+ Layout::Calculator calc = Calculator(this);
+ bool result = calc.calculate();
+ if (textLengthIncrement != 0) {
+ TRACE(("Recalculating layout the second time to fit textLength!\n"));
+ result = calc.calculate();
+ }
if (_characters.empty())
_calculateCursorShapeForEmpty();
return result;
diff --git a/src/libnrtype/Layout-TNG-Input.cpp b/src/libnrtype/Layout-TNG-Input.cpp
index cd9179c5f..84f3f260e 100644
--- a/src/libnrtype/Layout-TNG-Input.cpp
+++ b/src/libnrtype/Layout-TNG-Input.cpp
@@ -72,6 +72,11 @@ void Layout::appendText(Glib::ustring const &text, SPStyle *style, void *source_
last_rotate = *it;
new_source->rotate.resize(1, last_rotate);
}
+ new_source->textLength._set = optional_attributes->textLength._set;
+ new_source->textLength.value = optional_attributes->textLength.value;
+ new_source->textLength.computed = optional_attributes->textLength.computed;
+ new_source->textLength.unit = optional_attributes->textLength.unit;
+ new_source->lengthAdjust = optional_attributes->lengthAdjust;
}
_input_stream.push_back(new_source);
diff --git a/src/libnrtype/Layout-TNG-OutIter.cpp b/src/libnrtype/Layout-TNG-OutIter.cpp
index 6dc42d998..c9c318960 100644
--- a/src/libnrtype/Layout-TNG-OutIter.cpp
+++ b/src/libnrtype/Layout-TNG-OutIter.cpp
@@ -401,15 +401,17 @@ std::vector<Geom::Point> Layout::createSelectionShape(iterator const &it_start,
bottom_right[Geom::X] = span_x + _characters[char_index].x;
double baseline_y = _spans[span_index].line(this).baseline_y + _spans[span_index].baseline_shift;
+ double vertical_scale = _glyphs.back().vertical_scale;
+
if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
- double span_height = _spans[span_index].line_height.ascent + _spans[span_index].line_height.descent;
+ double span_height = vertical_scale * (_spans[span_index].line_height.ascent + _spans[span_index].line_height.descent);
top_left[Geom::Y] = top_left[Geom::X];
top_left[Geom::X] = baseline_y - span_height * 0.5;
bottom_right[Geom::Y] = bottom_right[Geom::X];
bottom_right[Geom::X] = baseline_y + span_height * 0.5;
} else {
- top_left[Geom::Y] = baseline_y - _spans[span_index].line_height.ascent;
- bottom_right[Geom::Y] = baseline_y + _spans[span_index].line_height.descent;
+ top_left[Geom::Y] = baseline_y - vertical_scale * _spans[span_index].line_height.ascent;
+ bottom_right[Geom::Y] = baseline_y + vertical_scale * _spans[span_index].line_height.descent;
}
}
@@ -505,21 +507,23 @@ void Layout::queryCursorShape(iterator const &it, Geom::Point &position, double
position[Geom::Y] = span->line(this).baseline_y + span->baseline_shift;
}
// up to now *position is the baseline point, not the final point which will be the bottom of the descent
+ double vertical_scale = _glyphs.back().vertical_scale;
+
if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
- height = span->line_height.ascent + span->line_height.descent;
+ height = vertical_scale * span->line_height.ascent + span->line_height.descent;
rotation += M_PI / 2;
std::swap(position[Geom::X], position[Geom::Y]);
- position[Geom::X] -= sin(rotation) * height * 0.5;
- position[Geom::Y] += cos(rotation) * height * 0.5;
+ position[Geom::X] -= vertical_scale * sin(rotation) * height * 0.5;
+ position[Geom::Y] += vertical_scale * cos(rotation) * height * 0.5;
} else {
double caret_slope_run = 0.0, caret_slope_rise = 1.0;
if (span->font)
const_cast<font_instance*>(span->font)->FontSlope(caret_slope_run, caret_slope_rise);
double caret_slope = atan2(caret_slope_run, caret_slope_rise);
- height = (span->line_height.ascent + span->line_height.descent) / cos(caret_slope);
+ height = vertical_scale * (span->line_height.ascent + span->line_height.descent) / cos(caret_slope);
rotation += caret_slope;
- position[Geom::X] -= sin(rotation) * span->line_height.descent;
- position[Geom::Y] += cos(rotation) * span->line_height.descent;
+ position[Geom::X] -= sin(rotation) * vertical_scale * span->line_height.descent;
+ position[Geom::Y] += cos(rotation) * vertical_scale * span->line_height.descent;
}
}
}
@@ -601,9 +605,10 @@ void Layout::simulateLayoutUsingKerning(iterator const &from, iterator const &to
if (input_item->Type() == TEXT_SOURCE) {
SPStyle const *style = static_cast<InputStreamTextSource*>(input_item)->style;
if (_characters[char_index].char_attributes.is_white)
- dx -= style->word_spacing.computed;
+ dx -= style->word_spacing.computed * getTextLengthMultiplierDue();
if (_characters[char_index].char_attributes.is_cursor_position)
- dx -= style->letter_spacing.computed;
+ dx -= style->letter_spacing.computed * getTextLengthMultiplierDue();
+ dx -= getTextLengthIncrementDue();
}
if (fabs(dx) > 0.0001) {
diff --git a/src/libnrtype/Layout-TNG-Output.cpp b/src/libnrtype/Layout-TNG-Output.cpp
index 61a141cc3..6e3faf33b 100644
--- a/src/libnrtype/Layout-TNG-Output.cpp
+++ b/src/libnrtype/Layout-TNG-Output.cpp
@@ -108,7 +108,7 @@ void Layout::_getGlyphTransformMatrix(int glyph_index, Geom::Affine *matrix) con
(*matrix)[0] = span.font_size * cos_rotation;
(*matrix)[1] = span.font_size * sin_rotation;
(*matrix)[2] = span.font_size * sin_rotation;
- (*matrix)[3] = -span.font_size * cos_rotation;
+ (*matrix)[3] = -span.font_size * cos_rotation * (_glyphs[glyph_index].vertical_scale); // unscale vertically so the specified text height is preserved if lengthAdjust=spacingAndGlyphs
if (span.block_progression == LEFT_TO_RIGHT || span.block_progression == RIGHT_TO_LEFT) {
(*matrix)[4] = _lines[_chunks[span.in_chunk].in_line].baseline_y + _glyphs[glyph_index].y;
(*matrix)[5] = _chunks[span.in_chunk].left_x + _glyphs[glyph_index].x;
@@ -804,6 +804,36 @@ void Layout::transform(Geom::Affine const &transform)
}
}
+double Layout::getTextLengthIncrementDue() const
+{
+ if (textLength._set && textLengthIncrement != 0 && lengthAdjust == Inkscape::Text::Layout::LENGTHADJUST_SPACING) {
+ return textLengthIncrement;
+ }
+ return 0;
+}
+
+
+double Layout::getTextLengthMultiplierDue() const
+{
+ if (textLength._set && textLengthMultiplier != 1 && (lengthAdjust == Inkscape::Text::Layout::LENGTHADJUST_SPACINGANDGLYPHS)) {
+ return textLengthMultiplier;
+ }
+ return 1;
+}
+
+double Layout::getActualLength() const
+{
+ double length = 0;
+ for (std::vector<Span>::const_iterator it_span = _spans.begin() ; it_span != _spans.end() ; it_span++) {
+ // take x_end of the last span of each chunk
+ if (it_span == _spans.end() - 1 || (it_span + 1)->in_chunk != it_span->in_chunk)
+ length += it_span->x_end;
+ }
+ return length;
+
+
+}
+
}//namespace Text
}//namespace Inkscape
diff --git a/src/libnrtype/Layout-TNG.cpp b/src/libnrtype/Layout-TNG.cpp
index dd9c90949..8b0889188 100644
--- a/src/libnrtype/Layout-TNG.cpp
+++ b/src/libnrtype/Layout-TNG.cpp
@@ -20,6 +20,10 @@ Layout::Layout() :
_input_truncated(0),
_path_fitted(NULL)
{
+ textLength._set = false;
+ textLengthMultiplier = 1;
+ textLengthIncrement = 0;
+ lengthAdjust = LENGTHADJUST_SPACING;
}
Layout::~Layout()
@@ -31,6 +35,11 @@ void Layout::clear()
{
_clearInputObjects();
_clearOutputObjects();
+
+ textLength._set = false;
+ textLengthMultiplier = 1;
+ textLengthIncrement = 0;
+ lengthAdjust = LENGTHADJUST_SPACING;
}
bool Layout::_directions_are_orthogonal(Direction d1, Direction d2)
diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h
index fd5bbfaef..e91c32ebe 100644
--- a/src/libnrtype/Layout-TNG.h
+++ b/src/libnrtype/Layout-TNG.h
@@ -21,6 +21,7 @@
#include <algorithm>
#include <vector>
#include <boost/optional.hpp>
+#include <svg/svg-length.h>
#ifdef HAVE_CAIRO_PDF
namespace Inkscape {
@@ -37,7 +38,6 @@ using Inkscape::Extension::Internal::CairoRenderContext;
class SPStyle;
class Shape;
struct SPPrintContext;
-class SVGLength;
class Path;
class SPCurve;
class font_instance;
@@ -160,6 +160,9 @@ public:
/** Display alignment for shapes. See appendWrapShape(). */
enum DisplayAlign {DISPLAY_ALIGN_BEFORE, DISPLAY_ALIGN_CENTER, DISPLAY_ALIGN_AFTER};
+ /** lengthAdjust values */
+ enum LengthAdjust {LENGTHADJUST_SPACING, LENGTHADJUST_SPACINGANDGLYPHS};
+
/** The optional attributes which can be applied to a SVG text or
related tag. See appendText(). See SVG1.1 section 10.4 for the
definitions of all these members. See sp_svg_length_list_read() for
@@ -172,6 +175,8 @@ public:
std::vector<SVGLength> dx;
std::vector<SVGLength> dy;
std::vector<SVGLength> rotate;
+ SVGLength textLength;
+ LengthAdjust lengthAdjust;
};
/** Control codes which can be embedded in the text to be flowed. See
@@ -290,6 +295,43 @@ public:
//@}
+ // ************************** textLength and friends *************************
+
+ /** Gives the length target of this layout, as given by textLength attribute.
+
+ FIXME: by putting it here we only support @textLength on text and flowRoot, not on any
+ spans inside. For spans, we will need to add markers of start and end of a textLength span
+ into the _input_stream. These spans can nest (SVG 1.1, section 10.5). After a first layout
+ calculation, we would go through the input stream and, for each end of a textLength span,
+ go through its items, choose those where it wasn't yet set by a nested span, calculate
+ their number of characters, divide the length deficit by it, and set set the
+ textLengthMultiplier for those characters only. For now we do this for the entire layout,
+ without dealing with spans.
+ */
+ SVGLength textLength;
+
+ /** How do we meet textLength if specified: by letterspacing or by scaling horizontally */
+ LengthAdjust lengthAdjust;
+
+ /** By how much each character needs to be wider or narrower, using the specified lengthAdjust
+ strategy, for the layout to meet its textLength target. Is set to non-zero after the layout
+ is calculated for the first time, then it is recalculated with each glyph getting its adjustment. */
+ /** This one is used by scaling strategies: each glyph width is multiplied by this */
+ double textLengthMultiplier;
+ /** This one is used by letterspacing strategy: to each glyph width, this is added */
+ double textLengthIncrement;
+
+ /** Get the actual spacing increment if it's due with the current values of above stuff, otherwise 0 */
+ double getTextLengthIncrementDue() const;
+ /** Get the actual scale multiplier if it's due with the current values of above stuff, otherwise 1 */
+ double getTextLengthMultiplierDue() const;
+
+ /** Get actual length of layout, by summing span lengths. For one-line non-flowed text, just
+ the width; for multiline non-flowed, sum of lengths of all lines; for flowed text, sum of
+ scanline widths for all non-last lines plus text width of last line. */
+ double getActualLength() const;
+
+
// ************************** doing the actual flowing *************************
/** \name Processing
@@ -618,6 +660,8 @@ private:
std::vector<SVGLength> dx;
std::vector<SVGLength> dy;
std::vector<SVGLength> rotate;
+ SVGLength textLength;
+ LengthAdjust lengthAdjust;
// a few functions for some of the more complicated style accesses
float styleComputeFontSize() const;
@@ -701,6 +745,7 @@ private:
float y; /// relative to the current line's baseline
float rotation; /// absolute, modulo any object transforms, which we don't know about
float width;
+ float vertical_scale; /// to implement lengthAdjust="spacingAndGlyphs" that must scale glyphs only horizontally; instead we change font size and then undo that change vertically only
inline Span const & span(Layout const *l) const {return l->_spans[l->_characters[in_character].in_span];}
inline Chunk const & chunk(Layout const *l) const {return l->_chunks[l->_spans[l->_characters[in_character].in_span].in_chunk];}
inline Line const & line(Layout const *l) const {return l->_lines[l->_chunks[l->_spans[l->_characters[in_character].in_span].in_chunk].in_line];}
diff --git a/src/sp-text.cpp b/src/sp-text.cpp
index 1cd690729..755a4f769 100644
--- a/src/sp-text.cpp
+++ b/src/sp-text.cpp
@@ -81,6 +81,10 @@ void SPText::build(SPDocument *doc, Inkscape::XML::Node *repr) {
this->readAttr( "dy" );
this->readAttr( "rotate" );
+ // textLength and friends
+ this->readAttr( "textLength" );
+ this->readAttr( "lengthAdjust" );
+
// SVG 2 Auto wrapped text
this->readAttr( "width" );
this->readAttr( "height" );
@@ -403,6 +407,10 @@ Geom::Affine SPText::set_transform(Geom::Affine const &xform) {
}
}
+ // we cannot optimize text with textLength because it may show different size than specified
+ if (this->attributes.getTextLength()->_set)
+ return xform;
+
/* This function takes care of scaling & translation only, we return whatever parts we can't
handle. */
@@ -466,6 +474,13 @@ unsigned SPText::_buildLayoutInput(SPObject *root, Inkscape::Text::Layout::Optio
if (SP_IS_TEXT(root)) {
SP_TEXT(root)->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, true, true);
+ if (SP_TEXT(root)->attributes.getTextLength()->_set) { // set textLength on the entire layout, see note in TNG-Layout.h
+ layout.textLength._set = true;
+ layout.textLength.value = SP_TEXT(root)->attributes.getTextLength()->value;
+ layout.textLength.computed = SP_TEXT(root)->attributes.getTextLength()->computed;
+ layout.textLength.unit = SP_TEXT(root)->attributes.getTextLength()->unit;
+ layout.lengthAdjust = (Inkscape::Text::Layout::LengthAdjust) SP_TEXT(root)->attributes.getLengthAdjust();
+ }
}
else if (SP_IS_TSPAN(root)) {
SPTSpan *tspan = SP_TSPAN(root);
@@ -611,6 +626,8 @@ void TextTagAttributes::readFrom(Inkscape::XML::Node const *node)
readSingleAttribute(SP_ATTR_DX, node->attribute("dx"));
readSingleAttribute(SP_ATTR_DY, node->attribute("dy"));
readSingleAttribute(SP_ATTR_ROTATE, node->attribute("rotate"));
+ readSingleAttribute(SP_ATTR_TEXTLENGTH, node->attribute("textLength"));
+ readSingleAttribute(SP_ATTR_LENGTHADJUST, node->attribute("lengthAdjust"));
}
bool TextTagAttributes::readSingleAttribute(unsigned key, gchar const *value)
@@ -622,6 +639,16 @@ bool TextTagAttributes::readSingleAttribute(unsigned key, gchar const *value)
case SP_ATTR_DX: attr_vector = &attributes.dx; break;
case SP_ATTR_DY: attr_vector = &attributes.dy; break;
case SP_ATTR_ROTATE: attr_vector = &attributes.rotate; break;
+ case SP_ATTR_TEXTLENGTH:
+ attributes.textLength.readOrUnset(value);
+ return true;
+ break;
+ case SP_ATTR_LENGTHADJUST:
+ attributes.lengthAdjust = (value && !strcmp(value, "spacingAndGlyphs")?
+ Inkscape::Text::Layout::LENGTHADJUST_SPACINGANDGLYPHS :
+ Inkscape::Text::Layout::LENGTHADJUST_SPACING); // default is "spacing"
+ return true;
+ break;
default: return false;
}
@@ -632,14 +659,34 @@ bool TextTagAttributes::readSingleAttribute(unsigned key, gchar const *value)
void TextTagAttributes::writeTo(Inkscape::XML::Node *node) const
{
- writeSingleAttribute(node, "x", attributes.x);
- writeSingleAttribute(node, "y", attributes.y);
- writeSingleAttribute(node, "dx", attributes.dx);
- writeSingleAttribute(node, "dy", attributes.dy);
- writeSingleAttribute(node, "rotate", attributes.rotate);
+ writeSingleAttributeVector(node, "x", attributes.x);
+ writeSingleAttributeVector(node, "y", attributes.y);
+ writeSingleAttributeVector(node, "dx", attributes.dx);
+ writeSingleAttributeVector(node, "dy", attributes.dy);
+ writeSingleAttributeVector(node, "rotate", attributes.rotate);
+
+ writeSingleAttributeLength(node, "textLength", attributes.textLength);
+
+ if (attributes.textLength._set) {
+ if (attributes.lengthAdjust == Inkscape::Text::Layout::LENGTHADJUST_SPACING) {
+ node->setAttribute("lengthAdjust", "spacing");
+ } else if (attributes.lengthAdjust == Inkscape::Text::Layout::LENGTHADJUST_SPACINGANDGLYPHS) {
+ node->setAttribute("lengthAdjust", "spacingAndGlyphs");
+ }
+ }
+}
+
+void TextTagAttributes::writeSingleAttributeLength(Inkscape::XML::Node *node, gchar const *key, const SVGLength &length)
+{
+ if (length._set) {
+ gchar single_value_string[32];
+ g_ascii_formatd(single_value_string, sizeof (single_value_string), "%.8g", length.computed);
+ node->setAttribute(key, single_value_string);
+ } else
+ node->setAttribute(key, NULL);
}
-void TextTagAttributes::writeSingleAttribute(Inkscape::XML::Node *node, gchar const *key, std::vector<SVGLength> const &attr_vector)
+void TextTagAttributes::writeSingleAttributeVector(Inkscape::XML::Node *node, gchar const *key, std::vector<SVGLength> const &attr_vector)
{
if (attr_vector.empty())
node->setAttribute(key, NULL);
@@ -697,6 +744,13 @@ void TextTagAttributes::mergeInto(Inkscape::Text::Layout::OptionalTextTagAttrs *
mergeSingleAttribute(&output->dx, parent_attrs.dx, parent_attrs_offset, copy_dxdyrotate ? &attributes.dx : NULL);
mergeSingleAttribute(&output->dy, parent_attrs.dy, parent_attrs_offset, copy_dxdyrotate ? &attributes.dy : NULL);
mergeSingleAttribute(&output->rotate, parent_attrs.rotate, parent_attrs_offset, copy_dxdyrotate ? &attributes.rotate : NULL);
+ if (attributes.textLength._set) { // only from current node, this is not inherited from parent
+ output->textLength.value = attributes.textLength.value;
+ output->textLength.computed = attributes.textLength.computed;
+ output->textLength.unit = attributes.textLength.unit;
+ output->textLength._set = attributes.textLength._set;
+ output->lengthAdjust = attributes.lengthAdjust;
+ }
}
void TextTagAttributes::mergeSingleAttribute(std::vector<SVGLength> *output_list, std::vector<SVGLength> const &parent_list, unsigned parent_offset, std::vector<SVGLength> const *overlay_list)
diff --git a/src/text-tag-attributes.h b/src/text-tag-attributes.h
index d2bfd854e..7a389ed39 100644
--- a/src/text-tag-attributes.h
+++ b/src/text-tag-attributes.h
@@ -54,7 +54,7 @@ public:
that only fields from \a parent_attrs starting at that index will
be used. Basically, the algorithm is that if a child attribute
exists that will be used, otherwise the parent attribute will be used,
- otherwise the vector will end. */
+ otherwise the vector will end. textLength is never merged with parent. */
void mergeInto(Inkscape::Text::Layout::OptionalTextTagAttrs *output, Inkscape::Text::Layout::OptionalTextTagAttrs const &parent_attrs, unsigned parent_attrs_offset, bool copy_xy, bool copy_dxdyrotate) const;
/** Deletes all the values from all the vectors beginning at
@@ -127,6 +127,9 @@ public:
/** Sets the first coordinates in the x and y vectors. */
void setFirstXY(Geom::Point &point);
+ SVGLength *getTextLength() { return &(attributes.textLength); }
+ int getLengthAdjust() { return attributes.lengthAdjust; }
+
private:
/// This holds the actual values.
Inkscape::Text::Layout::OptionalTextTagAttrs attributes;
@@ -134,7 +137,11 @@ private:
/** Does the reverse of readSingleAttribute(), converting a vector<> to
its SVG string representation and writing it in to \a node. Used by
writeTo(). */
- static void writeSingleAttribute(Inkscape::XML::Node *node, gchar const *key, std::vector<SVGLength> const &attr_vector);
+ static void writeSingleAttributeVector(Inkscape::XML::Node *node, gchar const *key, std::vector<SVGLength> const &attr_vector);
+
+ /** Writes a single length value to \a node. Used by
+ writeTo(). */
+ static void writeSingleAttributeLength(Inkscape::XML::Node *node, gchar const *key, const SVGLength &length);
/** Does mergeInto() for one member of #attributes. If \a overlay_list
is NULL then it does a simple copy of parent elements, starting at