summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTavmjong Bah <tavmjong@free.fr>2015-10-28 13:40:01 +0000
committertavmjong-free <tavmjong@free.fr>2015-10-28 13:40:01 +0000
commitce697d7ebfb1e4affce10805c89445244d29388a (patch)
treebc5440dfa84592e6e51e835e2c3e3a13f82efb76 /src
parentadd gtk3 experimental support in CMake (diff)
downloadinkscape-ce697d7ebfb1e4affce10805c89445244d29388a.tar.gz
inkscape-ce697d7ebfb1e4affce10805c89445244d29388a.zip
Implement 'text-orientation' with user interface. Update 'writing-mode', adding 'vertical-lr'.
Overhaul vertical text. Eliminate any use of "internal" leading in glyph metrics. Etc. (bzr r14430.1.1)
Diffstat (limited to 'src')
-rw-r--r--src/desktop-style.cpp62
-rw-r--r--src/desktop-style.h1
-rw-r--r--src/display/nr-style.cpp2
-rw-r--r--src/display/nr-style.h1
-rw-r--r--src/libnrtype/FontInstance.cpp25
-rw-r--r--src/libnrtype/Layout-TNG-Compute.cpp284
-rw-r--r--src/libnrtype/Layout-TNG-OutIter.cpp14
-rw-r--r--src/libnrtype/Layout-TNG-Output.cpp27
-rw-r--r--src/libnrtype/Layout-TNG-Scanline-Maker.h14
-rw-r--r--src/libnrtype/Layout-TNG-Scanline-Makers.cpp32
-rw-r--r--src/libnrtype/Layout-TNG.h51
-rw-r--r--src/style-enums.h8
-rw-r--r--src/style-internal.h1
-rw-r--r--src/widgets/text-toolbar.cpp179
-rw-r--r--src/widgets/toolbox.cpp2
15 files changed, 507 insertions, 196 deletions
diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp
index 02c18339b..2260d851f 100644
--- a/src/desktop-style.cpp
+++ b/src/desktop-style.cpp
@@ -500,7 +500,7 @@ objects_query_fillstroke (const std::vector<SPItem*> &objects, SPStyle *style_re
SPIPaint *paint_res = isfill? &style_res->fill : &style_res->stroke;
bool paintImpossible = true;
- paint_res->set = TRUE;
+ paint_res->set = true;
SVGICCColor* iccColor = 0;
@@ -1144,7 +1144,7 @@ objects_query_fontstyle (const std::vector<SPItem*> &objects, SPStyle *style_res
different = true; // different styles
}
- set = TRUE;
+ set = true;
style_res->font_weight.value = style_res->font_weight.computed = style->font_weight.computed;
style_res->font_style.value = style_res->font_style.computed = style->font_style.computed;
style_res->font_stretch.value = style_res->font_stretch.computed = style->font_stretch.computed;
@@ -1256,6 +1256,56 @@ objects_query_fontvariants (const std::vector<SPItem*> &objects, SPStyle *style_
}
+/**
+ * Write to style_res the average writing modes style of objects.
+ */
+int
+objects_query_writing_modes (const std::vector<SPItem*> &objects, SPStyle *style_res)
+{
+ bool different = false;
+ bool set = false;
+
+ int texts = 0;
+
+ for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) {
+ SPObject *obj = *i;
+
+ if (!isTextualItem(obj)) {
+ continue;
+ }
+
+ SPStyle *style = obj->style;
+ if (!style) {
+ continue;
+ }
+
+ texts ++;
+
+ if (set &&
+ ( ( style_res->writing_mode.computed != style->writing_mode.computed ) ||
+ ( style_res->text_orientation.computed != style->text_orientation.computed ) ) ) {
+ different = true; // different styles
+ }
+
+ set = true;
+ style_res->writing_mode.computed = style->writing_mode.computed;
+ style_res->text_orientation.computed = style->text_orientation.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;
+ }
+}
+
int
objects_query_fontfeaturesettings (const std::vector<SPItem*> &objects, SPStyle *style_res)
{
@@ -1293,7 +1343,7 @@ objects_query_fontfeaturesettings (const std::vector<SPItem*> &objects, SPStyle
style_res->font_feature_settings.value = NULL;
}
- style_res->font_feature_settings.set = TRUE;
+ style_res->font_feature_settings.set = true;
style_res->font_feature_settings.value = g_strdup(style->font_feature_settings.value);
}
@@ -1449,7 +1499,7 @@ objects_query_fontfamily (const std::vector<SPItem*> &objects, SPStyle *style_re
style_res->font_family.value = NULL;
}
- style_res->font_family.set = TRUE;
+ style_res->font_family.set = true;
style_res->font_family.value = g_strdup(style->font_family.value);
}
@@ -1508,7 +1558,7 @@ objects_query_fontspecification (const std::vector<SPItem*> &objects, SPStyle *s
style_res->font_specification.value = NULL;
}
- style_res->font_specification.set = TRUE;
+ style_res->font_specification.set = true;
style_res->font_specification.value = g_strdup(style->font_specification.value);
}
}
@@ -1728,6 +1778,8 @@ sp_desktop_query_style_from_list (const std::vector<SPItem*> &list, SPStyle *sty
return objects_query_fontfeaturesettings (list, style);
} else if (property == QUERY_STYLE_PROPERTY_FONTNUMBERS) {
return objects_query_fontnumbers (list, style);
+ } else if (property == QUERY_STYLE_PROPERTY_WRITINGMODES) {
+ return objects_query_writing_modes (list, style);
} else if (property == QUERY_STYLE_PROPERTY_BASELINES) {
return objects_query_baselines (list, style);
diff --git a/src/desktop-style.h b/src/desktop-style.h
index 95c434844..b9199b615 100644
--- a/src/desktop-style.h
+++ b/src/desktop-style.h
@@ -51,6 +51,7 @@ enum { // which property was queried (add when you need more)
QUERY_STYLE_PROPERTY_FONTFEATURESETTINGS, // font feature settings (OpenType features)
QUERY_STYLE_PROPERTY_FONTNUMBERS, // size, spacings
QUERY_STYLE_PROPERTY_BASELINES, // baseline-shift
+ QUERY_STYLE_PROPERTY_WRITINGMODES, // writing-mode, text-orientation
QUERY_STYLE_PROPERTY_MASTEROPACITY, // opacity
QUERY_STYLE_PROPERTY_BLEND, // blend
QUERY_STYLE_PROPERTY_BLUR // blur
diff --git a/src/display/nr-style.cpp b/src/display/nr-style.cpp
index 1740785e2..8b82a1dff 100644
--- a/src/display/nr-style.cpp
+++ b/src/display/nr-style.cpp
@@ -68,7 +68,6 @@ NRStyle::NRStyle()
, tspan_width(0)
, ascender(0)
, descender(0)
- , line_gap(0)
, underline_thickness(0)
, underline_position(0)
, line_through_thickness(0)
@@ -330,7 +329,6 @@ void NRStyle::set(SPStyle *style, SPStyle *context_style)
tspan_width = style->text_decoration_data.tspan_width;
ascender = style->text_decoration_data.ascender;
descender = style->text_decoration_data.descender;
- line_gap = style->text_decoration_data.line_gap;
underline_thickness = style->text_decoration_data.underline_thickness;
underline_position = style->text_decoration_data.underline_position;
line_through_thickness = style->text_decoration_data.line_through_thickness;
diff --git a/src/display/nr-style.h b/src/display/nr-style.h
index 5f78795d3..6c652311a 100644
--- a/src/display/nr-style.h
+++ b/src/display/nr-style.h
@@ -115,7 +115,6 @@ struct NRStyle {
float tspan_width;
float ascender;
float descender;
- float line_gap;
float underline_thickness;
float underline_position;
float line_through_thickness;
diff --git a/src/libnrtype/FontInstance.cpp b/src/libnrtype/FontInstance.cpp
index a5572c517..6b2e030d1 100644
--- a/src/libnrtype/FontInstance.cpp
+++ b/src/libnrtype/FontInstance.cpp
@@ -374,10 +374,10 @@ void font_instance::LoadGlyph(int glyph_id)
GLYPHMETRICS metrics;
DWORD bufferSize=GetGlyphOutline (parent->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity);
double scale=1.0/parent->fontSize;
- n_g.h_advance=metrics.gmCellIncX*scale;
- n_g.v_advance=otm.otmTextMetrics.tmHeight*scale;
- n_g.h_width=metrics.gmBlackBoxX*scale;
- n_g.v_width=metrics.gmBlackBoxY*scale;
+ n_g.h_advance = metrics.gmCellIncX * scale;
+ n_g.v_advance = otm.otmTextMetrics.tmHeight * scale;
+ n_g.h_width = metrics.gmBlackBoxX * scale;
+ n_g.v_width = metrics.gmBlackBoxY * scale;
if ( bufferSize == GDI_ERROR) {
// shit happened
} else if ( bufferSize == 0) {
@@ -459,7 +459,13 @@ void font_instance::LoadGlyph(int glyph_id)
n_g.v_advance=((double)theFace->glyph->metrics.vertAdvance)/((double)theFace->units_per_EM);
n_g.v_width=((double)theFace->glyph->metrics.height)/((double)theFace->units_per_EM);
} else {
- n_g.v_width=n_g.v_advance=((double)theFace->height)/((double)theFace->units_per_EM);
+ // CSS3 Writing modes dictates that if vertical font metrics are missing we must
+ // synthisize them. No method is specified. The SVG 1.1 spec suggests using the em
+ // height (which is not theFace->height as that includes leading). The em height
+ // is ascender + descender (descender positive). Note: The "Requirements for
+ // Japanese Text Layout" W3C document says that Japanese kanji should be "set
+ // solid" which implies that vertical (and horizontal) advance should be 1em.
+ n_g.v_width=n_g.v_advance= 1.0;
}
if ( theFace->glyph->format == ft_glyph_format_outline ) {
FT_Outline_Funcs ft2_outline_funcs = {
@@ -529,6 +535,15 @@ bool font_instance::FontMetrics(double &ascent,double &descent,double &leading)
leading=fabs(((double)theFace->height)/((double)theFace->units_per_EM));
leading-=ascent+descent;
#endif
+
+ // CSS dictates em size is ascent + descent
+ double em = ascent + descent;
+ if( em <= 0 ) {
+ return false; // Pathological
+ }
+ ascent /= em;
+ descent /= em;
+ leading /= em;
return true;
}
diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp
index c4b0a5bee..4201d052c 100644
--- a/src/libnrtype/Layout-TNG-Compute.cpp
+++ b/src/libnrtype/Layout-TNG-Compute.cpp
@@ -23,33 +23,6 @@ namespace Text {
#define TRACE(_args) IFTRACE(g_print _args)
-// ******* enum conversion tables
-
-// These enums are probably from the SVG 1.0 era where one could interpret 'writing-mode' as setting direction.
-// SVG 1.1 makes it clear that 'direction' should be used. 'direction' has only two values 'ltr' and 'rtl'.
-// The first two values for the 'writing-mode' enum just happen to match the first two value of 'direction' so the
-// existing code worked when 'writing-mode' was changed to 'direction'.
-// static Layout::EnumConversionItem const enum_convert_spstyle_direction_to_pango_direction[] = {
-// {SP_CSS_WRITING_MODE_LR_TB, PANGO_DIRECTION_LTR},
-// {SP_CSS_WRITING_MODE_RL_TB, PANGO_DIRECTION_RTL},
-// {SP_CSS_WRITING_MODE_TB_LR, PANGO_DIRECTION_LTR}}; // this is correct
-
-// static Layout::EnumConversionItem const enum_convert_spstyle_direction_to_my_direction[] = {
-// {SP_CSS_WRITING_MODE_LR_TB, Layout::LEFT_TO_RIGHT},
-// {SP_CSS_WRITING_MODE_RL_TB, Layout::RIGHT_TO_LEFT},
-// {SP_CSS_WRITING_MODE_TB_LR, Layout::LEFT_TO_RIGHT}}; // this is correct
-
-// Proper 'direction' enums
-static Layout::EnumConversionItem const enum_convert_spstyle_direction_to_pango_direction[] = {
- {SP_CSS_DIRECTION_LTR, PANGO_DIRECTION_LTR},
- {SP_CSS_DIRECTION_RTL, PANGO_DIRECTION_RTL}};
-
-static Layout::EnumConversionItem const enum_convert_spstyle_direction_to_my_direction[] = {
- {SP_CSS_DIRECTION_LTR, Layout::LEFT_TO_RIGHT},
- {SP_CSS_DIRECTION_RTL, Layout::RIGHT_TO_LEFT}};
-
-
-
/** \brief private to Layout. Does the real work of text flowing.
This class does a standard greedy paragraph wrapping algorithm.
@@ -140,9 +113,10 @@ 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; /// This is not the CSS line-height attribute!
+ FontMetrics 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
+ SPCSSTextOrientation text_orientation;
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 <tspan> attributes. We change span when we encounter one.
@@ -221,7 +195,7 @@ class Layout::Calculator
void _buildPangoItemizationForPara(ParagraphInfo *para) const;
static void _computeFontLineHeight(font_instance *font, double font_size,
- SPStyle const *style, LineHeight *line_height,
+ SPStyle const *style, FontMetrics *line_height,
double *line_height_multiplier);
unsigned _buildSpansForPara(ParagraphInfo *para) const;
@@ -270,7 +244,7 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
bool _goToNextWrapShape();
bool _findChunksForLine(ParagraphInfo const &para, UnbrokenSpanPosition *start_span_pos,
- std::vector<ChunkInfo> *chunk_info, LineHeight *line_height);
+ std::vector<ChunkInfo> *chunk_info, FontMetrics *line_height);
static inline PangoLogAttr const &_charAttributes(ParagraphInfo const &para,
UnbrokenSpanPosition const &span_pos)
@@ -282,7 +256,7 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
UnbrokenSpanPosition const &start_span_pos,
ScanlineMaker::ScanRun const &scan_run,
std::vector<ChunkInfo> *chunk_info,
- LineHeight *line_height) const;
+ FontMetrics *line_height) const;
/** computes the width of a single UnbrokenSpan (pointed to by span->start.iter_span)
and outputs its vital statistics into the other fields of \a span.
@@ -300,8 +274,11 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
span->setZero();
if (span->start.iter_span->dx._set && span->start.char_byte == 0){
- if(para.direction == RIGHT_TO_LEFT){ span->width -= span->start.iter_span->dx.computed; }
- else { span->width += span->start.iter_span->dx.computed; }
+ if(para.direction == RIGHT_TO_LEFT){
+ span->width -= span->start.iter_span->dx.computed;
+ } else {
+ span->width += span->start.iter_span->dx.computed;
+ }
}
if (span->start.iter_span->pango_item_index == -1) {
@@ -382,10 +359,22 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
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) {
- if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT)
- char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, true);
- else
+ if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) {
+ // Vertical text
+
+ if( text_source->style->text_orientation.computed == SP_CSS_TEXT_ORIENTATION_SIDEWAYS ||
+ (text_source->style->text_orientation.computed == SP_CSS_TEXT_ORIENTATION_MIXED &&
+ para.pango_items[span->end.iter_span->pango_item_index].item->analysis.gravity == 0) ) {
+ // Sideways orientation
+ char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, false);
+ } else {
+ // Upright orientation
+ char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, true);
+ }
+ } else {
+ // Horizontal text
char_width += font_size_multiplier * span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].geometry.width;
+ }
span->end_glyph_index++;
}
if (char_attributes.is_cursor_position)
@@ -474,7 +463,7 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
are ready to output the final result to #_flow. This method takes its
input parameters and does that.
*/
- void _outputLine(ParagraphInfo const &para, LineHeight const &line_height, std::vector<ChunkInfo> const &chunk_info)
+ void _outputLine(ParagraphInfo const &para, FontMetrics const &line_height, std::vector<ChunkInfo> const &chunk_info)
{
TRACE(("Start _outputLine\n"));
if (chunk_info.empty()) {
@@ -486,7 +475,17 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
TRACE(("found line fit; creating output\n"));
Layout::Line new_line;
new_line.in_paragraph = _flow._paragraphs.size() - 1;
- new_line.baseline_y = _scanline_maker->yCoordinate() + line_height.ascent;
+ new_line.baseline_y = _scanline_maker->yCoordinate();
+ if( !_flow._input_wrap_shapes.empty() ) {
+ // Flowed text
+ if( _block_progression == RIGHT_TO_LEFT || _block_progression == LEFT_TO_RIGHT ) {
+ // Vertical text, use em box center as baseline
+ new_line.baseline_y += 0.5 * line_height.emSize();
+ } else {
+ new_line.baseline_y += line_height.getAscent();
+ }
+ }
+
new_line.in_shape = _current_shape_index;
_flow._lines.push_back(new_line);
@@ -527,17 +526,16 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
// If <tspan> "y" attribute is set, use it (initial "y" attributes in
// <tspans> other than the first have already been stripped for <tspans>
// marked with role="line", see sp-text.cpp: SPText::_buildLayoutInput).
+ // NOTE: for vertical text, "y" is the user-space "x" value.
if( it_chunk->broken_spans.front().start.iter_span->y._set ) {
// Use set "y" attribute
new_line.baseline_y = it_chunk->broken_spans.front().start.iter_span->y.computed;
-
// Save baseline
_flow._lines.back().baseline_y = new_line.baseline_y;
- // Save new <tspan> y coordinate
- _scanline_maker->setNewYCoordinate(new_line.baseline_y - line_height.ascent);
-
+ // Set the initial y coordinate of the next line.
+ _scanline_maker->setNewYCoordinate(new_line.baseline_y);
}
// Reset relative y_offset ("dy" attribute is relative but should be reset at
@@ -556,21 +554,21 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
}
_flow._chunks.push_back(new_chunk);
- double x;
+ double current_x;
double direction_sign;
Direction previous_direction = para.direction;
double counter_directional_width_remaining = 0.0;
float glyph_rotate = 0.0;
if (para.direction == LEFT_TO_RIGHT) {
direction_sign = +1.0;
- x = 0.0;
+ current_x = 0.0;
} else {
direction_sign = -1.0;
if (para.alignment == FULL && !_flow._input_wrap_shapes.empty()){
- x = it_chunk->scanrun_width;
+ current_x = it_chunk->scanrun_width;
}
else {
- x = it_chunk->text_width;
+ current_x = it_chunk->text_width;
}
}
@@ -583,7 +581,7 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
if (it_span->start.char_byte == 0) {
// Start of an unbroken span, we might have dx, dy or rotate still to process
// (x and y are done per chunk)
- if (unbroken_span.dx._set) x += unbroken_span.dx.computed;
+ if (unbroken_span.dx._set) current_x += unbroken_span.dx.computed;
if (unbroken_span.dy._set) _y_offset += unbroken_span.dy.computed;
if (unbroken_span.rotate._set) glyph_rotate = unbroken_span.rotate.computed * (M_PI/180);
}
@@ -601,6 +599,7 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
new_span.in_input_stream_item = unbroken_span.input_index;
new_span.baseline_shift = 0.0;
new_span.block_progression = _block_progression;
+ new_span.text_orientation = unbroken_span.text_orientation;
if ((_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) && (new_span.font = para.pango_items[unbroken_span.pango_item_index].font))
{
new_span.font->Ref();
@@ -609,12 +608,12 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
new_span.input_stream_first_character = Glib::ustring::const_iterator(unbroken_span.input_stream_first_character.base() + it_span->start.char_byte);
} else { // a control code
new_span.font = NULL;
- new_span.font_size = new_span.line_height.ascent + new_span.line_height.descent;
+ new_span.font_size = new_span.line_height.emSize();
new_span.direction = para.direction;
}
if (new_span.direction == para.direction) {
- x -= counter_directional_width_remaining;
+ current_x -= counter_directional_width_remaining;
counter_directional_width_remaining = 0.0;
} else if (new_span.direction != previous_direction) {
// measure width of spans we need to switch round
@@ -630,10 +629,10 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
}
counter_directional_width_remaining += direction_sign * (it_following_span->width + it_following_span->whitespace_count * add_to_each_whitespace);
}
- x += counter_directional_width_remaining;
+ current_x += counter_directional_width_remaining;
counter_directional_width_remaining = 0.0; // we want to go increasingly negative
}
- new_span.x_start = x;
+ new_span.x_start = current_x;
if (_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) {
// the span is set up, push the glyphs and chars
@@ -673,10 +672,12 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
}
// create the Layout::Glyph
+ PangoGlyphInfo *unbroken_span_glyph_info = &unbroken_span.glyph_string->glyphs[glyph_index];
Layout::Glyph new_glyph;
- new_glyph.glyph = unbroken_span.glyph_string->glyphs[glyph_index].glyph;
+ new_glyph.glyph = unbroken_span_glyph_info->glyph;
new_glyph.in_character = _flow._characters.size();
new_glyph.rotation = glyph_rotate;
+ new_glyph.orientation = ORIENTATION_UPRIGHT; // Only effects vertical text
// We may have scaled font size to fit textLength; now, if
// @lengthAdjust=spacingAndGlyphs, this scaling must be only horizontal,
@@ -686,28 +687,52 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
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;
- new_glyph.y -= unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * font_size_multiplier * 0.5;
- new_glyph.width = new_span.line_height.ascent + new_span.line_height.descent;
- } else */
-
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.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);
+ // Vertical text
+
+ // TODO: Should also check 'glyph_orientation_vertical' if 'text-orientation' is unset...
+ if( new_span.text_orientation == SP_CSS_TEXT_ORIENTATION_SIDEWAYS ||
+ (new_span.text_orientation == SP_CSS_TEXT_ORIENTATION_MIXED &&
+ para.pango_items[unbroken_span.pango_item_index].item->analysis.gravity == 0) ) {
+
+ // Sideways orientation (Latin characters, CJK punctuation), 90deg rotation done at output stage.
+ new_glyph.orientation = ORIENTATION_SIDEWAYS;
+
+ new_glyph.x = current_x + unbroken_span_glyph_info->geometry.x_offset * font_size_multiplier;
+
+ new_glyph.y =_y_offset +
+ unbroken_span.baseline_shift +
+ (unbroken_span_glyph_info->geometry.y_offset * font_size_multiplier)
+ + 0.5 * (new_span.line_height.descent - new_span.line_height.ascent);
+
+ new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, false);
+
+ } else {
+
+ // Upright orientation
+ new_glyph.x = current_x + unbroken_span_glyph_info->geometry.x_offset * font_size_multiplier +
+ new_span.line_height.ascent;
+
+ new_glyph.y = _y_offset + // Does baseline shift have any meaning here?
+ unbroken_span.baseline_shift +
+ (unbroken_span_glyph_info->geometry.y_offset -
+ unbroken_span_glyph_info->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_info->glyph, true);
+ if( new_glyph.width == 0 ) {
+ new_glyph.width = unbroken_span_glyph_info->geometry.width * font_size_multiplier;
+ }
+
+ }
} else {
- new_glyph.x = x + unbroken_span.glyph_string->glyphs[glyph_index].geometry.x_offset * font_size_multiplier;
+ // Horizontal text
+ new_glyph.x = current_x + unbroken_span_glyph_info->geometry.x_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;
+ unbroken_span_glyph_info->geometry.y_offset * font_size_multiplier;
+ new_glyph.width = unbroken_span_glyph_info->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);
+ new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, false);
// for some reason pango returns zero width for invalid glyph characters (those empty boxes), so go to freetype for the info
}
if (new_span.direction == RIGHT_TO_LEFT) {
@@ -718,8 +743,10 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
if (unbroken_span.glyph_string->glyphs[rtl_index].attr.is_cluster_start && rtl_index != glyph_index)
break;
if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT)
+ // Vertical text
cluster_width += new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[rtl_index].glyph, true);
else
+ // Horizontal text
cluster_width += font_size_multiplier * unbroken_span.glyph_string->glyphs[rtl_index].geometry.width;
}
new_glyph.x -= cluster_width;
@@ -781,15 +808,15 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
advance_width *= direction_sign;
if (new_span.direction != para.direction) {
counter_directional_width_remaining -= advance_width;
- x -= advance_width;
+ current_x -= advance_width;
x_in_span_last -= advance_width;
} else {
- x += advance_width;
+ current_x += advance_width;
x_in_span_last += advance_width;
}
}
} else if (_flow._input_stream[unbroken_span.input_index]->Type() == CONTROL_CODE) {
- x += static_cast<InputStreamControlCode const *>(_flow._input_stream[unbroken_span.input_index])->width;
+ current_x += static_cast<InputStreamControlCode const *>(_flow._input_stream[unbroken_span.input_index])->width;
}
new_span.x_end = new_span.x_start + x_in_span_last;
@@ -978,6 +1005,7 @@ void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) con
#if PANGO_VERSION_CHECK(1,37,1)
TRACE((" ... compiled for font features\n"));
#endif
+
Glib::ustring para_text;
PangoAttrList *attributes_list;
unsigned input_index;
@@ -999,10 +1027,10 @@ void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) con
} else if (_flow._input_stream[input_index]->Type() == TEXT_SOURCE) {
Layout::InputStreamTextSource *text_source = static_cast<Layout::InputStreamTextSource *>(_flow._input_stream[input_index]);
- // create the font_instance
- font_instance *font = text_source->styleGetFontInstance();
- if (font == NULL)
- continue; // bad news: we'll have to ignore all this text because we know of no font to render it
+ // create the font_instance
+ font_instance *font = text_source->styleGetFontInstance();
+ if (font == NULL)
+ continue; // bad news: we'll have to ignore all this text because we know of no font to render it
PangoAttribute *attribute_font_description = pango_attr_font_desc_new(font->descr);
attribute_font_description->start_index = para_text.bytes();
@@ -1028,25 +1056,23 @@ void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) con
TRACE(("whole para: \"%s\"\n", para_text.data()));
TRACE(("%d input sources used\n", input_index - para->first_input_index));
-
// do the pango_itemize()
GList *pango_items_glist = NULL;
+ para->direction = LEFT_TO_RIGHT; // CSS default
if (_flow._input_stream[para->first_input_index]->Type() == TEXT_SOURCE) {
Layout::InputStreamTextSource const *text_source = static_cast<Layout::InputStreamTextSource *>(_flow._input_stream[para->first_input_index]);
if (text_source->style->direction.set) {
- PangoDirection pango_direction = (PangoDirection)_enum_converter(text_source->style->direction.computed, enum_convert_spstyle_direction_to_pango_direction, sizeof(enum_convert_spstyle_direction_to_pango_direction)/sizeof(enum_convert_spstyle_direction_to_pango_direction[0]));
+ para->direction = (text_source->style->direction.computed == SP_CSS_DIRECTION_LTR) ? LEFT_TO_RIGHT : RIGHT_TO_LEFT;
+ PangoDirection pango_direction = (text_source->style->direction.computed == SP_CSS_DIRECTION_LTR) ? PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL;
pango_items_glist = pango_itemize_with_base_dir(_pango_context, pango_direction, para_text.data(), 0, para_text.bytes(), attributes_list, NULL);
- para->direction = (Layout::Direction)_enum_converter(text_source->style->direction.computed, enum_convert_spstyle_direction_to_my_direction, sizeof(enum_convert_spstyle_direction_to_my_direction)/sizeof(enum_convert_spstyle_direction_to_my_direction[0]));
}
}
- if (pango_items_glist == NULL) { // no direction specified, guess it
- pango_items_glist = pango_itemize(_pango_context, para_text.data(), 0, para_text.bytes(), attributes_list, NULL);
- // I think according to the css spec this is wrong and we're never allowed to guess the directionality
- // of a paragraph. Need to talk to an rtl speaker.
- if (pango_items_glist == NULL || pango_items_glist->data == NULL) para->direction = LEFT_TO_RIGHT;
- else para->direction = (((PangoItem*)pango_items_glist->data)->analysis.level & 1) ? RIGHT_TO_LEFT : LEFT_TO_RIGHT;
+ if( pango_items_glist == NULL ) {
+ // Type wasn't TEXT_SOURCE or direction was not set.
+ pango_items_glist = pango_itemize(_pango_context, para_text.data(), 0, para_text.bytes(), attributes_list, NULL);
}
+
pango_attr_list_unref(attributes_list);
// convert the GList to our vector<> and make the font_instance for each PangoItem at the same time
@@ -1070,43 +1096,45 @@ void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) con
}
/**
- * Gets the ascent, descent and leading for a font and the alteration that has to be performed
- * according to the value specified by the line-height css property. The result of multiplying
- * \a line_height by \a line_height_multiplier is the inline box height as specified in css2
- * section 10.8.
+ * Gets the ascent and descent for a font given the 'font-size' propert and finds the value of
+ * line_height_multiplier given the 'line-height' property. The result of multiplying
+ * \a l by \a line_height_multiplier is the inline box height as specified in css2
+ * section 10.8. http://www.w3.org/TR/CSS2/visudet.html#line-height
*/
+// THIS FUNCTION SHOULD NOT BE NECESSARY
void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font_size,
- SPStyle const *style, LineHeight *line_height,
+ SPStyle const *style, FontMetrics *font_metrics,
double *line_height_multiplier)
{
if (font == NULL) {
- line_height->setZero();
+ font_metrics->setZero();
*line_height_multiplier = 1.0;
}
else {
- font->FontMetrics(line_height->ascent, line_height->descent, line_height->leading);
+ font->FontMetrics(font_metrics->ascent, font_metrics->descent, font_metrics->leading);
}
- *line_height *= font_size;
+ *font_metrics *= font_size;
// yet another borked SPStyle member that we're going to have to fix ourselves
+ // To do: check if it really is still borked.
for ( ; ; ) {
if (style->line_height.set && !style->line_height.inherit) {
if (style->line_height.normal)
break;
switch (style->line_height.unit) {
case SP_CSS_UNIT_NONE:
- *line_height_multiplier = style->line_height.computed * font_size / line_height->total();
+ *line_height_multiplier = style->line_height.computed;
return;
case SP_CSS_UNIT_EX:
- *line_height_multiplier = style->line_height.value * 0.5 * font_size / line_height->total();
+ *line_height_multiplier = style->line_height.value * 0.5;
// 0.5 is an approximation of the x-height. Fixme.
return;
case SP_CSS_UNIT_EM:
case SP_CSS_UNIT_PERCENT:
- *line_height_multiplier = style->line_height.value * font_size / line_height->total();
+ *line_height_multiplier = style->line_height.value;
return;
default: // absolute values
- *line_height_multiplier = style->line_height.computed / line_height->total();
+ *line_height_multiplier = style->line_height.computed / font_metrics->emSize();
return;
}
break;
@@ -1115,7 +1143,7 @@ void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font
style = style->object->parent->style;
if (style == NULL) break;
}
- *line_height_multiplier = LINE_HEIGHT_NORMAL * font_size / line_height->total();
+ *line_height_multiplier = LINE_HEIGHT_NORMAL;
}
bool compareGlyphWidth(const PangoGlyphInfo &a, const PangoGlyphInfo &b)
@@ -1155,7 +1183,6 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const
new_span.input_index = input_index;
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;
para->unbroken_spans.push_back(new_span);
@@ -1194,11 +1221,13 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const
new_span.dy._set = false;
new_span.rotate._set = false;
if (_block_progression == TOP_TO_BOTTOM || _block_progression == BOTTOM_TO_TOP) {
+ // Horizontal text
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].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 {
+ // Vertical text
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].computed * _flow.getTextLengthMultiplierDue();
@@ -1240,6 +1269,7 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const
g_assert( span_start_byte_in_source + new_span.text_bytes <= text_source->text->bytes() );
g_assert( memchr(text_source->text->data() + span_start_byte_in_source, '\0', static_cast<size_t>(new_span.text_bytes))
== NULL );
+
/* Notes as of 4/29/13. Pango_shape is not generating English language ligatures, but it is generating
them for Hebrew (and probably other similar languages). In the case observed 3 unicode characters (a base
and 2 Mark, nonspacings) are merged into two glyphs (the base + first Mn, the 2nd Mn). All of these map
@@ -1247,6 +1277,8 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const
characters and glyphs. A big chunk of the conditional code which immediately follows this call
is there to clean up the resulting mess.
*/
+
+ // Convert characters to glyphs
pango_shape(text_source->text->data() + span_start_byte_in_source,
new_span.text_bytes,
&para->pango_items[pango_item_index].item->analysis,
@@ -1329,6 +1361,7 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const
// 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;
+ new_span.text_orientation = (SPCSSTextOrientation)text_source->style->text_orientation.computed;
// TODO: metrics for vertical text
TRACE(("add text span %lu \"%s\"\n", para->unbroken_spans.size(), text_source->text->raw().substr(span_start_byte_in_source, new_span.text_bytes).c_str()));
@@ -1399,7 +1432,7 @@ bool Layout::Calculator::_goToNextWrapShape()
bool Layout::Calculator::_findChunksForLine(ParagraphInfo const &para,
UnbrokenSpanPosition *start_span_pos,
std::vector<ChunkInfo> *chunk_info,
- LineHeight *line_height)
+ FontMetrics *line_height)
{
// init the initial line_height
if (start_span_pos->iter_span == para.unbroken_spans.end()) {
@@ -1422,7 +1455,6 @@ bool Layout::Calculator::_findChunksForLine(ParagraphInfo const &para,
// if we're not wrapping set the line_height big and negative so we can use negative line height
line_height->ascent = -1.0e10;
line_height->descent = -1.0e10;
- line_height->leading = -1.0e10;
}
else
line_height->setZero();
@@ -1474,7 +1506,7 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const &para,
UnbrokenSpanPosition const &start_span_pos,
ScanlineMaker::ScanRun const &scan_run,
std::vector<ChunkInfo> *chunk_info,
- LineHeight *line_height) const
+ FontMetrics *line_height) const
{
ChunkInfo new_chunk;
new_chunk.text_width = 0.0;
@@ -1511,16 +1543,17 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const &para,
}
// see if this span is too tall to fit on the current line
- LineHeight total_height = new_span.start.iter_span->line_height;
- total_height *= new_span.start.iter_span->line_height_multiplier;
+ FontMetrics new_span_height = new_span.start.iter_span->line_height;
+ new_span_height.computeEffective( new_span.start.iter_span->line_height_multiplier );
/* floating point 80-bit/64-bit rounding problems require epsilon. See
discussion http://inkscape.gristle.org/2005-03-16.txt around 22:00 */
- if ( total_height.ascent > line_height->ascent + FLT_EPSILON
- || total_height.descent > line_height->descent + FLT_EPSILON
- || total_height.leading > line_height->leading + FLT_EPSILON) {
- line_height->max(total_height);
- if (!_scanline_maker->canExtendCurrentScanline(*line_height))
+ if ( new_span_height.ascent > line_height->ascent + FLT_EPSILON ||
+ new_span_height.descent > line_height->descent + FLT_EPSILON) {
+ // Take larger of each of the two ascents and two descents per CSS
+ line_height->max(new_span_height);
+ if (!_scanline_maker->canExtendCurrentScanline(*line_height)) {
return false;
+ }
}
bool span_fitted = _measureUnbrokenSpan(para, &new_span, &last_span_at_break, &last_span_at_emergency_break, new_chunk.scanrun_width - new_chunk.text_width);
@@ -1540,7 +1573,7 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const &para,
TRACE(("chunk complete, used %f width (%d whitespaces, %lu brokenspans)\n", new_chunk.text_width, new_chunk.whitespace_count, new_chunk.broken_spans.size()));
chunk_info->push_back(new_chunk);
- if (scan_run.width() >= 4.0 * line_height->total() && last_span_at_break.end == start_span_pos) {
+ if (scan_run.width() >= 4.0 * line_height->emSize() && last_span_at_break.end == start_span_pos) {
/* **non-SVG spec bit**: See bug #1191102
If the user types a very long line with no spaces, the way the spec
is written at the moment means that when the length of the text
@@ -1619,14 +1652,27 @@ bool Layout::Calculator::calculate()
_flow._clearOutputObjects();
_pango_context = (font_factory::Default())->fontContext;
+
_font_factory_size_multiplier = (font_factory::Default())->fontSize;
+ // Reset gravity hint in case it was changed via previous use of 'text-orientation'
+ // (scripts take their natural gravity given base gravity).
+ pango_context_set_gravity_hint(_pango_context, PANGO_GRAVITY_HINT_NATURAL);
+
_block_progression = _flow._blockProgression();
+ if( _block_progression == RIGHT_TO_LEFT || _block_progression == LEFT_TO_RIGHT ) {
+ // Vertical text, CJK
+ pango_context_set_base_gravity(_pango_context, PANGO_GRAVITY_EAST);
+ } else {
+ // Horizontal text
+ pango_context_set_base_gravity(_pango_context, PANGO_GRAVITY_AUTO);
+ }
+
_y_offset = 0.0;
_createFirstScanlineMaker();
ParagraphInfo para;
- LineHeight line_height; // needs to be maintained across paragraphs to be able to deal with blank paras
+ FontMetrics line_height; // needs to be maintained across paragraphs to be able to deal with blank paras
for(para.first_input_index = 0 ; para.first_input_index < _flow._input_stream.size() ; ) {
// jump to the next wrap shape if this is a SHAPE_BREAK control code
if (_flow._input_stream[para.first_input_index]->Type() == CONTROL_CODE) {
@@ -1640,7 +1686,10 @@ bool Layout::Calculator::calculate()
if (_scanline_maker == NULL)
break; // we're trying to flow past the last wrap shape
- _buildPangoItemizationForPara(&para);
+ // Break things up into little pango units with unique direction, gravity, etc.
+ _buildPangoItemizationForPara(&para);
+
+ // Do shaping (convert characters to glyphs)
unsigned para_end_input_index = _buildSpansForPara(&para);
if (_flow._input_stream[para.first_input_index]->Type() == TEXT_SOURCE)
@@ -1679,7 +1728,7 @@ bool Layout::Calculator::calculate()
Layout::Span new_span;
if (_flow._spans.empty()) {
new_span.font = NULL;
- new_span.font_size = line_height.ascent + line_height.descent;
+ new_span.font_size = line_height.emSize();
new_span.line_height = line_height;
new_span.x_end = 0.0;
} else {
@@ -1757,7 +1806,7 @@ void Layout::_calculateCursorShapeForEmpty()
font_instance *font = text_source->styleGetFontInstance();
double font_size = text_source->styleComputeFontSize();
double caret_slope_run = 0.0, caret_slope_rise = 1.0;
- LineHeight line_height;
+ FontMetrics line_height;
if (font) {
const_cast<font_instance*>(font)->FontSlope(caret_slope_run, caret_slope_rise);
font->FontMetrics(line_height.ascent, line_height.descent, line_height.leading);
@@ -1780,10 +1829,13 @@ void Layout::_calculateCursorShapeForEmpty()
ShapeScanlineMaker scanline_maker(_input_wrap_shapes.front().shape, block_progression);
std::vector<ScanlineMaker::ScanRun> scan_runs = scanline_maker.makeScanline(line_height);
if (!scan_runs.empty()) {
- if (block_progression == LEFT_TO_RIGHT || block_progression == RIGHT_TO_LEFT)
+ if (block_progression == LEFT_TO_RIGHT || block_progression == RIGHT_TO_LEFT) {
+ // Vertical text
_empty_cursor_shape.position = Geom::Point(scan_runs.front().y + font_size, scan_runs.front().x_start);
- else
+ } else {
+ // Horizontal text
_empty_cursor_shape.position = Geom::Point(scan_runs.front().x_start, scan_runs.front().y + font_size);
+ }
}
}
}
diff --git a/src/libnrtype/Layout-TNG-OutIter.cpp b/src/libnrtype/Layout-TNG-OutIter.cpp
index 137fe0a0f..c7275c24e 100644
--- a/src/libnrtype/Layout-TNG-OutIter.cpp
+++ b/src/libnrtype/Layout-TNG-OutIter.cpp
@@ -120,7 +120,7 @@ Layout::iterator Layout::getNearestCursorPositionTo(double x, double y) const
double best_y_range = DBL_MAX;
double best_x_range = DBL_MAX;
for (chunk_index = 0 ; chunk_index < _chunks.size() ; chunk_index++) {
- LineHeight line_height = {0.0, 0.0, 0.0};
+ FontMetrics line_height = {0.0, 0.0, 0.0};
double chunk_width = 0.0;
for ( ; span_index < _spans.size() && _spans[span_index].in_chunk == chunk_index ; span_index++) {
line_height.max(_spans[span_index].line_height);
@@ -340,8 +340,8 @@ Geom::Rect Layout::characterBoundingBox(iterator const &it, double *rotation) co
double baseline_y = _characters[char_index].line(this).baseline_y + _characters[char_index].span(this).baseline_shift;
if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
- double span_height = _spans[_characters[char_index].in_span].line_height.ascent + _spans[_characters[char_index].in_span].line_height.descent;
- top_left[Geom::Y] = top_left[Geom::X];
+ double span_height = _spans[_characters[char_index].in_span].line_height.emSize();
+ 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;
@@ -404,7 +404,7 @@ std::vector<Geom::Point> Layout::createSelectionShape(iterator const &it_start,
double vertical_scale = _glyphs.back().vertical_scale;
if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
- double span_height = vertical_scale * (_spans[span_index].line_height.ascent + _spans[span_index].line_height.descent);
+ double span_height = vertical_scale * _spans[span_index].line_height.emSize();
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];
@@ -510,17 +510,19 @@ void Layout::queryCursorShape(iterator const &it, Geom::Point &position, double
double vertical_scale = _glyphs.empty() ? 1.0 : _glyphs.back().vertical_scale;
if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
- height = vertical_scale * span->line_height.ascent + span->line_height.descent;
+ // Vertical text
+ height = vertical_scale * span->line_height.emSize();
rotation += M_PI / 2;
std::swap(position[Geom::X], position[Geom::Y]);
position[Geom::X] -= vertical_scale * sin(rotation) * height * 0.5;
position[Geom::Y] += vertical_scale * cos(rotation) * height * 0.5;
} else {
+ // Horizontal text
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 = vertical_scale * (span->line_height.ascent + span->line_height.descent) / cos(caret_slope);
+ height = vertical_scale * (span->line_height.emSize()) / cos(caret_slope);
rotation += caret_slope;
position[Geom::X] -= sin(rotation) * vertical_scale * span->line_height.descent;
position[Geom::Y] += cos(rotation) * vertical_scale * span->line_height.descent;
diff --git a/src/libnrtype/Layout-TNG-Output.cpp b/src/libnrtype/Layout-TNG-Output.cpp
index 6e3faf33b..0bbf266c7 100644
--- a/src/libnrtype/Layout-TNG-Output.cpp
+++ b/src/libnrtype/Layout-TNG-Output.cpp
@@ -93,26 +93,41 @@ void Layout::_clearOutputObjects()
_path_fitted = NULL;
}
-void Layout::LineHeight::max(LineHeight const &other)
+void Layout::FontMetrics::max(FontMetrics const &other)
{
if (other.ascent > ascent) ascent = other.ascent;
if (other.descent > descent) descent = other.descent;
- if (other.leading > leading) leading = other.leading;
+}
+
+void Layout::FontMetrics::computeEffective( const double &line_height_multiplier ) {
+ double half_leading = 0.5 * (line_height_multiplier - 1.0) * emSize();
+ ascent += half_leading;
+ descent += half_leading;
}
void Layout::_getGlyphTransformMatrix(int glyph_index, Geom::Affine *matrix) const
{
Span const &span = _glyphs[glyph_index].span(this);
- double sin_rotation = sin(_glyphs[glyph_index].rotation);
- double cos_rotation = cos(_glyphs[glyph_index].rotation);
+ double rotation = _glyphs[glyph_index].rotation;
+ if ( (span.block_progression == LEFT_TO_RIGHT || span.block_progression == RIGHT_TO_LEFT) &&
+ _glyphs[glyph_index].orientation == ORIENTATION_SIDEWAYS ) {
+ // Vertical sideways text
+ rotation += M_PI/2.0;
+ }
+ double sin_rotation = sin(rotation);
+ double cos_rotation = cos(rotation);
(*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 * (_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) {
+ // Vertical text
+ // This effectively swaps x for y which changes handedness of coordinate system. This is a bit strange
+ // and not what one would expect but the compute code already reverses y so OK.
(*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;
} else {
+ // Horizontal text
(*matrix)[4] = _chunks[span.in_chunk].left_x + _glyphs[glyph_index].x;
(*matrix)[5] = _lines[_chunks[span.in_chunk].in_line].baseline_y + _glyphs[glyph_index].y;
}
@@ -129,7 +144,7 @@ void Layout::show(DrawingGroup *in_arena, Geom::OptRect const &paintbox) const
text_source->style->text_decoration_data.tspan_width = _spans[span_index].width();
text_source->style->text_decoration_data.ascender = _spans[span_index].line_height.getAscent();
text_source->style->text_decoration_data.descender = _spans[span_index].line_height.getDescent();
- text_source->style->text_decoration_data.line_gap = _spans[span_index].line_height.getLeading();
+
if(!span_index ||
(_chunks[_spans[span_index].in_chunk].in_line != _chunks[_spans[span_index-1].in_chunk].in_line)){
text_source->style->text_decoration_data.tspan_line_start = true;
@@ -580,7 +595,7 @@ Glib::ustring Layout::dumpAsText() const
result += Glib::ustring::compose(" font '%1' %2 %3 %4\n", sp_font_description_get_family(_spans[span_index].font->descr), _spans[span_index].font_size, style_to_text(pango_font_description_get_style(_spans[span_index].font->descr)), weight_to_text(pango_font_description_get_weight(_spans[span_index].font->descr)));
}
result += Glib::ustring::compose(" x_start = %1, x_end = %2\n", _spans[span_index].x_start, _spans[span_index].x_end)
- + Glib::ustring::compose(" line height: ascent %1, descent %2 leading %3\n", _spans[span_index].line_height.ascent, _spans[span_index].line_height.descent, _spans[span_index].line_height.leading)
+ + Glib::ustring::compose(" line height: ascent %1, descent %2\n", _spans[span_index].line_height.ascent, _spans[span_index].line_height.descent)
+ Glib::ustring::compose(" direction %1, block-progression %2\n", direction_to_text(_spans[span_index].direction), direction_to_text(_spans[span_index].block_progression))
+ " ** characters:\n";
Glib::ustring::const_iterator iter_char = _spans[span_index].input_stream_first_character;
diff --git a/src/libnrtype/Layout-TNG-Scanline-Maker.h b/src/libnrtype/Layout-TNG-Scanline-Maker.h
index d513d7cc1..da70bff89 100644
--- a/src/libnrtype/Layout-TNG-Scanline-Maker.h
+++ b/src/libnrtype/Layout-TNG-Scanline-Maker.h
@@ -47,7 +47,7 @@ public:
between calls if the new height too big to fit in the space remaining in
this shape. Returns an empty vector if there is no space left in the
current shape. */
- virtual std::vector<ScanRun> makeScanline(Layout::LineHeight const &line_height) =0;
+ virtual std::vector<ScanRun> makeScanline(Layout::FontMetrics const &line_height) =0;
/** Indicates that the caller has successfully filled the current line
and hence that the next call to makeScanline() should return lines on
@@ -71,7 +71,7 @@ public:
The metrics given here are considered to be the ones that are being
used now, and hence is the line advance height used by completeLine().
*/
- virtual bool canExtendCurrentScanline(Layout::LineHeight const &line_height) =0;
+ virtual bool canExtendCurrentScanline(Layout::FontMetrics const &line_height) =0;
};
/** \brief private to Layout. Generates infinite scanlines for when you don't want wrapping
@@ -90,7 +90,7 @@ public:
virtual ~InfiniteScanlineMaker();
/** Returns a single infinite run at the current location */
- virtual std::vector<ScanRun> makeScanline(Layout::LineHeight const &line_height);
+ virtual std::vector<ScanRun> makeScanline(Layout::FontMetrics const &line_height);
/** Increments the current y by the current line height */
virtual void completeLine();
@@ -102,11 +102,11 @@ public:
virtual void setNewYCoordinate(double new_y);
/** Always true, but has to save the new height */
- virtual bool canExtendCurrentScanline(Layout::LineHeight const &line_height);
+ virtual bool canExtendCurrentScanline(Layout::FontMetrics const &line_height);
private:
double _x, _y;
- Layout::LineHeight _current_line_height;
+ Layout::FontMetrics _current_line_height;
bool _negative_block_progression; /// if true, indicates that completeLine() should decrement rather than increment, ie block-progression is either rl or bt
};
@@ -122,7 +122,7 @@ public:
ShapeScanlineMaker(Shape const *shape, Layout::Direction block_progression);
virtual ~ShapeScanlineMaker();
- virtual std::vector<ScanRun> makeScanline(Layout::LineHeight const &line_height);
+ virtual std::vector<ScanRun> makeScanline(Layout::FontMetrics const &line_height);
virtual void completeLine();
@@ -131,7 +131,7 @@ public:
virtual void setNewYCoordinate(double new_y);
/** never true */
- virtual bool canExtendCurrentScanline(Layout::LineHeight const &line_height);
+ virtual bool canExtendCurrentScanline(Layout::FontMetrics const &line_height);
private:
/** To generate scanlines for top-to-bottom text it is easiest if we
simply rotate the given shape by a multiple of 90 degrees. This stores
diff --git a/src/libnrtype/Layout-TNG-Scanline-Makers.cpp b/src/libnrtype/Layout-TNG-Scanline-Makers.cpp
index 7144f3876..67fffd620 100644
--- a/src/libnrtype/Layout-TNG-Scanline-Makers.cpp
+++ b/src/libnrtype/Layout-TNG-Scanline-Makers.cpp
@@ -19,9 +19,7 @@ namespace Text {
Layout::InfiniteScanlineMaker::InfiniteScanlineMaker(double initial_x, double initial_y, Layout::Direction block_progression)
{
- _current_line_height.ascent = 0.0;
- _current_line_height.descent = 0.0;
- _current_line_height.leading = 0.0;
+ _current_line_height.setZero();
switch (block_progression) {
case LEFT_TO_RIGHT:
case RIGHT_TO_LEFT:
@@ -41,7 +39,7 @@ Layout::InfiniteScanlineMaker::~InfiniteScanlineMaker()
{
}
-std::vector<Layout::ScanlineMaker::ScanRun> Layout::InfiniteScanlineMaker::makeScanline(Layout::LineHeight const &line_height)
+std::vector<Layout::ScanlineMaker::ScanRun> Layout::InfiniteScanlineMaker::makeScanline(Layout::FontMetrics const &line_height)
{
std::vector<ScanRun> runs(1);
runs[0].x_start = _x;
@@ -54,12 +52,10 @@ std::vector<Layout::ScanlineMaker::ScanRun> Layout::InfiniteScanlineMaker::makeS
void Layout::InfiniteScanlineMaker::completeLine()
{
if (_negative_block_progression)
- _y -= _current_line_height.total();
+ _y -= _current_line_height.emSize();
else
- _y += _current_line_height.total();
- _current_line_height.ascent = 0.0;
- _current_line_height.descent = 0.0;
- _current_line_height.leading = 0.0;
+ _y += _current_line_height.emSize();
+ _current_line_height.setZero();
}
void Layout::InfiniteScanlineMaker::setNewYCoordinate(double new_y)
@@ -67,7 +63,7 @@ void Layout::InfiniteScanlineMaker::setNewYCoordinate(double new_y)
_y = new_y;
}
-bool Layout::InfiniteScanlineMaker::canExtendCurrentScanline(Layout::LineHeight const &line_height)
+bool Layout::InfiniteScanlineMaker::canExtendCurrentScanline(Layout::FontMetrics const &line_height)
{
_current_line_height = line_height;
return true;
@@ -111,29 +107,29 @@ Layout::ShapeScanlineMaker::~ShapeScanlineMaker()
delete _rotated_shape;
}
-std::vector<Layout::ScanlineMaker::ScanRun> Layout::ShapeScanlineMaker::makeScanline(Layout::LineHeight const &line_height)
+std::vector<Layout::ScanlineMaker::ScanRun> Layout::ShapeScanlineMaker::makeScanline(Layout::FontMetrics const &line_height)
{
- FloatLigne line_rasterization;
- FloatLigne line_decent_length_runs;
- float line_text_height = (float)(line_height.ascent + line_height.descent);
-
if (_y > _bounding_box_bottom)
return std::vector<ScanRun>();
if (_y < _bounding_box_top)
_y = _bounding_box_top;
+ FloatLigne line_rasterization;
+ FloatLigne line_decent_length_runs;
+ float line_text_height = (float)(line_height.emSize());
if (line_text_height == 0.0)
line_text_height = 0.001; // Scan() doesn't work for zero height so this will have to do
- _current_line_height = (float)line_height.total();
+ _current_line_height = (float)line_height.emSize();
// I think what's going on here is that we're moving the top of the scanline to the given position...
_rotated_shape->Scan(_rasterizer_y, _current_rasterization_point, _y, line_text_height);
// ...then actually retreiving the scanline (which alters the first two parameters)
_rotated_shape->Scan(_rasterizer_y, _current_rasterization_point, _y + line_text_height , &line_rasterization, true, line_text_height);
- // sanitise the raw rasterisation, which could have weird overlaps
+ // sanitise the raw rasterisation, which could have weird overlaps
line_rasterization.Flatten();
+ // line_rasterization.Affiche();
// cut out runs that cover less than 90% of the line
line_decent_length_runs.Over(&line_rasterization, 0.9 * line_text_height);
@@ -179,7 +175,7 @@ void Layout::ShapeScanlineMaker::setNewYCoordinate(double new_y)
// it's not an important question because <flowSpan> doesn't have a y attribute
}
-bool Layout::ShapeScanlineMaker::canExtendCurrentScanline(Layout::LineHeight const &/*line_height*/)
+bool Layout::ShapeScanlineMaker::canExtendCurrentScanline(Layout::FontMetrics const &/*line_height*/)
{
//we actually could return true if only the leading changed, but that's too much effort for something that rarely happens
return false;
diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h
index 26db1fad9..66cc96d3f 100644
--- a/src/libnrtype/Layout-TNG.h
+++ b/src/libnrtype/Layout-TNG.h
@@ -22,6 +22,7 @@
#include <vector>
#include <boost/optional.hpp>
#include <svg/svg-length.h>
+#include "style-enums.h"
#ifdef HAVE_CAIRO_PDF
namespace Inkscape {
@@ -157,6 +158,9 @@ public:
both the 'direction' and 'block-progression' CSS attributes. */
enum Direction {LEFT_TO_RIGHT, RIGHT_TO_LEFT, TOP_TO_BOTTOM, BOTTOM_TO_TOP};
+ /** Used to specify orientation of glyphs in vertical text. */
+ enum Orientation {ORIENTATION_UPRIGHT, ORIENTATION_SIDEWAYS};
+
/** Display alignment for shapes. See appendWrapShape(). */
enum DisplayAlign {DISPLAY_ALIGN_BEFORE, DISPLAY_ALIGN_CENTER, DISPLAY_ALIGN_AFTER};
@@ -603,18 +607,45 @@ public:
//@}
- /// it's useful for this to be public so that ScanlineMaker can use it
- struct LineHeight {
+
+ /**
+ * Keep track of font metrics. Two use cases:
+ * 1. Keep track of ascent and descent of an individual font.
+ * 2. Keep track of effective ascent and descent that includes half-leading.
+ *
+ * Note: Leading refers to the "external" leading which is added (subtracted) due to
+ * a computed value of 'line-height' that differs from 'font-size'. "Internal" leading
+ * which is specified inside a font is not used in CSS. The 'font-size' is based on
+ * the font's em size which is 'ascent' + 'descent'.
+ *
+ * This structure was renamed (and modified) from "LineHeight".
+ *
+ * It's useful for this to be public so that ScanlineMaker can use it.
+ */
+ struct FontMetrics {
+
double ascent;
double descent;
- double leading;
- inline double total() const {return ascent + descent + leading;}
- inline void setZero() {ascent = descent = leading = 0.0;}
- inline LineHeight& operator*=(double x) {ascent *= x; descent *= x; leading *= x; return *this;}
- void max(LineHeight const &other); /// makes this object contain the largest of all three members between this object and other
+ double leading; // Not used... to be removed (requires change to font->FontMetrics()
+
+ // CSS 2.1 dictates that font-size is based on em-size which is defined as ascent + descent
+ inline double emSize() const {return ascent + descent;}
+ // Alternatively name function for use 2.
+ inline double lineSize() const { return ascent + descent; }
+ inline void setZero() {ascent = descent = 0.0;}
+
+ // For scaling for 'font-size'.
+ inline FontMetrics& operator*=(double x) {ascent *= x; descent *= x; return *this;}
+
+ /// Save the larger values of ascent and descent between this and other. Needed for laying
+ /// out a line with mixed font-sizes, fonts, or line spacings.
+ void max(FontMetrics const &other);
+
+ /// Calculate the effective ascent and descent including half "leading".
+ void computeEffective( const double &line_height );
+
inline double getAscent() const {return ascent; }
inline double getDescent() const {return descent; }
- inline double getLeading() const {return leading; }
};
/// see _enum_converter()
@@ -748,6 +779,7 @@ private:
float x; /// relative to the start of the chunk
float y; /// relative to the current line's baseline
float rotation; /// absolute, modulo any object transforms, which we don't know about
+ Orientation orientation; /// Orientation of glyph in vertical text
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];}
@@ -772,8 +804,9 @@ private:
float x_start; /// relative to the start of the chunk
float x_end; /// relative to the start of the chunk
inline float width() const {return std::abs(x_start - x_end);}
- LineHeight line_height;
+ FontMetrics line_height;
double baseline_shift; /// relative to the line's baseline
+ SPCSSTextOrientation text_orientation;
Direction direction; /// See CSS3 section 3.2. Either rtl or ltr
Direction block_progression; /// See CSS3 section 3.2. The direction in which lines go.
unsigned in_input_stream_item;
diff --git a/src/style-enums.h b/src/style-enums.h
index 03255b3b1..c9a558e0f 100644
--- a/src/style-enums.h
+++ b/src/style-enums.h
@@ -174,10 +174,7 @@ enum SPCSSWritingMode {
enum SPCSSTextOrientation {
SP_CSS_TEXT_ORIENTATION_MIXED,
SP_CSS_TEXT_ORIENTATION_UPRIGHT,
- SP_CSS_TEXT_ORIENTATION_SIDEWAYS_RIGHT,
- SP_CSS_TEXT_ORIENTATION_SIDEWAYS_LEFT,
- SP_CSS_TEXT_ORIENTATION_SIDEWAYS,
- SP_CSS_TEXT_ORIENTATION_USE_GLYPH_ORIENTATION
+ SP_CSS_TEXT_ORIENTATION_SIDEWAYS
};
enum SPTextAnchor {
@@ -519,10 +516,7 @@ static SPStyleEnum const enum_writing_mode[] = {
static SPStyleEnum const enum_text_orientation[] = {
{"mixed", SP_CSS_TEXT_ORIENTATION_MIXED}, // Default
{"upright", SP_CSS_TEXT_ORIENTATION_UPRIGHT},
- {"sideways-right", SP_CSS_TEXT_ORIENTATION_SIDEWAYS_RIGHT},
- {"sideways-left", SP_CSS_TEXT_ORIENTATION_SIDEWAYS_LEFT},
{"sideways", SP_CSS_TEXT_ORIENTATION_SIDEWAYS},
- {"use-glyph-orientation", SP_CSS_TEXT_ORIENTATION_USE_GLYPH_ORIENTATION},
{NULL, -1}
};
diff --git a/src/style-internal.h b/src/style-internal.h
index 1ddab30f1..889292454 100644
--- a/src/style-internal.h
+++ b/src/style-internal.h
@@ -1260,7 +1260,6 @@ struct SPITextDecorationData {
float tspan_width; // from libnrtype, when it calculates spans
float ascender; // the rest from tspan's font
float descender;
- float line_gap;
float underline_thickness;
float underline_position;
float line_through_thickness;
diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp
index 7b22e4af7..75c4a6736 100644
--- a/src/widgets/text-toolbar.cpp
+++ b/src/widgets/text-toolbar.cpp
@@ -737,7 +737,7 @@ static void sp_text_rotation_value_changed( GtkAdjustment *adj, GObject *tbl )
g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
}
-static void sp_text_orientation_mode_changed( EgeSelectOneAction *act, GObject *tbl )
+static void sp_writing_mode_changed( EgeSelectOneAction *act, GObject *tbl )
{
// quit if run by the _changed callbacks
if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
@@ -752,13 +752,73 @@ static void sp_text_orientation_mode_changed( EgeSelectOneAction *act, GObject *
{
case 0:
{
- sp_repr_css_set_property (css, "writing-mode", "lr");
+ sp_repr_css_set_property (css, "writing-mode", "lr-tb");
break;
}
case 1:
{
- sp_repr_css_set_property (css, "writing-mode", "tb");
+ sp_repr_css_set_property (css, "writing-mode", "tb-rl");
+ break;
+ }
+
+ case 2:
+ {
+ sp_repr_css_set_property (css, "writing-mode", "vertical-lr");
+ break;
+ }
+}
+
+ SPStyle query(SP_ACTIVE_DOCUMENT);
+ int result_numbers =
+ sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
+
+ // If querying returned nothing, update default style.
+ if (result_numbers == QUERY_STYLE_NOTHING)
+ {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->mergeStyle("/tools/text/style", css);
+ }
+
+ sp_desktop_set_style (SP_ACTIVE_DESKTOP, css, true, true);
+ if(result_numbers != QUERY_STYLE_NOTHING)
+ {
+ DocumentUndo::done(SP_ACTIVE_DESKTOP->getDocument(), SP_VERB_CONTEXT_TEXT,
+ _("Text: Change writing mode"));
+ }
+ sp_repr_css_attr_unref (css);
+
+ g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
+}
+
+static void sp_text_orientation_changed( EgeSelectOneAction *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) );
+
+ int mode = ege_select_one_action_get_active( act );
+
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+ switch (mode)
+ {
+ case 0:
+ {
+ sp_repr_css_set_property (css, "text-orientation", "auto");
+ break;
+ }
+
+ case 1:
+ {
+ sp_repr_css_set_property (css, "text-orientation", "upright");
+ break;
+ }
+
+ case 2:
+ {
+ sp_repr_css_set_property (css, "text-orientation", "sideways");
break;
}
}
@@ -893,13 +953,17 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/
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);
+ int result_wmode = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_WRITINGMODES);
/*
* If no text in selection (querying returned nothing), read the style from
* the /tools/text preferencess (default style for new texts). Return if
* tool bar already set to these preferences.
*/
- if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
+ if (result_family == QUERY_STYLE_NOTHING ||
+ result_style == QUERY_STYLE_NOTHING ||
+ result_numbers == QUERY_STYLE_NOTHING ||
+ result_wmode == QUERY_STYLE_NOTHING ) {
// There are no texts in selection, read from preferences.
query.readFromPrefs("/tools/text");
#ifdef DEBUG_TEXT
@@ -1047,13 +1111,42 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/
gtk_adjustment_set_value( letterSpacingAdjustment, letterSpacing );
+ // Writing mode
+ int activeButton2 = 0;
+ if (query.writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) activeButton2 = 0;
+ if (query.writing_mode.computed == SP_CSS_WRITING_MODE_TB_RL) activeButton2 = 1;
+ if (query.writing_mode.computed == SP_CSS_WRITING_MODE_TB_LR) activeButton2 = 2;
+
+ EgeSelectOneAction* writingModeAction =
+ EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextWritingModeAction" ) );
+ ege_select_one_action_set_active( writingModeAction, activeButton2 );
+
// Orientation
- int activeButton2 = (query.writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB ? 0 : 1);
+ int activeButton3 = 0;
+ if (query.text_orientation.computed == SP_CSS_TEXT_ORIENTATION_MIXED ) activeButton3 = 0;
+ if (query.text_orientation.computed == SP_CSS_TEXT_ORIENTATION_UPRIGHT ) activeButton3 = 1;
+ if (query.text_orientation.computed == SP_CSS_TEXT_ORIENTATION_SIDEWAYS) activeButton3 = 2;
EgeSelectOneAction* textOrientationAction =
EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextOrientationAction" ) );
- ege_select_one_action_set_active( textOrientationAction, activeButton2 );
+ ege_select_one_action_set_active( textOrientationAction, activeButton3 );
+
+ // Disable text orientation for horizontal text.. See above for why this nonsense
+ model = GTK_LIST_STORE( ege_select_one_action_get_model( textOrientationAction ) );
+ path = gtk_tree_path_new_from_string("0");
+ gtk_tree_model_get_iter( GTK_TREE_MODEL (model), &iter, path );
+ gtk_list_store_set( model, &iter, /* column */ 3, activeButton2 != 0, -1 );
+
+ path = gtk_tree_path_new_from_string("1");
+ gtk_tree_model_get_iter( GTK_TREE_MODEL (model), &iter, path );
+ gtk_list_store_set( model, &iter, /* column */ 3, activeButton2 != 0, -1 );
+
+ path = gtk_tree_path_new_from_string("2");
+ gtk_tree_model_get_iter( GTK_TREE_MODEL (model), &iter, path );
+ gtk_list_store_set( model, &iter, /* column */ 3, activeButton2 != 0, -1 );
+
+ ege_select_one_action_update_sensitive( textOrientationAction );
}
@@ -1387,7 +1480,7 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje
g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_align_mode_changed), holder );
}
- /* Orientation (Left to Right, Top to Bottom */
+ /* Writing mode (Horizontal, Vertical-LR, Vertical-RL) */
{
GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
@@ -1402,14 +1495,73 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje
gtk_list_store_append( model, &iter );
gtk_list_store_set( model, &iter,
- 0, _("Vertical"),
- 1, _("Vertical text"),
+ 0, _("Vertical — RL"),
+ 1, _("Vertical text — lines: right to left"),
2, INKSCAPE_ICON("format-text-direction-vertical"),
-1 );
+ gtk_list_store_append( model, &iter );
+ gtk_list_store_set( model, &iter,
+ 0, _("Vertical — LR"),
+ 1, _("Vertical text — lines: left to right"), // Mongolian!
+ 2, INKSCAPE_ICON("format-text-direction-vertical-lr"),
+ -1 );
+
+ EgeSelectOneAction* act = ege_select_one_action_new( "TextWritingModeAction", // Name
+ _("Writing mode"), // Label
+ _("Block progression"), // Tooltip
+ NULL, // Icon name
+ GTK_TREE_MODEL(model) ); // Model
+
+ g_object_set( act, "short_label", "NotUsed", NULL );
+ gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
+ g_object_set_data( holder, "TextWritingModeAction", act );
+
+ ege_select_one_action_set_appearance( act, "full" );
+ ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
+ g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
+ ege_select_one_action_set_icon_column( act, 2 );
+ ege_select_one_action_set_icon_size( act, secondarySize );
+ ege_select_one_action_set_tooltip_column( act, 1 );
+
+ gint mode = prefs->getInt("/tools/text/writing_mode", 0);
+ ege_select_one_action_set_active( act, mode );
+ g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_writing_mode_changed), holder );
+ }
+
+ /* Text (glyph) orientation (Auto (mixed), Upright, Sideways) */
+ {
+ GtkListStore* model = gtk_list_store_new( 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN );
+
+ GtkTreeIter iter;
+
+ gtk_list_store_append( model, &iter );
+ gtk_list_store_set( model, &iter,
+ 0, _("Auto"),
+ 1, _("Auto glyph orientation"),
+ 2, INKSCAPE_ICON("text-orientation-auto"),
+ 3, true,
+ -1 );
+
+ gtk_list_store_append( model, &iter );
+ gtk_list_store_set( model, &iter,
+ 0, _("Upright"),
+ 1, _("Upright glyph orientation"),
+ 2, INKSCAPE_ICON("text-orientation-upright"),
+ 3, true,
+ -1 );
+
+ gtk_list_store_append( model, &iter );
+ gtk_list_store_set( model, &iter,
+ 0, _("Sideways"),
+ 1, _("Sideways glyph orientation"),
+ 2, INKSCAPE_ICON("text-orientation-sideways"),
+ 3, true,
+ -1 );
+
EgeSelectOneAction* act = ege_select_one_action_new( "TextOrientationAction", // Name
- _("Orientation"), // Label
- _("Text orientation"), // Tooltip
+ _("Text orientation"), // Label
+ _("Text (glyph) orientation in vertical text."), // Tooltip
NULL, // Icon name
GTK_TREE_MODEL(model) ); // Model
@@ -1423,10 +1575,11 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje
ege_select_one_action_set_icon_column( act, 2 );
ege_select_one_action_set_icon_size( act, secondarySize );
ege_select_one_action_set_tooltip_column( act, 1 );
+ ege_select_one_action_set_sensitive_column( act, 3 );
- gint mode = prefs->getInt("/tools/text/orientation", 0);
+ gint mode = prefs->getInt("/tools/text/text_orientation", 0);
ege_select_one_action_set_active( act, mode );
- g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_orientation_mode_changed), holder );
+ g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_orientation_changed), holder );
}
/* Line height */
diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp
index a1c32352c..00c09e50e 100644
--- a/src/widgets/toolbox.cpp
+++ b/src/widgets/toolbox.cpp
@@ -478,6 +478,8 @@ static gchar const * ui_descr =
" <toolitem action='TextDyAction' />"
" <toolitem action='TextRotationAction' />"
" <separator />"
+ " <toolitem action='TextWritingModeAction' />"
+ " <separator />"
" <toolitem action='TextOrientationAction' />"
" </toolbar>"