Code

Super duper mega (fun!) commit: replaced encoding=utf-8 with fileencoding=utf-8 in...
[inkscape.git] / src / libnrtype / Layout-TNG-Compute.cpp
index cad2f9b19996a598e3dda61f6498c4f34d6b0934..7e684e7e39344dd4e9c18bc8ce0cd69280261a97 100644 (file)
@@ -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 <tspan> attributes. We change span when we encounter one.
@@ -444,22 +445,32 @@ class Layout::Calculator
 
             // we may also have y move orders to deal with here (dx, dy and rotate are done per span)
 
-            // Comment added:  1 June 2010:
-            // The first line in a normal <text> 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 <text> object. New "y" values are then stored in each <tspan> 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 <tspan>s created/edited by other programs.
+            // Comment updated:  23 July 2010:
+            // We must handle two cases:
+            //
+            //   1. Inkscape SVG where the first line is placed by the read-in "y" value and the
+            //      rest are determined by 'font-size' and 'line-height' (and not by any
+            //      y-kerning). <tspan>s in this case are marked by sodipodi:role="line". This
+            //      allows new lines to be inserted in the middle of a <text> object. On output,
+            //      new "y" values are calculated for each <tspan> that represents a new line. Line
+            //      spacing is already handled by the calling routine.
+            //
+            //   2. Plain SVG where each <text> or <tspan> is placed by its own "x" and "y" values.
+            //      Note that in this case Inkscape treats each <text> object with any included
+            //      <tspan>s as a single line of text. This can be confusing in the code below.
+
             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 empty or new line (sodipode:role="line")
                 if( _flow._characters.empty() ||
                     _flow._characters.back().chunk(&_flow).in_line != _flow._lines.size() - 1) {
 
+                    // This is the Inkscape SVG case.
+                    //
                     // If <tspan> "y" attribute is set, use it (initial "y" attributes in
-                    // <tspans> other than the first have already been stripped).
+                    // <tspans> other than the first have already been stripped for <tspans>
+                    // marked with role="line", see sp-text.cpp: SPText::_buildLayoutInput).
                     if( it_chunk->broken_spans.front().start.iter_span->y._set ) {
 
                         // Use set "y" attribute
@@ -474,9 +485,17 @@ class Layout::Calculator
                     }
 
                     // Reset relative y_offset ("dy" attribute is relative but should be reset at
-                    // the beginning of each <tspan>.)
+                    // the beginning of each line since each line will have a new "y" written out.)
                     _y_offset = 0.0;
 
+                } else {
+
+                    // This is the plain SVG case
+                    //
+                    // "x" and "y" are used to place text, simulating lines as necessary
+                    if( it_chunk->broken_spans.front().start.iter_span->y._set ) {
+                        _y_offset = it_chunk->broken_spans.front().start.iter_span->y.computed - new_line.baseline_y;
+                    }
                 }
             }
             _flow._chunks.push_back(new_chunk);
@@ -521,7 +540,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 +623,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 +1008,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 +1155,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));
@@ -1231,8 +1261,9 @@ bool Layout::Calculator::_findChunksForLine(ParagraphInfo const &para,
     UnbrokenSpanPosition span_pos;
     for( ; ; ) {
         std::vector<ScanlineMaker::ScanRun> scan_runs;
-        scan_runs = _scanline_maker->makeScanline(*line_height);
+        scan_runs = _scanline_maker->makeScanline(*line_height); // Only one line with "InfiniteScanlineMaker
         while (scan_runs.empty()) {
+            // Only used by ShapeScanlineMaker
             if (!_goToNextWrapShape()) return false;  // no more shapes to wrap in to
             scan_runs = _scanline_maker->makeScanline(*line_height);
         }
@@ -1599,4 +1630,4 @@ bool Layout::calculateFlow()
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :