namespace Text {
//#define IFTRACE(_code) _code
-//#define TRACE(_args) g_print _args
#define IFTRACE(_code)
-#define TRACE(_args)
+
+#define TRACE(_args) IFTRACE(g_print _args)
// ******* enum conversion tables
static Layout::EnumConversionItem const enum_convert_spstyle_direction_to_pango_direction[] = {
unsigned whitespace_count;
bool ends_with_whitespace;
double each_whitespace_width;
+ double letter_spacing; // Save so we can subtract from width at end of line (for center justification)
+ double word_spacing;
void setZero();
};
do {
PangoLogAttr const &char_attributes = _charAttributes(para, span->end);
- if (char_attributes.is_mandatory_break) {
+ if (char_attributes.is_mandatory_break && span->end != span->start) {
*last_emergency_break_span = *last_break_span = *span;
TRACE(("span %d end of para; width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count));
return false;
span->end.increment();
- if (span->width > maximum_width && !char_attributes.is_white) { // whitespaces don't matter, we can put as many as we want at eol
+ // Width should not include letter_spacing (or word_spacing) after last letter at end of line.
+ // word_spacing is attached to white space that is already removed from line end (?)
+ double test_width = span->width - text_source->style->letter_spacing.computed;
+
+ // Save letter_spacing and word_spacing for subtraction later if span is last span in line.
+ span->letter_spacing = text_source->style->letter_spacing.computed;
+ span->word_spacing = text_source->style->word_spacing.computed;
+
+ if (test_width > maximum_width && !char_attributes.is_white) { // whitespaces don't matter, we can put as many as we want at eol
TRACE(("span %d exceeded scanrun; width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count));
return false;
}
} while (span->end.char_byte != 0); // while we haven't wrapped to the next span
+
TRACE(("fitted span %d width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count));
return true;
}
case RIGHT:
return it_chunk->x - it_chunk->text_width;
case CENTER:
- return it_chunk->x - it_chunk->text_width / 2;
+ return it_chunk->x - it_chunk->text_width/ 2;
}
}
new_span.in_input_stream_item = unbroken_span.input_index;
new_span.baseline_shift = _y_offset;
new_span.block_progression = _block_progression;
- if (_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) {
- new_span.font = para.pango_items[unbroken_span.pango_item_index].font;
- new_span.font->Ref();
+ 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();
new_span.font_size = unbroken_span.font_size;
new_span.direction = para.pango_items[unbroken_span.pango_item_index].item->analysis.level & 1 ? RIGHT_TO_LEFT : LEFT_TO_RIGHT;
new_span.input_stream_first_character = Glib::ustring::const_iterator(unbroken_span.input_stream_first_character.base() + it_span->start.char_byte);
new_glyph.x = x + unbroken_span.glyph_string->glyphs[glyph_index].geometry.x_offset * font_size_multiplier;
new_glyph.y = _y_offset + unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset * font_size_multiplier;
new_glyph.width = unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * font_size_multiplier;
- if (new_glyph.width == 0)
+ 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);
// for some reason pango returns zero width for invalid glyph characters (those empty boxes), so go to freetype for the info
}
iter_source_text++;
char_index_in_unbroken_span++;
char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base();
- glyph_rotate = 0.0;
}
advance_width *= direction_sign;
@@ -832,10 +842,12 @@ void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) cons
} else if (_flow._input_stream[input_index]->Type() == TEXT_SOURCE) {
Layout::InputStreamTextSource *text_source = static_cast<Layout::InputStreamTextSource *>(_flow._input_stream[input_index]);
- PangoFontDescription *temp_descr = text_source->styleGetFontDescription();
- PangoAttribute *attribute_font_description = pango_attr_font_desc_new(temp_descr);
- pango_font_description_free(temp_descr);
+ // 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();
para_text.append(&*text_source->text_begin.base(), text_source->text_length); // build the combined text
attribute_font_description->end_index = para_text.bytes();
@@ -901,7 +913,8 @@ void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font
line_height->setZero();
*line_height_multiplier = 1.0;
}
- font->FontMetrics(line_height->ascent, line_height->descent, line_height->leading);
+ else
+ font->FontMetrics(line_height->ascent, line_height->descent, line_height->leading);
*line_height *= font_size;
// yet another borked SPStyle member that we're going to have to fix ourselves
@@ -927,7 +940,7 @@ void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font
}
break;
}
- if (style->object->parent == NULL) break;
+ if (style->object == NULL || style->object->parent == NULL) break;
style = style->object->parent->style;
if (style == NULL) break;
}
if (text_source->dy.size() > char_index_in_source) new_span.dx = text_source->dy[char_index_in_source];
}
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;
if (input_index == 0 && para->unbroken_spans.empty() && !new_span.y._set && _flow._input_wrap_shapes.empty()) {
// if we don't set an explicit y some of the automatic wrapping code takes over and moves the text vertically
// so that the top of the letters is at zero, not the baseline
// now we know the length, do some final calculations and add the UnbrokenSpan to the list
new_span.font_size = text_source->styleComputeFontSize();
if (new_span.text_bytes) {
- int const original_bidi_level = para->pango_items[pango_item_index].item->analysis.level;
- para->pango_items[pango_item_index].item->analysis.level = 0;
- // pango_shape() will reorder glyphs in rtl sections which messes us up because
- // the svg spec requires us to draw glyphs in character order
new_span.glyph_string = pango_glyph_string_new();
/* Some assertions intended to help diagnose bug #1277746. */
g_assert( 0 < new_span.text_bytes );
new_span.text_bytes,
¶->pango_items[pango_item_index].item->analysis,
new_span.glyph_string);
- para->pango_items[pango_item_index].item->analysis.level = original_bidi_level;
+
+ if (para->pango_items[pango_item_index].item->analysis.level & 1) {
+ // pango_shape() will reorder glyphs in rtl sections into visual order which messes
+ // us up because the svg spec requires us to draw glyphs in logical order
+ // let's reverse the glyphstring on a cluster-by-cluster basis
+ const unsigned nglyphs = new_span.glyph_string->num_glyphs;
+ std::vector<PangoGlyphInfo> infos(nglyphs);
+ std::vector<gint> clusters(nglyphs);
+ unsigned i, cluster_start = 0;
+
+ for (i = 0 ; i < nglyphs ; ++i) {
+ if (new_span.glyph_string->glyphs[i].attr.is_cluster_start) {
+ if (i != cluster_start) {
+ std::copy(&new_span.glyph_string->glyphs[cluster_start], &new_span.glyph_string->glyphs[i], infos.end() - i);
+ std::copy(&new_span.glyph_string->log_clusters[cluster_start], &new_span.glyph_string->log_clusters[i], clusters.end() - i);
+ }
+ cluster_start = i;
+ }
+ }
+ if (i != cluster_start) {
+ std::copy(&new_span.glyph_string->glyphs[cluster_start], &new_span.glyph_string->glyphs[i], infos.end() - i);
+ std::copy(&new_span.glyph_string->log_clusters[cluster_start], &new_span.glyph_string->log_clusters[i], clusters.end() - i);
+ }
+ std::copy(infos.begin(), infos.end(), new_span.glyph_string->glyphs);
+ std::copy(clusters.begin(), clusters.end(), new_span.glyph_string->log_clusters);
+ }
new_span.pango_item_index = pango_item_index;
_computeFontLineHeight(para->pango_items[pango_item_index].font, new_span.font_size, text_source->style, &new_span.line_height, &new_span.line_height_multiplier);
// TODO: metrics for vertical text
chunk_info->back().whitespace_count--;
}
+ if (!chunk_info->empty() && !chunk_info->back().broken_spans.empty() ) {
+ // for justification we need to discard line-spacing and word-spacing at end of the chunk
+ chunk_info->back().broken_spans.back().width -= chunk_info->back().broken_spans.back().letter_spacing;
+ chunk_info->back().text_width -= chunk_info->back().broken_spans.back().letter_spacing;
+ TRACE(("width after subtracting last letter_spacing: %f\n", chunk_info->back().broken_spans.back().width));
+ }
+
return true;
}
{
if (_flow._input_stream.empty())
return false;
- g_assert(_flow._input_stream.front()->Type() == TEXT_SOURCE);
+ /**
+ * hm, why do we want assert (crash) the application, now do simply return false
+ * \todo check if this is the correct behaviour
+ * g_assert(_flow._input_stream.front()->Type() == TEXT_SOURCE);
+ */
if (_flow._input_stream.front()->Type() != TEXT_SOURCE)
+ {
+ g_warning("flow text is not of type TEXT_SOURCE. Abort.");
return false;
-
+ }
TRACE(("begin calculateFlow()\n"));
_flow._clearOutputObjects();
}
para.free();
- if (_scanline_maker)
+ if (_scanline_maker) {
delete _scanline_maker;
+ _flow._input_truncated = false;
+ } else {
+ _flow._input_truncated = true;
+ }
return true;
}
void Layout::_calculateCursorShapeForEmpty()
{
- _empty_cursor_shape.position = NR::Point(0, 0);
+ _empty_cursor_shape.position = Geom::Point(0, 0);
_empty_cursor_shape.height = 0.0;
_empty_cursor_shape.rotation = 0.0;
if (_input_stream.empty() || _input_stream.front()->Type() != TEXT_SOURCE)
_empty_cursor_shape.rotation = caret_slope;
if (_input_wrap_shapes.empty()) {
- _empty_cursor_shape.position = NR::Point(text_source->x.empty() || !text_source->x.front()._set ? 0.0 : text_source->x.front().computed,
+ _empty_cursor_shape.position = Geom::Point(text_source->x.empty() || !text_source->x.front()._set ? 0.0 : text_source->x.front().computed,
text_source->y.empty() || !text_source->y.front()._set ? 0.0 : text_source->y.front().computed);
} else {
Direction block_progression = text_source->styleGetBlockProgression();
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)
- _empty_cursor_shape.position = NR::Point(scan_runs.front().y + font_size, scan_runs.front().x_start);
+ _empty_cursor_shape.position = Geom::Point(scan_runs.front().y + font_size, scan_runs.front().x_start);
else
- _empty_cursor_shape.position = NR::Point(scan_runs.front().x_start, scan_runs.front().y + font_size);
+ _empty_cursor_shape.position = Geom::Point(scan_runs.front().x_start, scan_runs.front().y + font_size);
}
}
}