From 9f10079c170c5903e3ea8dd56fedb6e98fef5c8b Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Fri, 21 May 2010 09:52:45 +0200 Subject: Subtract letter_spacing from width at end of line so center alignment works as expected. (bzr r9436) --- src/libnrtype/Layout-TNG-Compute.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'src/libnrtype') diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index f6b9688bb..518df152f 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -153,6 +153,8 @@ class Layout::Calculator 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(); }; @@ -340,12 +342,21 @@ class Layout::Calculator 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; } @@ -369,7 +380,7 @@ class Layout::Calculator 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; } } @@ -1337,6 +1348,13 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const ¶, 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; } -- cgit v1.2.3 From e527d5674fd65d7ce144144a2cbfbda279fdd183 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Tue, 1 Jun 2010 12:41:52 +0200 Subject: Prevent vertical kerning from changing line spacing. Fixes bug 516216. Further work is needed to allow vertical kerning across multiple lines. Fixed bugs: - https://launchpad.net/bugs/516216 (bzr r9466) --- src/libnrtype/Layout-TNG-Compute.cpp | 69 ++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 15 deletions(-) (limited to 'src/libnrtype') diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index 518df152f..cad2f9b19 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -76,7 +76,14 @@ class Layout::Calculator /** for y= attributes in tspan elements et al, we do the adjustment by moving each glyph individually by this number. The spec means that this is maintained across - paragraphs. */ + paragraphs. + + To do non-flow text layout, only the first "y" attribute is normally used. If there is only one + "y" attribute in a other than the first , it is ignored. This allows Inkscape to + insert a new line anywhere. On output, the Inkscape determined "y" is written out so other SVG + viewers know where to place the . + */ + double _y_offset; /** to stop pango from hinting its output, the font factory creates all fonts very large. @@ -158,7 +165,9 @@ class Layout::Calculator void setZero(); }; - /** The definition of a chunk used here is the same as that used in Layout. */ + /** The definition of a chunk used here is the same as that used in Layout: + A collection of contiguous broken spans on the same line. (One chunk per line + unless shape splits line into several sections... then one chunk per section. */ struct ChunkInfo { std::vector broken_spans; double scanrun_width; @@ -410,6 +419,7 @@ class Layout::Calculator */ void _outputLine(ParagraphInfo const ¶, LineHeight const &line_height, std::vector const &chunk_info) { + TRACE(("Start _outputLine\n")); if (chunk_info.empty()) { TRACE(("line too short to fit anything on it, go to next\n")); return; @@ -429,19 +439,45 @@ class Layout::Calculator // add the chunk to the list Layout::Chunk new_chunk; new_chunk.in_line = _flow._lines.size() - 1; + TRACE((" New chunk: in_line: %d\n", new_chunk.in_line)); new_chunk.left_x = _getChunkLeftWithAlignment(para, it_chunk, &add_to_each_whitespace); + // we may also have y move orders to deal with here (dx, dy and rotate are done per span) - if (!it_chunk->broken_spans.empty() // this one only happens for empty paragraphs - && it_chunk->broken_spans.front().start.char_byte == 0 - && it_chunk->broken_spans.front().start.iter_span->y._set) { - // if this is the start of a line, we should change the baseline rather than each glyph individually - if (_flow._characters.empty() || _flow._characters.back().chunk(&_flow).in_line != _flow._lines.size() - 1) { - new_line.baseline_y = it_chunk->broken_spans.front().start.iter_span->y.computed; - _flow._lines.back().baseline_y = new_line.baseline_y; + + // Comment added: 1 June 2010: + // The first line in a normal object is placed by the read-in "y" value. The rest are + // determined by Inkscape. This is to allow insertation of new lines in the middle + // of a object. New "y" values are then stored in each that represents + // a new line. The line spacing should depend only on font-size and line-height (and not + // on any y-kerning). Line spacing is already handled by the calling routine. Note that + // this may render improperly any SVG with s created/edited by other programs. + if (!it_chunk->broken_spans.empty() // Not empty paragraph + && it_chunk->broken_spans.front().start.char_byte == 0 ) { // Beginning of unbroken span + + // If empty or new line + if( _flow._characters.empty() || + _flow._characters.back().chunk(&_flow).in_line != _flow._lines.size() - 1) { + + // If "y" attribute is set, use it (initial "y" attributes in + // other than the first have already been stripped). + 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 y coordinate + _scanline_maker->setNewYCoordinate(new_line.baseline_y - line_height.ascent); + + } + + // Reset relative y_offset ("dy" attribute is relative but should be reset at + // the beginning of each .) _y_offset = 0.0; - _scanline_maker->setNewYCoordinate(new_line.baseline_y - line_height.ascent); - } else - _y_offset = it_chunk->broken_spans.front().start.iter_span->y.computed - new_line.baseline_y; + + } } _flow._chunks.push_back(new_chunk); @@ -466,7 +502,8 @@ class Layout::Calculator UnbrokenSpan const &unbroken_span = *it_span->start.iter_span; 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) + // 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.dy._set) _y_offset += unbroken_span.dy.computed; if (unbroken_span.rotate._set) glyph_rotate = unbroken_span.rotate.computed * (M_PI/180); @@ -984,6 +1021,7 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const } } else if (_flow._input_stream[input_index]->Type() == TEXT_SOURCE && pango_item_index < para->pango_items.size()) { Layout::InputStreamTextSource const *text_source = static_cast(_flow._input_stream[input_index]); + unsigned char_index_in_source = 0; unsigned span_start_byte_in_source = 0; @@ -1000,6 +1038,7 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const unsigned const text_source_bytes = ( text_source->text_end.base() - text_source->text_begin.base() - span_start_byte_in_source ); + TRACE(("New Span\n")); UnbrokenSpan new_span; new_span.text_bytes = std::min(text_source_bytes, pango_item_bytes); new_span.input_stream_first_character = Glib::ustring::const_iterator(text_source->text_begin.base() + span_start_byte_in_source); @@ -1385,7 +1424,7 @@ bool Layout::Calculator::calculate() _createFirstScanlineMaker(); ParagraphInfo para; - LineHeight line_height; // needs to be maintained across paragraphs to be able to deal with blank paras (this is wrong) + LineHeight 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) { @@ -1426,7 +1465,7 @@ bool Layout::Calculator::calculate() break; // out of shapes to wrap in to _outputLine(para, line_height, line_chunk_info); - _scanline_maker->completeLine(); + _scanline_maker->completeLine(); // Increments y by line height } while (span_pos.iter_span != para.unbroken_spans.end()); TRACE(("para %d end\n\n", _flow._paragraphs.size() - 1)); -- cgit v1.2.3 From 763ecf36ae7377cd9c1fadefbe0c4c24b37a3ab0 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Fri, 4 Jun 2010 21:41:52 +0200 Subject: Add support for baseline-shift attribute (all possible values). Add superscript and subscript support to Text toolbar. Missing icons for superscript and subscript. Known bug: adding a character to a superscript or subscript text string resets baseline-shift attribute. (bzr r9475) --- src/libnrtype/FontInstance.cpp | 3 ++- src/libnrtype/Layout-TNG-Compute.cpp | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) (limited to 'src/libnrtype') diff --git a/src/libnrtype/FontInstance.cpp b/src/libnrtype/FontInstance.cpp index b958e899d..bdf700346 100644 --- a/src/libnrtype/FontInstance.cpp +++ b/src/libnrtype/FontInstance.cpp @@ -412,7 +412,7 @@ void font_instance::InitTheFace() SetGraphicsMode(daddy->hScreenDC, GM_COMPATIBLE); SelectObject(daddy->hScreenDC,theFace); #else - theFace=pango_ft2_font_get_face(pFont); + theFace=pango_ft2_font_get_face(pFont); // Deprecated, use pango_fc_font_lock_face() instead if ( theFace ) { FT_Select_Charmap(theFace,ft_encoding_unicode) && FT_Select_Charmap(theFace,ft_encoding_symbol); } @@ -662,6 +662,7 @@ bool font_instance::FontMetrics(double &ascent,double &descent,double &leading) ascent=fabs(otm.otmAscent*scale); descent=fabs(otm.otmDescent*scale); leading=fabs(otm.otmLineGap*scale); + //otmSubscriptSize, otmSubscriptOffset, otmSuperscriptSize, otmSuperscriptOffset, #else if ( theFace->units_per_EM == 0 ) { return false; // bitmap font diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index cad2f9b19..192596ecf 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -124,8 +124,9 @@ class Layout::Calculator unsigned input_index; /// index into Layout::_input_stream Glib::ustring::const_iterator input_stream_first_character; double font_size; - LineHeight line_height; + LineHeight line_height; /// This is not the CSS line-height attribute! double line_height_multiplier; /// calculated from the font-height css property + double baseline_shift; /// calculated from the baseline-shift css property unsigned text_bytes; unsigned char_index_in_para; /// the index of the first character in this span in the paragraph, for looking up char_attributes SVGLength x, y, dx, dy, rotate; // these are reoriented copies of the attributes. We change span when we encounter one. @@ -521,7 +522,7 @@ class Layout::Calculator new_span.in_chunk = _flow._chunks.size() - 1; new_span.line_height = unbroken_span.line_height; new_span.in_input_stream_item = unbroken_span.input_index; - new_span.baseline_shift = _y_offset; + new_span.baseline_shift = 0.0; new_span.block_progression = _block_progression; if ((_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) && (new_span.font = para.pango_items[unbroken_span.pango_item_index].font)) { @@ -604,11 +605,16 @@ class Layout::Calculator if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) { new_glyph.x = x + unbroken_span.glyph_string->glyphs[glyph_index].geometry.x_offset * font_size_multiplier + new_span.line_height.ascent; - new_glyph.y = _y_offset + (unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset - unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * 0.5) * font_size_multiplier; + new_glyph.y = _y_offset - + unbroken_span.baseline_shift + + (unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset - + unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * 0.5) * font_size_multiplier; new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[glyph_index].glyph, true); } else { new_glyph.x = x + unbroken_span.glyph_string->glyphs[glyph_index].geometry.x_offset * font_size_multiplier; - new_glyph.y = _y_offset + unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset * font_size_multiplier; + new_glyph.y = _y_offset - + unbroken_span.baseline_shift + + unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset * font_size_multiplier; new_glyph.width = unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * font_size_multiplier; if ((new_glyph.width == 0) && (para.pango_items[unbroken_span.pango_item_index].font)) new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[glyph_index].glyph, false); @@ -984,6 +990,7 @@ void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font *line_height_multiplier = LINE_HEIGHT_NORMAL * font_size / line_height->total(); } + /** * Split the paragraph into spans. Also call pango_shape() on them. * @@ -1130,6 +1137,11 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const } new_span.pango_item_index = pango_item_index; _computeFontLineHeight(para->pango_items[pango_item_index].font, new_span.font_size, text_source->style, &new_span.line_height, &new_span.line_height_multiplier); + + // At some point we may want to calculate baseline_shift here (to take advantage + // of otm features like superscript baseline), but for now we use style baseline_shift. + new_span.baseline_shift = text_source->style->baseline_shift.computed; + // TODO: metrics for vertical text TRACE(("add text span %d \"%s\"\n", para->unbroken_spans.size(), text_source->text->raw().substr(span_start_byte_in_source, new_span.text_bytes).c_str())); TRACE((" %d glyphs\n", new_span.glyph_string->num_glyphs)); -- cgit v1.2.3