summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJabier Arraiza <jabier.arraiza@marker.es>2017-06-12 18:20:56 +0000
committerJabier Arraiza <jabier.arraiza@marker.es>2017-06-12 18:20:56 +0000
commit59de64ad8af1ef0527100ffe09b4e85617c3226c (patch)
tree89ed20b19fcefb4e71578a37a235a15aa9450933 /src
parentadding last changes (diff)
parentSmall fixes to README (diff)
downloadinkscape-59de64ad8af1ef0527100ffe09b4e85617c3226c.tar.gz
inkscape-59de64ad8af1ef0527100ffe09b4e85617c3226c.zip
Updating to master
Diffstat (limited to 'src')
-rw-r--r--src/libnrtype/Layout-TNG-Compute.cpp1423
-rw-r--r--src/libnrtype/Layout-TNG-Input.cpp113
-rw-r--r--src/libnrtype/Layout-TNG-Output.cpp12
-rw-r--r--src/libnrtype/Layout-TNG.h4
-rw-r--r--src/live_effects/lpe-bool.h2
-rw-r--r--src/sp-flowtext.cpp4
-rw-r--r--src/sp-text.cpp8
7 files changed, 827 insertions, 739 deletions
diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp
index 8e173a7c7..63364ab34 100644
--- a/src/libnrtype/Layout-TNG-Compute.cpp
+++ b/src/libnrtype/Layout-TNG-Compute.cpp
@@ -19,6 +19,8 @@
namespace Inkscape {
namespace Text {
+//#define DEBUG_LAYOUT_TNG_COMPUTE
+
//#define IFTRACE(_code) _code
#define IFTRACE(_code)
@@ -53,27 +55,22 @@ class Layout::Calculator
{
class SpanPosition;
friend class SpanPosition;
-
Layout &_flow;
-
ScanlineMaker *_scanline_maker;
-
unsigned _current_shape_index; /// index into Layout::_input_wrap_shapes
-
PangoContext *_pango_context;
-
Direction _block_progression;
- /** 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.
-
- To do non-flow text layout, only the first "y" attribute is normally used. If there is only one
- "y" attribute in a <tspan> other than the first <tspan>, 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 <tspans>.
- */
-
+ /**
+ * 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.
+ *
+ * To do non-flow text layout, only the first "y" attribute is normally used. If there is only one
+ * "y" attribute in a <tspan> other than the first <tspan>, 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 <tspans>.
+ */
double _y_offset;
/** to stop pango from hinting its output, the font factory creates all fonts very large.
@@ -87,7 +84,20 @@ class Layout::Calculator
Layout *sub_flow; // this is only set for the first input item in a sub-flow
InputItemInfo() : in_sub_flow(false), sub_flow(NULL) {}
- void free();
+
+ /* fixme: I don't like the fact that InputItemInfo etc. use the default copy constructor and
+ * operator= (and thus don't involve incrementing reference counts), yet they provide a free method
+ * that does delete or Unref.
+ *
+ * I suggest using the garbage collector to manage deletion.
+ */
+ void free()
+ {
+ if (sub_flow) {
+ delete sub_flow;
+ sub_flow = NULL;
+ }
+ }
};
/** Temporary storage associated with each item returned by the call to
@@ -97,17 +107,36 @@ class Layout::Calculator
font_instance *font;
PangoItemInfo() : item(NULL), font(NULL) {}
- void free();
+
+ /* fixme: I don't like the fact that InputItemInfo etc. use the default copy constructor and
+ * operator= (and thus don't involve incrementing reference counts), yet they provide a free method
+ * that does delete or Unref.
+ *
+ * I suggest using the garbage collector to manage deletion.
+ */
+ void free()
+ {
+ if (item) {
+ pango_item_free(item);
+ item = NULL;
+ }
+ if (font) {
+ font->Unref();
+ font = NULL;
+ }
+ }
};
+
/** These spans have approximately the same definition as that used for
- Layout::Span (constant font, direction, etc), except that they are from
- before we have located the line breaks, so bear no relation to chunks.
- They are guaranteed to be in at most one PangoItem (spans with no text in
- them will not have an associated PangoItem), exactly one input source and
- will only have one change of x, y, dx, dy or rotate attribute, which will
- be at the beginning. An UnbrokenSpan can cross a chunk boundary, c.f.
- BrokenSpan. */
+ * Layout::Span (constant font, direction, etc), except that they are from
+ * before we have located the line breaks, so bear no relation to chunks.
+ * They are guaranteed to be in at most one PangoItem (spans with no text in
+ * them will not have an associated PangoItem), exactly one input source and
+ * will only have one change of x, y, dx, dy or rotate attribute, which will
+ * be at the beginning. An UnbrokenSpan can cross a chunk boundary, c.f.
+ * BrokenSpan.
+ */
struct UnbrokenSpan {
PangoGlyphString *glyph_string;
int pango_item_index; /// index into _para.pango_items, or -1 if this is style only
@@ -123,10 +152,48 @@ class Layout::Calculator
SVGLength x, y, dx, dy, rotate; // these are reoriented copies of the <tspan> attributes. We change span when we encounter one.
UnbrokenSpan() : glyph_string(NULL) {}
- void free() {if (glyph_string) pango_glyph_string_free(glyph_string); glyph_string = NULL;}
+ void free()
+ {
+ if (glyph_string)
+ pango_glyph_string_free(glyph_string);
+ glyph_string = NULL;
+ }
};
- /** a useful little iterator for moving char-by-char across spans. */
+
+ /** Used to provide storage for anything that applies to the current
+ paragraph only. Since we're only processing one paragraph at a time,
+ there's only one instantiation of this struct, on the stack of
+ calculate(). */
+ struct ParagraphInfo {
+ unsigned first_input_index; ///< Index into Layout::_input_stream.
+ Direction direction;
+ Alignment alignment;
+ std::vector<InputItemInfo> input_items;
+ std::vector<PangoItemInfo> pango_items;
+ std::vector<PangoLogAttr> char_attributes; ///< For every character in the paragraph.
+ std::vector<UnbrokenSpan> unbroken_spans;
+
+ template<typename T> static void free_sequence(T &seq)
+ {
+ for (typename T::iterator it(seq.begin()); it != seq.end(); ++it) {
+ it->free();
+ }
+ seq.clear();
+ }
+
+ void free()
+ {
+ free_sequence(input_items);
+ free_sequence(pango_items);
+ free_sequence(unbroken_spans);
+ }
+ };
+
+
+ /**
+ * A useful little iterator for moving char-by-char across spans.
+ */
struct UnbrokenSpanPosition {
std::vector<UnbrokenSpan>::iterator iter_span;
unsigned char_byte;
@@ -140,9 +207,11 @@ class Layout::Calculator
{return char_byte != other.char_byte || iter_span != other.iter_span;}
};
- /** The line breaking algorithm will convert each UnbrokenSpan into one
- or more of these. A BrokenSpan will never cross a chunk boundary, c.f.
- UnbrokenSpan. */
+ /**
+ * The line breaking algorithm will convert each UnbrokenSpan into one
+ * or more of these. A BrokenSpan will never cross a chunk boundary,
+ * c.f. UnbrokenSpan.
+ */
struct BrokenSpan {
UnbrokenSpanPosition start;
UnbrokenSpanPosition end; // the end of this will always be the same as the start of the next
@@ -168,83 +237,11 @@ class Layout::Calculator
int whitespace_count;
};
- /** Used to provide storage for anything that applies to the current
- paragraph only. Since we're only processing one paragraph at a time,
- there's only one instantiation of this struct, on the stack of
- calculate(). */
- struct ParagraphInfo {
- unsigned first_input_index; ///< Index into Layout::_input_stream.
- Direction direction;
- Alignment alignment;
- std::vector<InputItemInfo> input_items;
- std::vector<PangoItemInfo> pango_items;
- std::vector<PangoLogAttr> char_attributes; ///< For every character in the paragraph.
- std::vector<UnbrokenSpan> unbroken_spans;
-
- template<typename T> static void free_sequence(T &seq);
- void free();
- };
-
-/* *********************************************************************************************************/
-// Initialisation of ParagraphInfo structure
-
-
-#if 0 /* unused */
- void _initialiseInputItems(ParagraphInfo *para) const;
-#endif
-
void _buildPangoItemizationForPara(ParagraphInfo *para) const;
-
- // Returns line_height_multiplier
- static double _computeFontLineHeight( SPStyle const *style );
-
+ static double _computeFontLineHeight( SPStyle const *style ); // Returns line_height_multiplier
unsigned _buildSpansForPara(ParagraphInfo *para) const;
-
-/* *********************************************************************************************************/
-// Per-line functions
-
-/**
- * For debugging, not called in distributed code
- *
- * Input: para->first_input_index, para->pango_items
- */
-static void dumpPangoItemsOut(ParagraphInfo *para){
- std::cout << "Pango items: " << para->pango_items.size() << std::endl;
- font_factory * factory = font_factory::Default();
- for(unsigned pidx = 0 ; pidx < para->pango_items.size(); pidx++){
- std::cout
- << "idx: " << pidx
- << " offset: "
- << para->pango_items[pidx].item->offset
- << " length: "
- << para->pango_items[pidx].item->length
- << " font: "
- << factory->ConstructFontSpecification( para->pango_items[pidx].font )
- << std::endl;
- }
-}
-
-/**
- * For debugging, not called in distributed code
- *
- * Input: para->first_input_index, para->pango_items
- */
-static void dumpUnbrokenSpans(ParagraphInfo *para){
- std::cout << "Unbroken Spans: " << para->unbroken_spans.size() << std::endl;
- for(unsigned uidx = 0 ; uidx < para->unbroken_spans.size(); uidx++){
- std::cout
- << "idx: " << uidx
- << " pango_item_index: " << para->unbroken_spans[uidx].pango_item_index
- << " input_index: " << para->unbroken_spans[uidx].input_index
- << " char_index_in_para: " << para->unbroken_spans[uidx].char_index_in_para
- << " text_bytes: " << para->unbroken_spans[uidx].text_bytes
- << std::endl;
- }
-}
-
-
-
bool _goToNextWrapShape();
+ void _createFirstScanlineMaker();
bool _findChunksForLine(ParagraphInfo const &para,
UnbrokenSpanPosition *start_span_pos,
@@ -252,12 +249,6 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
FontMetrics *line_box_height,
FontMetrics const *strut_height);
- static inline PangoLogAttr const &_charAttributes(ParagraphInfo const &para,
- UnbrokenSpanPosition const &span_pos)
- {
- return para.char_attributes[span_pos.iter_span->char_index_in_para + span_pos.char_index];
- }
-
bool _buildChunksInScanRun(ParagraphInfo const &para,
UnbrokenSpanPosition const &start_span_pos,
ScanlineMaker::ScanRun const &scan_run,
@@ -265,664 +256,675 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
FontMetrics *line_height,
FontMetrics const *strut_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.
- Measuring will stop if maximum_width is reached and in that case the
- function will return false. In other cases where a line break must be
- done immediately the function will also return false. On return
- \a last_break_span will contain the vital statistics for the span only
- up to the last line breaking change. If there are no line breaking
- characters in the span then \a last_break_span will not be altered.
- Similarly, \a last_emergency_break_span will contain the vital
- statistics for the span up to the last inter-character boundary,
- or will be unaltered if there is none. */
- bool _measureUnbrokenSpan(ParagraphInfo const &para, BrokenSpan *span, BrokenSpan *last_break_span, BrokenSpan *last_emergency_break_span, double maximum_width) const
+ bool _measureUnbrokenSpan(ParagraphInfo const &para,
+ BrokenSpan *span,
+ BrokenSpan *last_break_span,
+ BrokenSpan *last_emergency_break_span,
+ double maximum_width) const;
+
+ double _getChunkLeftWithAlignment(ParagraphInfo const &para,
+ std::vector<ChunkInfo>::const_iterator it_chunk,
+ double *add_to_each_whitespace) const;
+
+ void _outputLine(ParagraphInfo const &para,
+ FontMetrics const &line_height,
+ std::vector<ChunkInfo> const &chunk_info);
+
+ static inline PangoLogAttr const &_charAttributes(ParagraphInfo const &para,
+ UnbrokenSpanPosition const &span_pos)
{
- TRACE((" start _measureUnbrokenSpan %g\n", maximum_width));
- span->setZero();
+ return para.char_attributes[span_pos.iter_span->char_index_in_para + span_pos.char_index];
+ }
- 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;
- }
- }
+#ifdef DEBUG_LAYOUT_TNG_COMPUTE
+ static void dumpPangoItemsOut(ParagraphInfo *para);
+ static void dumpUnbrokenSpans(ParagraphInfo *para){
+#endif //DEBUG_LAYOUT_TNG_COMPUTE
- if (span->start.iter_span->pango_item_index == -1) {
- // if this is a style-only span there's no text in it
- // so we don't need to do very much at all
- span->end.iter_span++;
- return true;
+public:
+ Calculator(Layout *text_flow)
+ : _flow(*text_flow) {}
+
+ bool calculate();
+};
+
+
+/**
+ * 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.
+ * Measuring will stop if maximum_width is reached and in that case the
+ * function will return false. In other cases where a line break must be
+ * done immediately the function will also return false. On return
+ * \a last_break_span will contain the vital statistics for the span only
+ * up to the last line breaking change. If there are no line breaking
+ * characters in the span then \a last_break_span will not be altered.
+ * Similarly, \a last_emergency_break_span will contain the vital
+ * statistics for the span up to the last inter-character boundary,
+ * or will be unaltered if there is none.
+ */
+bool Layout::Calculator::_measureUnbrokenSpan(ParagraphInfo const &para,
+ BrokenSpan *span,
+ BrokenSpan *last_break_span,
+ BrokenSpan *last_emergency_break_span,
+ double maximum_width) const
+{
+ TRACE((" start _measureUnbrokenSpan %g\n", maximum_width));
+ 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 (_flow._input_stream[span->start.iter_span->input_index]->Type() == CONTROL_CODE) {
- InputStreamControlCode const *control_code = static_cast<InputStreamControlCode const *>(_flow._input_stream[span->start.iter_span->input_index]);
- if (control_code->code == SHAPE_BREAK || control_code->code == PARAGRAPH_BREAK) {
- *last_emergency_break_span = *last_break_span = *span;
- return false;
- }
- if (control_code->code == ARBITRARY_GAP) {
- if (span->width + control_code->width > maximum_width)
- return false;
- TRACE((" fitted control code, width = %f\n", control_code->width));
- span->width += control_code->width;
- span->end.increment();
- }
- return true;
+ if (span->start.iter_span->pango_item_index == -1) {
+ // if this is a style-only span there's no text in it
+ // so we don't need to do very much at all
+ span->end.iter_span++;
+ return true;
+ }
+ if (_flow._input_stream[span->start.iter_span->input_index]->Type() == CONTROL_CODE) {
+ InputStreamControlCode const *control_code = static_cast<InputStreamControlCode const *>(_flow._input_stream[span->start.iter_span->input_index]);
+ if (control_code->code == SHAPE_BREAK || control_code->code == PARAGRAPH_BREAK) {
+ *last_emergency_break_span = *last_break_span = *span;
+ return false;
+ }
+ if (control_code->code == ARBITRARY_GAP) {
+ if (span->width + control_code->width > maximum_width)
+ return false;
+ TRACE((" fitted control code, width = %f\n", control_code->width));
+ span->width += control_code->width;
+ span->end.increment();
}
+ return true;
- if (_flow._input_stream[span->start.iter_span->input_index]->Type() != TEXT_SOURCE)
- return true; // never happens
+ }
- InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream[span->start.iter_span->input_index]);
+ if (_flow._input_stream[span->start.iter_span->input_index]->Type() != TEXT_SOURCE)
+ return true; // never happens
- if (_directions_are_orthogonal(_block_progression, text_source->styleGetBlockProgression())) {
- // TODO: block-progression altered in the middle
- // Measure the precomputed flow from para.input_items
- span->end.iter_span++; // for now, skip to the next span
- return true;
- }
+ InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream[span->start.iter_span->input_index]);
- // a normal span going with a normal block-progression
- double font_size_multiplier = span->start.iter_span->font_size / (PANGO_SCALE * _font_factory_size_multiplier);
- double soft_hyphen_glyph_width = 0.0;
- bool soft_hyphen_in_word = false;
- bool is_soft_hyphen = false;
- IFTRACE(int char_count = 0);
-
- // if we're not at the start of the span we need to pre-init glyph_index
- span->start_glyph_index = 0;
- while (span->start_glyph_index < (unsigned)span->start.iter_span->glyph_string->num_glyphs
- && span->start.iter_span->glyph_string->log_clusters[span->start_glyph_index] < (int)span->start.char_byte)
- span->start_glyph_index++;
- span->end_glyph_index = span->start_glyph_index;
-
- // go char-by-char summing the width, while keeping track of the previous break point
- do {
- PangoLogAttr const &char_attributes = _charAttributes(para, span->end);
-
- if (char_attributes.is_mandatory_break && span->end != span->start) {
- TRACE((" is_mandatory_break ************\n"));
- *last_emergency_break_span = *last_break_span = *span;
- TRACE((" span %ld end of para; width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count));
- return false;
- }
+ if (_directions_are_orthogonal(_block_progression, text_source->styleGetBlockProgression())) {
+ // TODO: block-progression altered in the middle
+ // Measure the precomputed flow from para.input_items
+ span->end.iter_span++; // for now, skip to the next span
+ return true;
+ }
- if (char_attributes.is_line_break) {
- TRACE((" is_line_break ************\n"));
- // a suitable position to break at, record where we are
- *last_emergency_break_span = *last_break_span = *span;
- if (soft_hyphen_in_word) {
- // if there was a previous soft hyphen we're not going to need it any more so we can remove it
- span->width -= soft_hyphen_glyph_width;
- if (!is_soft_hyphen)
- soft_hyphen_in_word = false;
- }
- } else if (char_attributes.is_char_break) {
- *last_emergency_break_span = *span;
+ // a normal span going with a normal block-progression
+ double font_size_multiplier = span->start.iter_span->font_size / (PANGO_SCALE * _font_factory_size_multiplier);
+ double soft_hyphen_glyph_width = 0.0;
+ bool soft_hyphen_in_word = false;
+ bool is_soft_hyphen = false;
+ IFTRACE(int char_count = 0);
+
+ // if we're not at the start of the span we need to pre-init glyph_index
+ span->start_glyph_index = 0;
+ while (span->start_glyph_index < (unsigned)span->start.iter_span->glyph_string->num_glyphs
+ && span->start.iter_span->glyph_string->log_clusters[span->start_glyph_index] < (int)span->start.char_byte)
+ span->start_glyph_index++;
+ span->end_glyph_index = span->start_glyph_index;
+
+ // go char-by-char summing the width, while keeping track of the previous break point
+ do {
+ PangoLogAttr const &char_attributes = _charAttributes(para, span->end);
+
+ if (char_attributes.is_mandatory_break && span->end != span->start) {
+ TRACE((" is_mandatory_break ************\n"));
+ *last_emergency_break_span = *last_break_span = *span;
+ TRACE((" span %ld end of para; width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count));
+ return false;
+ }
+
+ if (char_attributes.is_line_break) {
+ TRACE((" is_line_break ************\n"));
+ // a suitable position to break at, record where we are
+ *last_emergency_break_span = *last_break_span = *span;
+ if (soft_hyphen_in_word) {
+ // if there was a previous soft hyphen we're not going to need it any more so we can remove it
+ span->width -= soft_hyphen_glyph_width;
+ if (!is_soft_hyphen)
+ soft_hyphen_in_word = false;
}
- // todo: break between chars if necessary (ie no word breaks present) when doing rectangular flowing
+ } else if (char_attributes.is_char_break) {
+ *last_emergency_break_span = *span;
+ }
+ // todo: break between chars if necessary (ie no word breaks present) when doing rectangular flowing
- // sum the glyph widths, letter spacing, word spacing, and textLength adjustment to get the character width
- double char_width = 0.0;
- while (span->end_glyph_index < (unsigned)span->end.iter_span->glyph_string->num_glyphs
- && span->end.iter_span->glyph_string->log_clusters[span->end_glyph_index] <= (int)span->end.char_byte) {
- if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) {
- // Vertical text
+ // sum the glyph widths, letter spacing, word spacing, and textLength adjustment to get the character width
+ double char_width = 0.0;
+ while (span->end_glyph_index < (unsigned)span->end.iter_span->glyph_string->num_glyphs
+ && span->end.iter_span->glyph_string->log_clusters[span->end_glyph_index] <= (int)span->end.char_byte) {
+ 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);
- }
+ 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 {
- // Horizontal text
- char_width += font_size_multiplier * span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].geometry.width;
+ // 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);
}
- span->end_glyph_index++;
- }
- if (char_attributes.is_cursor_position)
- char_width += text_source->style->letter_spacing.computed * _flow.getTextLengthMultiplierDue();
- if (char_attributes.is_white)
- char_width += text_source->style->word_spacing.computed * _flow.getTextLengthMultiplierDue();
- char_width += _flow.getTextLengthIncrementDue();
- span->width += char_width;
- IFTRACE(char_count++);
-
- if (char_attributes.is_white) {
- span->whitespace_count++;
- span->each_whitespace_width = char_width;
+ } else {
+ // Horizontal text
+ char_width += font_size_multiplier * span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].geometry.width;
}
- span->ends_with_whitespace = char_attributes.is_white;
+ span->end_glyph_index++;
+ }
+ if (char_attributes.is_cursor_position)
+ char_width += text_source->style->letter_spacing.computed * _flow.getTextLengthMultiplierDue();
+ if (char_attributes.is_white)
+ char_width += text_source->style->word_spacing.computed * _flow.getTextLengthMultiplierDue();
+ char_width += _flow.getTextLengthIncrementDue();
+ span->width += char_width;
+ IFTRACE(char_count++);
+
+ if (char_attributes.is_white) {
+ span->whitespace_count++;
+ span->each_whitespace_width = char_width;
+ }
+ span->ends_with_whitespace = char_attributes.is_white;
- is_soft_hyphen = (UNICODE_SOFT_HYPHEN == *Glib::ustring::const_iterator(span->end.iter_span->input_stream_first_character.base() + span->end.char_byte));
- if (is_soft_hyphen)
- soft_hyphen_glyph_width = char_width;
+ is_soft_hyphen = (UNICODE_SOFT_HYPHEN == *Glib::ustring::const_iterator(span->end.iter_span->input_stream_first_character.base() + span->end.char_byte));
+ if (is_soft_hyphen)
+ soft_hyphen_glyph_width = char_width;
- span->end.increment();
+ span->end.increment();
- // 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;
+ // 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;
+ // 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 %ld exceeded scanrun; width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count));
- return false;
- }
+ 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 %ld 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
+ } while (span->end.char_byte != 0); // while we haven't wrapped to the next span
- TRACE((" fitted span %ld width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count));
- TRACE((" end _measureUnbrokenSpan %g\n", maximum_width));
- return true;
- }
+ TRACE((" fitted span %ld width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count));
+ TRACE((" end _measureUnbrokenSpan %g\n", maximum_width));
+ return true;
+}
/* *********************************************************************************************************/
// Per-line functions (output)
- /** Uses the paragraph alignment and the chunk information to work out
- where the actual left of the final chunk must be. Also sets
- \a add_to_each_whitespace to be the amount of x to add at each
- whitespace character to make full justification work. */
- double _getChunkLeftWithAlignment(ParagraphInfo const &para, std::vector<ChunkInfo>::const_iterator it_chunk, double *add_to_each_whitespace) const
- {
- *add_to_each_whitespace = 0.0;
- if (_flow._input_wrap_shapes.empty()) {
- switch (para.alignment) {
- case FULL:
- case LEFT:
- default:
- return it_chunk->x;
- case RIGHT:
- return it_chunk->x - it_chunk->text_width;
- case CENTER:
- return it_chunk->x - it_chunk->text_width/ 2;
- }
- }
-
+/** Uses the paragraph alignment and the chunk information to work out
+ * where the actual left of the final chunk must be. Also sets
+ * \a add_to_each_whitespace to be the amount of x to add at each
+ * whitespace character to make full justification work.
+ */
+double Layout::Calculator::_getChunkLeftWithAlignment(ParagraphInfo const &para,
+ std::vector<ChunkInfo>::const_iterator it_chunk,
+ double *add_to_each_whitespace) const
+{
+ *add_to_each_whitespace = 0.0;
+ if (_flow._input_wrap_shapes.empty()) {
switch (para.alignment) {
case FULL:
- if (!it_chunk->broken_spans.empty()
- && it_chunk->broken_spans.back().end.iter_span != para.unbroken_spans.end()) { // don't justify the last chunk in the para
- if (it_chunk->whitespace_count)
- *add_to_each_whitespace = (it_chunk->scanrun_width - it_chunk->text_width) / it_chunk->whitespace_count;
- //else
- //add_to_each_charspace = something
- }
- return it_chunk->x;
case LEFT:
default:
return it_chunk->x;
case RIGHT:
- return it_chunk->x + it_chunk->scanrun_width - it_chunk->text_width;
+ return it_chunk->x - it_chunk->text_width;
case CENTER:
- return it_chunk->x + (it_chunk->scanrun_width - it_chunk->text_width) / 2;
+ return it_chunk->x - it_chunk->text_width/ 2;
}
}
- /** Once we've got here we have finished making changes to the line and
- are ready to output the final result to #_flow. This method takes its
- input parameters and does that.
- */
- void _outputLine(ParagraphInfo const &para, FontMetrics const &line_height, std::vector<ChunkInfo> const &chunk_info)
- {
- TRACE((" Start _outputLine: ascent %f, descent %f, top of box %f\n", line_height.ascent, line_height.descent, _scanline_maker->yCoordinate() ));
- if (chunk_info.empty()) {
- TRACE((" line too short to fit anything on it, go to next\n"));
- return;
- }
+ switch (para.alignment) {
+ case FULL:
+ if (!it_chunk->broken_spans.empty()
+ && it_chunk->broken_spans.back().end.iter_span != para.unbroken_spans.end()) { // don't justify the last chunk in the para
+ if (it_chunk->whitespace_count)
+ *add_to_each_whitespace = (it_chunk->scanrun_width - it_chunk->text_width) / it_chunk->whitespace_count;
+ //else
+ //add_to_each_charspace = something
+ }
+ return it_chunk->x;
+ case LEFT:
+ default:
+ return it_chunk->x;
+ case RIGHT:
+ return it_chunk->x + it_chunk->scanrun_width - it_chunk->text_width;
+ case CENTER:
+ return it_chunk->x + (it_chunk->scanrun_width - it_chunk->text_width) / 2;
+ }
+}
- // we've finished fiddling about with ascents and descents: create the output
- 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();
-
- // The y coordinate is at the beginning edge of the line box (top for horizontal text, left
- // edge for vertical lr text, right edge for vertical rl text. We align, by default to the
- // alphabetic baseline for horizontal text and the central baseline for vertical text.
- if( _block_progression == RIGHT_TO_LEFT ) {
- // Vertical text, use em box center as baseline
- new_line.baseline_y -= 0.5 * line_height.emSize();
- } else if ( _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.getTypoAscent();
- }
+/**
+ * Once we've got here we have finished making changes to the line
+ * and are ready to output the final result to #_flow.
+ * This method takes its input parameters and does that.
+ */
+void Layout::Calculator::_outputLine(ParagraphInfo const &para,
+ FontMetrics const &line_height,
+ std::vector<ChunkInfo> const &chunk_info)
+{
+ TRACE((" Start _outputLine: ascent %f, descent %f, top of box %f\n", line_height.ascent, line_height.descent, _scanline_maker->yCoordinate() ));
+ if (chunk_info.empty()) {
+ TRACE((" line too short to fit anything on it, go to next\n"));
+ return;
+ }
- new_line.in_shape = _current_shape_index;
- _flow._lines.push_back(new_line);
-
- for (std::vector<ChunkInfo>::const_iterator it_chunk = chunk_info.begin() ; it_chunk != chunk_info.end() ; it_chunk++) {
-
- double add_to_each_whitespace;
- // 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)
-
- // 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 (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 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;
-
- double top_of_line_box = new_line.baseline_y;
- if( _block_progression == RIGHT_TO_LEFT ) {
- // Vertical text, use em box center as baseline
- top_of_line_box += 0.5 * line_height.emSize();
- } else if (_block_progression == LEFT_TO_RIGHT ) {
- // Vertical text, use em box center as baseline
- top_of_line_box -= 0.5 * line_height.emSize();
- } else {
- top_of_line_box -= line_height.getTypoAscent();
- }
- TRACE((" y attribute set, next line top_of_line_box: %f\n", top_of_line_box ));
- // Set the initial y coordinate of the next line (see above).
- _scanline_maker->setNewYCoordinate(top_of_line_box);
+ // we've finished fiddling about with ascents and descents: create the output
+ 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();
+
+ // The y coordinate is at the beginning edge of the line box (top for horizontal text, left
+ // edge for vertical lr text, right edge for vertical rl text. We align, by default to the
+ // alphabetic baseline for horizontal text and the central baseline for vertical text.
+ if( _block_progression == RIGHT_TO_LEFT ) {
+ // Vertical text, use em box center as baseline
+ new_line.baseline_y -= 0.5 * line_height.emSize();
+ } else if ( _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.getTypoAscent();
+ }
+
+ new_line.in_shape = _current_shape_index;
+ _flow._lines.push_back(new_line);
+
+ for (std::vector<ChunkInfo>::const_iterator it_chunk = chunk_info.begin() ; it_chunk != chunk_info.end() ; it_chunk++) {
+
+ double add_to_each_whitespace;
+ // 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)
+
+ // 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 (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 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;
+
+ double top_of_line_box = new_line.baseline_y;
+ if( _block_progression == RIGHT_TO_LEFT ) {
+ // Vertical text, use em box center as baseline
+ top_of_line_box += 0.5 * line_height.emSize();
+ } else if (_block_progression == LEFT_TO_RIGHT ) {
+ // Vertical text, use em box center as baseline
+ top_of_line_box -= 0.5 * line_height.emSize();
+ } else {
+ top_of_line_box -= line_height.getTypoAscent();
}
+ TRACE((" y attribute set, next line top_of_line_box: %f\n", top_of_line_box ));
+ // Set the initial y coordinate of the next line (see above).
+ _scanline_maker->setNewYCoordinate(top_of_line_box);
+ }
- // Reset relative y_offset ("dy" attribute is relative but should be reset at
- // the beginning of each line since each line will have a new "y" written out.)
- _y_offset = 0.0;
+ // Reset relative y_offset ("dy" attribute is relative but should be reset at
+ // the beginning of each line since each line will have a new "y" written out.)
+ _y_offset = 0.0;
- } else {
+ } 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;
- }
+ // 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);
-
- 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;
- current_x = 0.0;
- } else {
- direction_sign = -1.0;
- if (para.alignment == FULL && !_flow._input_wrap_shapes.empty()){
- current_x = it_chunk->scanrun_width;
- }
- else {
- current_x = it_chunk->text_width;
- }
+ }
+ _flow._chunks.push_back(new_chunk);
+
+ 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;
+ current_x = 0.0;
+ } else {
+ direction_sign = -1.0;
+ if (para.alignment == FULL && !_flow._input_wrap_shapes.empty()){
+ current_x = it_chunk->scanrun_width;
}
+ else {
+ current_x = it_chunk->text_width;
+ }
+ }
- for (std::vector<BrokenSpan>::const_iterator it_span = it_chunk->broken_spans.begin() ; it_span != it_chunk->broken_spans.end() ; it_span++) {
- // begin adding spans to the list
- UnbrokenSpan const &unbroken_span = *it_span->start.iter_span;
- double x_in_span_last = 0.0; // set at the END when a new cluster starts
- double x_in_span = 0.0; // set from the preceding at the START when a new cluster starts.
-
- 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) 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);
- }
-
- if (_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE
- && unbroken_span.pango_item_index == -1) {
- // style only, nothing to output
- continue;
- }
+ for (std::vector<BrokenSpan>::const_iterator it_span = it_chunk->broken_spans.begin() ; it_span != it_chunk->broken_spans.end() ; it_span++) {
+ // begin adding spans to the list
+ UnbrokenSpan const &unbroken_span = *it_span->start.iter_span;
+ double x_in_span_last = 0.0; // set at the END when a new cluster starts
+ double x_in_span = 0.0; // set from the preceding at the START when a new cluster starts.
+
+ 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) 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);
+ }
- Layout::Span new_span;
+ if (_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE
+ && unbroken_span.pango_item_index == -1) {
+ // style only, nothing to output
+ continue;
+ }
- 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 = 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();
- 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);
- } else { // a control code
- new_span.font = NULL;
- new_span.font_size = new_span.line_height.emSize();
- new_span.direction = para.direction;
- }
+ Layout::Span new_span;
+
+ 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 = 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();
+ 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);
+ } else { // a control code
+ new_span.font = NULL;
+ new_span.font_size = new_span.line_height.emSize();
+ new_span.direction = para.direction;
+ }
- if (new_span.direction == para.direction) {
- 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
- counter_directional_width_remaining = 0.0;
- std::vector<BrokenSpan>::const_iterator it_following_span;
- for (it_following_span = it_span ; it_following_span != it_chunk->broken_spans.end() ; it_following_span++) {
- Layout::Direction following_span_progression = static_cast<InputStreamTextSource const *>(_flow._input_stream[it_following_span->start.iter_span->input_index])->styleGetBlockProgression();
- if (!Layout::_directions_are_orthogonal(following_span_progression, _block_progression)) {
- if (it_following_span->start.iter_span->pango_item_index == -1) { // when the span came from a control code
- if (new_span.direction != para.direction) break;
- } else
- if (new_span.direction != (para.pango_items[it_following_span->start.iter_span->pango_item_index].item->analysis.level & 1 ? RIGHT_TO_LEFT : LEFT_TO_RIGHT)) break;
- }
- counter_directional_width_remaining += direction_sign * (it_following_span->width + it_following_span->whitespace_count * add_to_each_whitespace);
+ if (new_span.direction == para.direction) {
+ 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
+ counter_directional_width_remaining = 0.0;
+ std::vector<BrokenSpan>::const_iterator it_following_span;
+ for (it_following_span = it_span ; it_following_span != it_chunk->broken_spans.end() ; it_following_span++) {
+ Layout::Direction following_span_progression = static_cast<InputStreamTextSource const *>(_flow._input_stream[it_following_span->start.iter_span->input_index])->styleGetBlockProgression();
+ if (!Layout::_directions_are_orthogonal(following_span_progression, _block_progression)) {
+ if (it_following_span->start.iter_span->pango_item_index == -1) { // when the span came from a control code
+ if (new_span.direction != para.direction) break;
+ } else
+ if (new_span.direction != (para.pango_items[it_following_span->start.iter_span->pango_item_index].item->analysis.level & 1 ? RIGHT_TO_LEFT : LEFT_TO_RIGHT)) break;
}
- current_x += counter_directional_width_remaining;
- counter_directional_width_remaining = 0.0; // we want to go increasingly negative
+ counter_directional_width_remaining += direction_sign * (it_following_span->width + it_following_span->whitespace_count * add_to_each_whitespace);
}
- 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
- InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream[unbroken_span.input_index]);
- Glib::ustring::const_iterator iter_source_text = Glib::ustring::const_iterator(unbroken_span.input_stream_first_character.base() + it_span->start.char_byte) ;
- unsigned char_index_in_unbroken_span = it_span->start.char_index;
- double font_size_multiplier = new_span.font_size / (PANGO_SCALE * _font_factory_size_multiplier);
- int log_cluster_size_glyphs = 0; // Number of glyphs in this log_cluster
- int log_cluster_size_chars = 0; // Number of characters in this log_cluster
- unsigned end_byte = 0;
-
- for (unsigned glyph_index = it_span->start_glyph_index ; glyph_index < it_span->end_glyph_index ; glyph_index++) {
- unsigned char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base();
- int newcluster = 0;
- if (unbroken_span.glyph_string->glyphs[glyph_index].attr.is_cluster_start){
- newcluster = 1;
- x_in_span = x_in_span_last;
- }
-
- if (unbroken_span.glyph_string->log_clusters[glyph_index] < (int)unbroken_span.text_bytes
- && *iter_source_text == UNICODE_SOFT_HYPHEN
- && glyph_index + 1 != it_span->end_glyph_index) {
- // if we're looking at a soft hyphen and it's not the last glyph in the
- // chunk we don't draw the glyph but we still need to add to _characters
- Layout::Character new_character;
- new_character.in_span = _flow._spans.size(); // the span hasn't been added yet, so no -1
- new_character.char_attributes = para.char_attributes[unbroken_span.char_index_in_para + char_index_in_unbroken_span];
- new_character.in_glyph = -1;
- _flow._characters.push_back(new_character);
- iter_source_text++;
- char_index_in_unbroken_span++;
- while (glyph_index < (unsigned)unbroken_span.glyph_string->num_glyphs
- && unbroken_span.glyph_string->log_clusters[glyph_index] == (int)char_byte)
- glyph_index++;
- glyph_index--;
- continue;
- }
+ current_x += counter_directional_width_remaining;
+ counter_directional_width_remaining = 0.0; // we want to go increasingly negative
+ }
+ 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
+ InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream[unbroken_span.input_index]);
+ Glib::ustring::const_iterator iter_source_text = Glib::ustring::const_iterator(unbroken_span.input_stream_first_character.base() + it_span->start.char_byte) ;
+ unsigned char_index_in_unbroken_span = it_span->start.char_index;
+ double font_size_multiplier = new_span.font_size / (PANGO_SCALE * _font_factory_size_multiplier);
+ int log_cluster_size_glyphs = 0; // Number of glyphs in this log_cluster
+ int log_cluster_size_chars = 0; // Number of characters in this log_cluster
+ unsigned end_byte = 0;
+
+ for (unsigned glyph_index = it_span->start_glyph_index ; glyph_index < it_span->end_glyph_index ; glyph_index++) {
+ unsigned char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base();
+ int newcluster = 0;
+ if (unbroken_span.glyph_string->glyphs[glyph_index].attr.is_cluster_start){
+ newcluster = 1;
+ x_in_span = x_in_span_last;
+ }
- // 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_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,
- // not vertical, so we unscale it back vertically during output
- if (_flow.lengthAdjust == Inkscape::Text::Layout::LENGTHADJUST_SPACINGANDGLYPHS)
- new_glyph.vertical_scale = 1.0 / _flow.getTextLengthMultiplierDue();
- else
- new_glyph.vertical_scale = 1.0;
-
- // Position glyph --------------------
- new_glyph.x = current_x + unbroken_span_glyph_info->geometry.x_offset * font_size_multiplier;
- new_glyph.y =_y_offset;
-
- // y-coordinate is flipped between vertical and horizontal text... delta_y is common offset but applied with opposite sign
- double delta_y = unbroken_span_glyph_info->geometry.y_offset * font_size_multiplier + unbroken_span.baseline_shift;
- SPCSSBaseline dominant_baseline = _flow._blockBaseline();
-
- if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) {
- // Vertical text
-
- // Default dominant baseline is determined by overall block (i.e. <text>) 'text-orientation' value.
- if( _flow._blockTextOrientation() != SP_CSS_TEXT_ORIENTATION_SIDEWAYS ) {
- if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_CENTRAL;
- } else {
- if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_ALPHABETIC;
- }
+ if (unbroken_span.glyph_string->log_clusters[glyph_index] < (int)unbroken_span.text_bytes
+ && *iter_source_text == UNICODE_SOFT_HYPHEN
+ && glyph_index + 1 != it_span->end_glyph_index) {
+ // if we're looking at a soft hyphen and it's not the last glyph in the
+ // chunk we don't draw the glyph but we still need to add to _characters
+ Layout::Character new_character;
+ new_character.in_span = _flow._spans.size(); // the span hasn't been added yet, so no -1
+ new_character.char_attributes = para.char_attributes[unbroken_span.char_index_in_para + char_index_in_unbroken_span];
+ new_character.in_glyph = -1;
+ _flow._characters.push_back(new_character);
+ iter_source_text++;
+ char_index_in_unbroken_span++;
+ while (glyph_index < (unsigned)unbroken_span.glyph_string->num_glyphs
+ && unbroken_span.glyph_string->log_clusters[glyph_index] == (int)char_byte)
+ glyph_index++;
+ glyph_index--;
+ continue;
+ }
- new_glyph.y += delta_y;
+ // 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_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,
+ // not vertical, so we unscale it back vertically during output
+ if (_flow.lengthAdjust == Inkscape::Text::Layout::LENGTHADJUST_SPACINGANDGLYPHS)
+ new_glyph.vertical_scale = 1.0 / _flow.getTextLengthMultiplierDue();
+ else
+ new_glyph.vertical_scale = 1.0;
+
+ // Position glyph --------------------
+ new_glyph.x = current_x + unbroken_span_glyph_info->geometry.x_offset * font_size_multiplier;
+ new_glyph.y =_y_offset;
+
+ // y-coordinate is flipped between vertical and horizontal text... delta_y is common offset but applied with opposite sign
+ double delta_y = unbroken_span_glyph_info->geometry.y_offset * font_size_multiplier + unbroken_span.baseline_shift;
+ SPCSSBaseline dominant_baseline = _flow._blockBaseline();
+
+ if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) {
+ // Vertical text
+
+ // Default dominant baseline is determined by overall block (i.e. <text>) 'text-orientation' value.
+ if( _flow._blockTextOrientation() != SP_CSS_TEXT_ORIENTATION_SIDEWAYS ) {
+ if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_CENTRAL;
+ } else {
+ if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_ALPHABETIC;
+ }
- // 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) ) {
+ new_glyph.y += delta_y;
- // Sideways orientation (Latin characters, CJK punctuation), 90deg rotation done at output stage. zzzzzzz
- new_glyph.orientation = ORIENTATION_SIDEWAYS;
+ // 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) ) {
- new_glyph.y -= new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ];
- new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, false);
+ // Sideways orientation (Latin characters, CJK punctuation), 90deg rotation done at output stage. zzzzzzz
+ new_glyph.orientation = ORIENTATION_SIDEWAYS;
- } else {
- // Upright orientation
+ new_glyph.y -= new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ];
+ new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, false);
- new_glyph.x += new_span.line_height.ascent;
+ } else {
+ // Upright orientation
- // Glyph reference point is center (shift: left edge to center glyph)
- new_glyph.y -= unbroken_span_glyph_info->geometry.width * 0.5 * font_size_multiplier;
- new_glyph.y -= new_span.font_size * (para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ] -
- para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ SP_CSS_BASELINE_CENTRAL ] );
+ new_glyph.x += 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, true);
- if( new_glyph.width == 0 ) {
- new_glyph.width = unbroken_span_glyph_info->geometry.width * font_size_multiplier;
- }
+ // Glyph reference point is center (shift: left edge to center glyph)
+ new_glyph.y -= unbroken_span_glyph_info->geometry.width * 0.5 * font_size_multiplier;
+ new_glyph.y -= new_span.font_size * (para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ] -
+ para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ SP_CSS_BASELINE_CENTRAL ] );
+ 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 {
- // Horizontal text
-
- if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_ALPHABETIC;
- new_glyph.y -= delta_y;
- new_glyph.y += new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ];
-
- 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_info->glyph, false);
- // for some reason pango returns zero width for invalid glyph characters (those empty boxes), so go to freetype for the info
}
+ } else {
+ // Horizontal text
+ if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_ALPHABETIC;
- if (new_span.direction == RIGHT_TO_LEFT) {
- // pango wanted to give us glyphs in visual order but we refused, so we need to work
- // out where the cluster start is ourselves
- double cluster_width = 0.0;
- for (unsigned rtl_index = glyph_index; rtl_index < it_span->end_glyph_index ; rtl_index++) {
- 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;
+ new_glyph.y -= delta_y;
+ new_glyph.y += new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ];
+
+ 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_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) {
+ // pango wanted to give us glyphs in visual order but we refused, so we need to work
+ // out where the cluster start is ourselves
+ double cluster_width = 0.0;
+ for (unsigned rtl_index = glyph_index; rtl_index < it_span->end_glyph_index ; rtl_index++) {
+ 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;
}
- _flow._glyphs.push_back(new_glyph);
-
- // create the Layout::Character(s)
- double advance_width = new_glyph.width;
- if (newcluster){
- newcluster = 0;
- // find where the text ends for this log_cluster
- end_byte = it_span->start.iter_span->text_bytes; // Upper limit
- for(int next_glyph_index = glyph_index+1; next_glyph_index < unbroken_span.glyph_string->num_glyphs; next_glyph_index++){
- if(unbroken_span.glyph_string->glyphs[next_glyph_index].attr.is_cluster_start){
- end_byte = unbroken_span.glyph_string->log_clusters[next_glyph_index];
- break;
- }
- }
- // Figure out how many glyphs and characters are in the log_cluster.
- log_cluster_size_glyphs = 0;
- log_cluster_size_chars = 0;
- for(; log_cluster_size_glyphs + glyph_index < it_span->end_glyph_index; log_cluster_size_glyphs++){
- if(unbroken_span.glyph_string->log_clusters[glyph_index ] !=
- unbroken_span.glyph_string->log_clusters[glyph_index + log_cluster_size_glyphs])break;
- }
- Glib::ustring::const_iterator lclist = iter_source_text;
- unsigned lcb = char_byte;
- while(lcb < end_byte){
- log_cluster_size_chars++;
- lclist++;
- lcb = lclist.base() - unbroken_span.input_stream_first_character.base();
+ new_glyph.x -= cluster_width;
+ }
+ _flow._glyphs.push_back(new_glyph);
+
+ // create the Layout::Character(s)
+ double advance_width = new_glyph.width;
+ if (newcluster){
+ newcluster = 0;
+ // find where the text ends for this log_cluster
+ end_byte = it_span->start.iter_span->text_bytes; // Upper limit
+ for(int next_glyph_index = glyph_index+1; next_glyph_index < unbroken_span.glyph_string->num_glyphs; next_glyph_index++){
+ if(unbroken_span.glyph_string->glyphs[next_glyph_index].attr.is_cluster_start){
+ end_byte = unbroken_span.glyph_string->log_clusters[next_glyph_index];
+ break;
}
}
- while (char_byte < end_byte) {
- /* Hack to survive ligatures: in log_cluster keep the number of available chars >= number of glyphs remaining.
- When there are no ligatures these two sizes are always the same.
- */
- if(log_cluster_size_chars < log_cluster_size_glyphs){
- log_cluster_size_glyphs--;
- break;
- }
- Layout::Character new_character;
- new_character.in_span = _flow._spans.size();
- new_character.x = x_in_span;
- new_character.char_attributes = para.char_attributes[unbroken_span.char_index_in_para + char_index_in_unbroken_span];
- new_character.in_glyph = _flow._glyphs.size() - 1;
- _flow._characters.push_back(new_character);
- if (new_character.char_attributes.is_white)
- advance_width += text_source->style->word_spacing.computed * _flow.getTextLengthMultiplierDue() + add_to_each_whitespace; // justification
- if (new_character.char_attributes.is_cursor_position)
- advance_width += text_source->style->letter_spacing.computed * _flow.getTextLengthMultiplierDue();
- advance_width += _flow.getTextLengthIncrementDue();
- iter_source_text++;
- char_index_in_unbroken_span++;
- char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base();
- log_cluster_size_chars--;
+ // Figure out how many glyphs and characters are in the log_cluster.
+ log_cluster_size_glyphs = 0;
+ log_cluster_size_chars = 0;
+ for(; log_cluster_size_glyphs + glyph_index < it_span->end_glyph_index; log_cluster_size_glyphs++){
+ if(unbroken_span.glyph_string->log_clusters[glyph_index ] !=
+ unbroken_span.glyph_string->log_clusters[glyph_index + log_cluster_size_glyphs])break;
}
-
- advance_width *= direction_sign;
- if (new_span.direction != para.direction) {
- counter_directional_width_remaining -= advance_width;
- current_x -= advance_width;
- x_in_span_last -= advance_width;
- } else {
- current_x += advance_width;
- x_in_span_last += advance_width;
+ Glib::ustring::const_iterator lclist = iter_source_text;
+ unsigned lcb = char_byte;
+ while(lcb < end_byte){
+ log_cluster_size_chars++;
+ lclist++;
+ lcb = lclist.base() - unbroken_span.input_stream_first_character.base();
}
}
- } else if (_flow._input_stream[unbroken_span.input_index]->Type() == CONTROL_CODE) {
- current_x += static_cast<InputStreamControlCode const *>(_flow._input_stream[unbroken_span.input_index])->width;
- }
+ while (char_byte < end_byte) {
+ /* Hack to survive ligatures: in log_cluster keep the number of available chars >= number of glyphs remaining.
+ When there are no ligatures these two sizes are always the same.
+ */
+ if(log_cluster_size_chars < log_cluster_size_glyphs){
+ log_cluster_size_glyphs--;
+ break;
+ }
+ Layout::Character new_character;
+ new_character.in_span = _flow._spans.size();
+ new_character.x = x_in_span;
+ new_character.char_attributes = para.char_attributes[unbroken_span.char_index_in_para + char_index_in_unbroken_span];
+ new_character.in_glyph = _flow._glyphs.size() - 1;
+ _flow._characters.push_back(new_character);
+ if (new_character.char_attributes.is_white)
+ advance_width += text_source->style->word_spacing.computed * _flow.getTextLengthMultiplierDue() + add_to_each_whitespace; // justification
+ if (new_character.char_attributes.is_cursor_position)
+ advance_width += text_source->style->letter_spacing.computed * _flow.getTextLengthMultiplierDue();
+ advance_width += _flow.getTextLengthIncrementDue();
+ iter_source_text++;
+ char_index_in_unbroken_span++;
+ char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base();
+ log_cluster_size_chars--;
+ }
- new_span.x_end = new_span.x_start + x_in_span_last;
- _flow._spans.push_back(new_span);
- previous_direction = new_span.direction;
+ advance_width *= direction_sign;
+ if (new_span.direction != para.direction) {
+ counter_directional_width_remaining -= advance_width;
+ current_x -= advance_width;
+ x_in_span_last -= advance_width;
+ } else {
+ current_x += advance_width;
+ x_in_span_last += advance_width;
+ }
+ }
+ } else if (_flow._input_stream[unbroken_span.input_index]->Type() == CONTROL_CODE) {
+ current_x += static_cast<InputStreamControlCode const *>(_flow._input_stream[unbroken_span.input_index])->width;
}
- // end adding spans to the list, on to the next chunk...
- }
- TRACE((" End _outputLine\n"));
- }
-/* *********************************************************************************************************/
-// Setup and top-level functions
-
- /** initialises the ScanlineMaker for the first shape in the flow, or
- the infinite version if we're not doing wrapping. */
- void _createFirstScanlineMaker()
- {
- _current_shape_index = 0;
- if (_flow._input_wrap_shapes.empty()) {
- // create the special no-wrapping infinite scanline maker
- double initial_x = 0, initial_y = 0;
- InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream.front());
- if (!text_source->x.empty())
- initial_x = text_source->x.front().computed;
- if (!text_source->y.empty())
- initial_y = text_source->y.front().computed;
- _scanline_maker = new InfiniteScanlineMaker(initial_x, initial_y, _block_progression);
- TRACE((" wrapping disabled\n"));
+ new_span.x_end = new_span.x_start + x_in_span_last;
+ _flow._spans.push_back(new_span);
+ previous_direction = new_span.direction;
}
- else {
- _scanline_maker = new ShapeScanlineMaker(_flow._input_wrap_shapes[_current_shape_index].shape, _block_progression);
- TRACE((" begin wrap shape 0\n"));
- }
- }
-
-public:
- Calculator(Layout *text_flow)
- : _flow(*text_flow) {}
-
- bool calculate();
-};
-
-/* fixme: I don't like the fact that InputItemInfo etc. use the default copy constructor and
- * operator= (and thus don't involve incrementing reference counts), yet they provide a free method
- * that does delete or Unref.
- *
- * I suggest using the garbage collector to manage deletion.
- */
-void Layout::Calculator::InputItemInfo::free()
-{
- if (sub_flow) {
- delete sub_flow;
- sub_flow = NULL;
+ // end adding spans to the list, on to the next chunk...
}
+ TRACE((" End _outputLine\n"));
}
-void Layout::Calculator::PangoItemInfo::free()
+/**
+ * Initialises the ScanlineMaker for the first shape in the flow,
+ * or the infinite version if we're not doing wrapping.
+ */
+void Layout::Calculator::_createFirstScanlineMaker()
{
- if (item) {
- pango_item_free(item);
- item = NULL;
+ _current_shape_index = 0;
+ if (_flow._input_wrap_shapes.empty()) {
+ // create the special no-wrapping infinite scanline maker
+ double initial_x = 0, initial_y = 0;
+ InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream.front());
+ if (!text_source->x.empty())
+ initial_x = text_source->x.front().computed;
+ if (!text_source->y.empty())
+ initial_y = text_source->y.front().computed;
+ _scanline_maker = new InfiniteScanlineMaker(initial_x, initial_y, _block_progression);
+ TRACE((" wrapping disabled\n"));
}
- if (font) {
- font->Unref();
- font = NULL;
+ else {
+ _scanline_maker = new ShapeScanlineMaker(_flow._input_wrap_shapes[_current_shape_index].shape, _block_progression);
+ TRACE((" begin wrap shape 0\n"));
}
}
@@ -949,21 +951,6 @@ void Layout::Calculator::BrokenSpan::setZero()
word_spacing = 0.0;
}
-template<typename T> void Layout::Calculator::ParagraphInfo::free_sequence(T &seq)
-{
- for (typename T::iterator it(seq.begin()); it != seq.end(); ++it) {
- it->free();
- }
- seq.clear();
-}
-
-void Layout::Calculator::ParagraphInfo::free()
-{
- free_sequence(input_items);
- free_sequence(pango_items);
- free_sequence(unbroken_spans);
-}
-
///**
// * For sections of text with a block-progression different to the rest
// * of the flow, the best thing to do is to detect them in advance and
@@ -1692,6 +1679,46 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const &para,
return true;
}
+#ifdef DEBUG_LAYOUT_TNG_COMPUTE
+/**
+ * For debugging, not called in distributed code
+ *
+ * Input: para->first_input_index, para->pango_items
+ */
+static void Layout::Calculator::dumpPangoItemsOut(ParagraphInfo *para){
+ std::cout << "Pango items: " << para->pango_items.size() << std::endl;
+ font_factory * factory = font_factory::Default();
+ for(unsigned pidx = 0 ; pidx < para->pango_items.size(); pidx++){
+ std::cout
+ << "idx: " << pidx
+ << " offset: "
+ << para->pango_items[pidx].item->offset
+ << " length: "
+ << para->pango_items[pidx].item->length
+ << " font: "
+ << factory->ConstructFontSpecification( para->pango_items[pidx].font )
+ << std::endl;
+ }
+}
+
+/**
+ * For debugging, not called in distributed code
+ *
+ * Input: para->first_input_index, para->pango_items
+ */
+static void Layout::Calculator::dumpUnbrokenSpans(ParagraphInfo *para){
+ std::cout << "Unbroken Spans: " << para->unbroken_spans.size() << std::endl;
+ for(unsigned uidx = 0 ; uidx < para->unbroken_spans.size(); uidx++){
+ std::cout
+ << "idx: " << uidx
+ << " pango_item_index: " << para->unbroken_spans[uidx].pango_item_index
+ << " input_index: " << para->unbroken_spans[uidx].input_index
+ << " char_index_in_para: " << para->unbroken_spans[uidx].char_index_in_para
+ << " text_bytes: " << para->unbroken_spans[uidx].text_bytes
+ << std::endl;
+ }
+}
+#endif //DEBUG_LAYOUT_TNG_COMPUTE
/** The management function to start the whole thing off. */
bool Layout::Calculator::calculate()
diff --git a/src/libnrtype/Layout-TNG-Input.cpp b/src/libnrtype/Layout-TNG-Input.cpp
index 6f5d4e5f8..98ce7d526 100644
--- a/src/libnrtype/Layout-TNG-Input.cpp
+++ b/src/libnrtype/Layout-TNG-Input.cpp
@@ -40,7 +40,13 @@ void Layout::_clearInputObjects()
}
// this function does nothing more than store all its parameters for future reference
-void Layout::appendText(Glib::ustring const &text, SPStyle *style, void *source_cookie, OptionalTextTagAttrs const *optional_attributes, unsigned optional_attributes_offset, Glib::ustring::const_iterator text_begin, Glib::ustring::const_iterator text_end)
+void Layout::appendText(Glib::ustring const &text,
+ SPStyle *style,
+ void *source_cookie,
+ OptionalTextTagAttrs const *optional_attributes,
+ unsigned optional_attributes_offset,
+ Glib::ustring::const_iterator text_begin,
+ Glib::ustring::const_iterator text_end)
{
if (style == NULL) return;
@@ -120,40 +126,40 @@ void Layout::appendWrapShape(Shape const *shape, DisplayAlign display_align)
int Layout::_enum_converter(int input, EnumConversionItem const *conversion_table, unsigned conversion_table_size)
{
- for (unsigned i = 0 ; i < conversion_table_size ; i++)
+ for (unsigned i = 0 ; i < conversion_table_size ; i++) {
if (conversion_table[i].input == input)
return conversion_table[i].output;
+ }
return conversion_table[0].output;
}
Layout::Direction Layout::InputStreamTextSource::styleGetBlockProgression() const
{
- switch( style->writing_mode.computed ) {
+ switch( style->writing_mode.computed ) {
+ case SP_CSS_WRITING_MODE_LR_TB:
+ case SP_CSS_WRITING_MODE_RL_TB:
+ return TOP_TO_BOTTOM;
- case SP_CSS_WRITING_MODE_LR_TB:
- case SP_CSS_WRITING_MODE_RL_TB:
- return TOP_TO_BOTTOM;
-
- case SP_CSS_WRITING_MODE_TB_RL:
- return RIGHT_TO_LEFT;
+ case SP_CSS_WRITING_MODE_TB_RL:
+ return RIGHT_TO_LEFT;
- case SP_CSS_WRITING_MODE_TB_LR:
- return LEFT_TO_RIGHT;
+ case SP_CSS_WRITING_MODE_TB_LR:
+ return LEFT_TO_RIGHT;
- default:
- std::cerr << "Layout::InputTextStream::styleGetBlockProgression: invalid writing mode." << std::endl;
- }
- return TOP_TO_BOTTOM;
+ default:
+ std::cerr << "Layout::InputTextStream::styleGetBlockProgression: invalid writing mode." << std::endl;
+ }
+ return TOP_TO_BOTTOM;
}
SPCSSTextOrientation Layout::InputStreamTextSource::styleGetTextOrientation() const
{
- return ((SPCSSTextOrientation)style->text_orientation.computed);
+ return ((SPCSSTextOrientation)style->text_orientation.computed);
}
SPCSSBaseline Layout::InputStreamTextSource::styleGetDominantBaseline() const
{
- return ((SPCSSBaseline)style->dominant_baseline.computed);
+ return ((SPCSSBaseline)style->dominant_baseline.computed);
}
static Layout::Alignment text_anchor_to_alignment(unsigned anchor, Layout::Direction para_direction)
@@ -202,22 +208,24 @@ Layout::Alignment Layout::InputStreamTextSource::styleGetAlignment(Layout::Direc
static const Layout::EnumConversionItem enum_convert_spstyle_style_to_pango_style[] = {
{SP_CSS_FONT_STYLE_NORMAL, PANGO_STYLE_NORMAL},
{SP_CSS_FONT_STYLE_ITALIC, PANGO_STYLE_ITALIC},
- {SP_CSS_FONT_STYLE_OBLIQUE, PANGO_STYLE_OBLIQUE}};
+ {SP_CSS_FONT_STYLE_OBLIQUE, PANGO_STYLE_OBLIQUE}
+};
static const Layout::EnumConversionItem enum_convert_spstyle_weight_to_pango_weight[] = {
// NB: The Pango web page calls 500 "the normal font" but both CSS2 and the Pango
// enumeration define 400 as normal.
{SP_CSS_FONT_WEIGHT_NORMAL, PANGO_WEIGHT_NORMAL},
- {SP_CSS_FONT_WEIGHT_BOLD,PANGO_WEIGHT_BOLD},
- {SP_CSS_FONT_WEIGHT_100, PANGO_WEIGHT_THIN},
- {SP_CSS_FONT_WEIGHT_200, PANGO_WEIGHT_ULTRALIGHT},
- {SP_CSS_FONT_WEIGHT_300, PANGO_WEIGHT_LIGHT},
- {SP_CSS_FONT_WEIGHT_400, PANGO_WEIGHT_NORMAL},
- {SP_CSS_FONT_WEIGHT_500, PANGO_WEIGHT_MEDIUM},
- {SP_CSS_FONT_WEIGHT_600, PANGO_WEIGHT_SEMIBOLD},
- {SP_CSS_FONT_WEIGHT_700, PANGO_WEIGHT_BOLD},
- {SP_CSS_FONT_WEIGHT_800, PANGO_WEIGHT_ULTRABOLD},
- {SP_CSS_FONT_WEIGHT_900, PANGO_WEIGHT_HEAVY}};
+ {SP_CSS_FONT_WEIGHT_BOLD, PANGO_WEIGHT_BOLD},
+ {SP_CSS_FONT_WEIGHT_100, PANGO_WEIGHT_THIN},
+ {SP_CSS_FONT_WEIGHT_200, PANGO_WEIGHT_ULTRALIGHT},
+ {SP_CSS_FONT_WEIGHT_300, PANGO_WEIGHT_LIGHT},
+ {SP_CSS_FONT_WEIGHT_400, PANGO_WEIGHT_NORMAL},
+ {SP_CSS_FONT_WEIGHT_500, PANGO_WEIGHT_MEDIUM},
+ {SP_CSS_FONT_WEIGHT_600, PANGO_WEIGHT_SEMIBOLD},
+ {SP_CSS_FONT_WEIGHT_700, PANGO_WEIGHT_BOLD},
+ {SP_CSS_FONT_WEIGHT_800, PANGO_WEIGHT_ULTRABOLD},
+ {SP_CSS_FONT_WEIGHT_900, PANGO_WEIGHT_HEAVY}
+};
static const Layout::EnumConversionItem enum_convert_spstyle_stretch_to_pango_stretch[] = {
{SP_CSS_FONT_STRETCH_NORMAL, PANGO_STRETCH_NORMAL},
@@ -228,11 +236,13 @@ static const Layout::EnumConversionItem enum_convert_spstyle_stretch_to_pango_st
{SP_CSS_FONT_STRETCH_SEMI_EXPANDED, PANGO_STRETCH_SEMI_EXPANDED},
{SP_CSS_FONT_STRETCH_EXPANDED, PANGO_STRETCH_EXPANDED},
{SP_CSS_FONT_STRETCH_EXTRA_EXPANDED, PANGO_STRETCH_EXTRA_EXPANDED},
- {SP_CSS_FONT_STRETCH_ULTRA_EXPANDED, PANGO_STRETCH_ULTRA_EXPANDED}};
+ {SP_CSS_FONT_STRETCH_ULTRA_EXPANDED, PANGO_STRETCH_ULTRA_EXPANDED}
+};
static const Layout::EnumConversionItem enum_convert_spstyle_variant_to_pango_variant[] = {
{SP_CSS_FONT_VARIANT_NORMAL, PANGO_VARIANT_NORMAL},
- {SP_CSS_FONT_VARIANT_SMALL_CAPS, PANGO_VARIANT_SMALL_CAPS}};
+ {SP_CSS_FONT_VARIANT_SMALL_CAPS, PANGO_VARIANT_SMALL_CAPS}
+};
font_instance *Layout::InputStreamTextSource::styleGetFontInstance() const
{
@@ -265,26 +275,57 @@ PangoFontDescription *Layout::InputStreamTextSource::styleGetFontDescription() c
}
pango_font_description_set_family(descr,family.c_str());
- pango_font_description_set_weight(descr,(PangoWeight)_enum_converter(style->font_weight.computed, enum_convert_spstyle_weight_to_pango_weight, sizeof(enum_convert_spstyle_weight_to_pango_weight)/sizeof(enum_convert_spstyle_weight_to_pango_weight[0])));
- pango_font_description_set_style(descr,(PangoStyle)_enum_converter(style->font_style.computed, enum_convert_spstyle_style_to_pango_style, sizeof(enum_convert_spstyle_style_to_pango_style)/sizeof(enum_convert_spstyle_style_to_pango_style[0])));
- pango_font_description_set_variant(descr,(PangoVariant)_enum_converter(style->font_variant.computed, enum_convert_spstyle_variant_to_pango_variant, sizeof(enum_convert_spstyle_variant_to_pango_variant)/sizeof(enum_convert_spstyle_variant_to_pango_variant[0])));
+ pango_font_description_set_weight(
+ descr,
+ (PangoWeight)_enum_converter(
+ style->font_weight.computed,
+ enum_convert_spstyle_weight_to_pango_weight,
+ sizeof(enum_convert_spstyle_weight_to_pango_weight) / sizeof(enum_convert_spstyle_weight_to_pango_weight[0])
+ )
+ );
+ pango_font_description_set_style(
+ descr,
+ (PangoStyle)_enum_converter(
+ style->font_style.computed,
+ enum_convert_spstyle_style_to_pango_style,
+ sizeof(enum_convert_spstyle_style_to_pango_style) / sizeof(enum_convert_spstyle_style_to_pango_style[0])
+ )
+ );
+ pango_font_description_set_variant(
+ descr,
+ (PangoVariant)_enum_converter(
+ style->font_variant.computed,
+ enum_convert_spstyle_variant_to_pango_variant,
+ sizeof(enum_convert_spstyle_variant_to_pango_variant) / sizeof(enum_convert_spstyle_variant_to_pango_variant[0])
+ )
+ );
#ifdef USE_PANGO_WIN32
// damn Pango fudges the size, so we need to unfudge. See source of pango_win32_font_map_init()
- pango_font_description_set_size(descr, (int) ((font_factory::Default())->fontSize*PANGO_SCALE*72/GetDeviceCaps(pango_win32_get_dc(),LOGPIXELSY))); // mandatory huge size (hinting workaround)
+ pango_font_description_set_size(
+ descr,
+ (int) ((font_factory::Default())->fontSize*PANGO_SCALE*72 / GetDeviceCaps(pango_win32_get_dc(),LOGPIXELSY)) // mandatory huge size (hinting workaround)
+ );
// we don't set stretch on Win32, because pango-win32 has no concept of it
// (Windows doesn't really provide any useful field it could use).
// If we did set stretch, then any text with a font-stretch attribute would
// end up falling back to Arial.
#else
pango_font_description_set_size(descr, (int) ((font_factory::Default())->fontSize*PANGO_SCALE)); // mandatory huge size (hinting workaround)
- pango_font_description_set_stretch(descr,(PangoStretch)_enum_converter(style->font_stretch.computed, enum_convert_spstyle_stretch_to_pango_stretch, sizeof(enum_convert_spstyle_stretch_to_pango_stretch)/sizeof(enum_convert_spstyle_stretch_to_pango_stretch[0])));
+ pango_font_description_set_stretch(
+ descr,
+ (PangoStretch)_enum_converter(
+ style->font_stretch.computed,
+ enum_convert_spstyle_stretch_to_pango_stretch,
+ sizeof(enum_convert_spstyle_stretch_to_pango_stretch) / sizeof(enum_convert_spstyle_stretch_to_pango_stretch[0])
+ )
+ );
#endif
return descr;
}
Layout::InputStreamTextSource::~InputStreamTextSource()
{
- sp_style_unref(style);
+ sp_style_unref(style);
}
}//namespace Text
diff --git a/src/libnrtype/Layout-TNG-Output.cpp b/src/libnrtype/Layout-TNG-Output.cpp
index a1a19f2b3..7f20dee95 100644
--- a/src/libnrtype/Layout-TNG-Output.cpp
+++ b/src/libnrtype/Layout-TNG-Output.cpp
@@ -515,6 +515,7 @@ void Layout::showGlyphs(CairoRenderContext *ctx) const
}
#endif
+#if DEBUG_TEXTLAYOUT_DUMPASTEXT
// these functions are for dumpAsText() only. No need to translate
static char const *direction_to_text(Layout::Direction d)
{
@@ -557,6 +558,7 @@ static char const *weight_to_text(PangoWeight w)
}
return "???";
}
+#endif //DEBUG_TEXTLAYOUT_DUMPASTEXT
Glib::ustring Layout::getFontFamily(unsigned span_index) const
{
@@ -570,6 +572,7 @@ Glib::ustring Layout::getFontFamily(unsigned span_index) const
return "";
}
+#if DEBUG_TEXTLAYOUT_DUMPASTEXT
Glib::ustring Layout::dumpAsText() const
{
Glib::ustring result;
@@ -609,7 +612,13 @@ Glib::ustring Layout::dumpAsText() const
_lines[_chunks[_spans[span_index].in_chunk].in_line].in_shape)
+ Glib::ustring::compose(" in chunk %1 (x=%2, baselineshift=%3)\n", _spans[span_index].in_chunk, _chunks[_spans[span_index].in_chunk].left_x, _spans[span_index].baseline_shift);
if (_spans[span_index].font) {
- 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(
+ " 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\n", _spans[span_index].line_height.ascent, _spans[span_index].line_height.descent)
@@ -640,6 +649,7 @@ Glib::ustring Layout::dumpAsText() const
result += "EOT\n";
return result;
}
+#endif //DEBUG_TEXTLAYOUT_DUMPASTEXT
void Layout::fitToPathAlign(SVGLength const &startOffset, Path const &path)
{
diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h
index c39315106..878c76af8 100644
--- a/src/libnrtype/Layout-TNG.h
+++ b/src/libnrtype/Layout-TNG.h
@@ -11,6 +11,8 @@
#ifndef __LAYOUT_TNG_H__
#define __LAYOUT_TNG_H__
+//#define DEBUG_TEXTLAYOUT_DUMPASTEXT
+
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
@@ -402,10 +404,12 @@ public:
/** Returns the font family of the indexed span */
Glib::ustring getFontFamily(unsigned span_index) const;
+#if DEBUG_TEXTLAYOUT_DUMPASTEXT
/** debug and unit test method. Creates a textual representation of the
contents of this object. The output is designed to be both human-readable
and comprehensible when diffed with a known-good dump. */
Glib::ustring dumpAsText() const;
+#endif
/** Moves all the glyphs in the structure so that the baseline of all
the characters sits neatly along the path specified. If the text has
diff --git a/src/live_effects/lpe-bool.h b/src/live_effects/lpe-bool.h
index 1c0aa0243..ce46f14d1 100644
--- a/src/live_effects/lpe-bool.h
+++ b/src/live_effects/lpe-bool.h
@@ -42,7 +42,7 @@ public:
inline friend bool_op to_bool_op(bool_op_ex val)
{
assert(val <= bool_op_ex_slice);
- (bool_op) val;
+ return (bool_op) val;
}
private:
diff --git a/src/sp-flowtext.cpp b/src/sp-flowtext.cpp
index d79652bf4..741ca7886 100644
--- a/src/sp-flowtext.cpp
+++ b/src/sp-flowtext.cpp
@@ -463,7 +463,9 @@ void SPFlowtext::rebuildLayout()
_buildLayoutInput(this, exclusion_shape, &shapes, &pending_line_break_object);
delete exclusion_shape;
layout.calculateFlow();
- //g_print("%s", layout.dumpAsText().c_str());
+#if DEBUG_TEXTLAYOUT_DUMPASTEXT
+ g_print("%s", layout.dumpAsText().c_str());
+#endif
}
void SPFlowtext::_clearFlow(Inkscape::DrawingGroup *in_arena)
diff --git a/src/sp-text.cpp b/src/sp-text.cpp
index b066ca3de..2a88c5009 100644
--- a/src/sp-text.cpp
+++ b/src/sp-text.cpp
@@ -620,12 +620,16 @@ void SPText::rebuildLayout()
if (SP_IS_TEXTPATH(&child)) {
SPTextPath const *textpath = SP_TEXTPATH(&child);
if (textpath->originalPath != NULL) {
- //g_print("%s", layout.dumpAsText().c_str());
+#if DEBUG_TEXTLAYOUT_DUMPASTEXT
+ g_print("%s", layout.dumpAsText().c_str());
+#endif
layout.fitToPathAlign(textpath->startOffset, *textpath->originalPath);
}
}
}
- //g_print("%s", layout.dumpAsText().c_str());
+#if DEBUG_TEXTLAYOUT_DUMPASTEXT
+ g_print("%s", layout.dumpAsText().c_str());
+#endif
// set the x,y attributes on role:line spans
for (auto& child: children) {