summaryrefslogtreecommitdiffstats
path: root/src/libnrtype
diff options
context:
space:
mode:
authorDavid Mathog <>2013-06-19 17:20:33 +0000
committer~suv <suv-sf@users.sourceforge.net>2013-06-19 17:20:33 +0000
commitd19db89e2e22d4e09c539bd42823d511aebb669f (patch)
treef5322f76b16b0e4c97895ce2b805ea7375e44142 /src/libnrtype
parentmerge from trunk (r12337) (diff)
downloadinkscape-d19db89e2e22d4e09c539bd42823d511aebb669f.tar.gz
inkscape-d19db89e2e22d4e09c539bd42823d511aebb669f.zip
changes_2013_05_22a.patch:
1. Resolves issue of bug #988601 message 170 (Support of 'Unset' styles in EMF export). 2. Implements CSS 3 (and CSS 2) text-decoration support. Note that it does not yet provide any method of adding these features - at present it just shows whatever is in the SVG. This new code is also used to display EMF/WMF strike-through and underline text decorations when these files are read in. Those decorations may also be written out to EMF/WMF. Other text decoration features, like overline, or dotted lines, are dropped. For SVG text-decoration -line, -style, -color are all implemented. CSS3 provides two ways to represent the same state, this code uses the compound text-decoration method rather than the 3 fields method. Also it leaves out keywords that are not needed and would break backwards compatibility. For instance: text-decoration: underline solid is valid, but would break CSS2. Solid is the default, so that sort of case is written as: text-decoration: underline If the state is CSS3 specific all of the needed fields are of course include, like text-decoration: underline wavy red 3. It incorporates the fix for bug 1181326 (Text edit mishandles span of just colored spaces) 4. It incorporates further changes to text editing so that style can be changed on spans consisting of only spaces when text decorations are present in the span. 5. It incorporates code to disable text decorations when text so marked is mapped onto a path. 6. Fixed more bugs in Hebrew language support than I can remember. Hebrew language export/import to EMF now works quite well. (See the examples in libTERE v 0.7.) WMF does not support unicode and for all intents and purposes Inkscape has no way to read or write Hebrew to it. Some of more important things that now work that didn't (or didn't always): Hebrew diacritical marks, R/L/center justification, and bidirectional text. The Hebrew fonts "Ezra SIL" and "EZRA SIL SR" should be installed before viewing the libTERE examples, otherwise font substitutions will cause some text shifts. 7. Implemented font failover in Text Reassemble, which makes the process more robust. (Again, see the examples in libTERE. ) (bzr r11668.1.71)
Diffstat (limited to 'src/libnrtype')
-rw-r--r--src/libnrtype/FontInstance.cpp35
-rw-r--r--src/libnrtype/Layout-TNG-Compute.cpp234
-rw-r--r--src/libnrtype/Layout-TNG-Output.cpp324
-rw-r--r--src/libnrtype/Layout-TNG.h4
-rw-r--r--src/libnrtype/font-instance.h2
5 files changed, 440 insertions, 159 deletions
diff --git a/src/libnrtype/FontInstance.cpp b/src/libnrtype/FontInstance.cpp
index 61225ad0c..fd0cdd3d4 100644
--- a/src/libnrtype/FontInstance.cpp
+++ b/src/libnrtype/FontInstance.cpp
@@ -675,6 +675,41 @@ bool font_instance::FontMetrics(double &ascent,double &descent,double &leading)
return true;
}
+bool font_instance::FontDecoration(
+ double &underline_position, double &underline_thickness,
+ double &linethrough_position, double &linethrough_thickness
+){
+ if ( pFont == NULL ) {
+ return false;
+ }
+ InitTheFace();
+ if ( theFace == NULL ) {
+ return false;
+ }
+#ifdef USE_PANGO_WIN32
+ OUTLINETEXTMETRIC otm;
+ if ( !GetOutlineTextMetrics(daddy->hScreenDC,sizeof(otm),&otm) ) {
+ return false;
+ }
+ double scale=1.0/daddy->fontSize;
+ underline_position = fabs(otm.otmUnderscorePosition *scale);
+ underline_thickness = fabs(otm.otmUnderscoreSize *scale);
+ linethrough_position = fabs(otm.otmStrikeoutPosition *scale);
+ linethrough_thickness = fabs(otm.otmStrikeoutSize *scale);
+#else
+ if ( theFace->units_per_EM == 0 ) {
+ return false; // bitmap font
+ }
+ underline_position = fabs(((double)theFace->underline_position )/((double)theFace->units_per_EM));
+ underline_thickness = fabs(((double)theFace->underline_thickness)/((double)theFace->units_per_EM));
+ // there is no specific linethrough information, mock it up from other font fields
+ linethrough_position = fabs(((double)theFace->ascender / 3.0 )/((double)theFace->units_per_EM));
+ linethrough_thickness = fabs(((double)theFace->underline_thickness)/((double)theFace->units_per_EM));
+#endif
+ return true;
+}
+
+
bool font_instance::FontSlope(double &run, double &rise)
{
run = 0.0;
diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp
index 7e684e7e3..1b2704a7e 100644
--- a/src/libnrtype/Layout-TNG-Compute.cpp
+++ b/src/libnrtype/Layout-TNG-Compute.cpp
@@ -120,8 +120,8 @@ class Layout::Calculator
BrokenSpan. */
struct UnbrokenSpan {
PangoGlyphString *glyph_string;
- int pango_item_index; /// index into _para.pango_items, or -1 if this is style only
- unsigned input_index; /// index into Layout::_input_stream
+ int pango_item_index; /// index into _para.pango_items, or -1 if this is style only
+ unsigned input_index; /// index into Layout::_input_stream
Glib::ustring::const_iterator input_stream_first_character;
double font_size;
LineHeight line_height; /// This is not the CSS line-height attribute!
@@ -213,6 +213,43 @@ class Layout::Calculator
/* *********************************************************************************************************/
// 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;
+ 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
+ << 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();
@@ -246,8 +283,10 @@ class Layout::Calculator
{
span->setZero();
- if (span->start.iter_span->dx._set && span->start.char_byte == 0)
- span->width += span->start.iter_span->dx.computed;
+ 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 (span->start.iter_span->pango_item_index == -1) {
// if this is a style-only span there's no text in it
@@ -510,15 +549,19 @@ class Layout::Calculator
x = 0.0;
} else {
direction_sign = -1.0;
- if (para.alignment == FULL && !_flow._input_wrap_shapes.empty())
+ if (para.alignment == FULL && !_flow._input_wrap_shapes.empty()){
x = it_chunk->scanrun_width;
- else
+ }
+ else {
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
@@ -535,7 +578,6 @@ class Layout::Calculator
}
Layout::Span new_span;
- double x_in_span = 0.0;
new_span.in_chunk = _flow._chunks.size() - 1;
new_span.line_height = unbroken_span.line_height;
@@ -581,14 +623,19 @@ class Layout::Calculator
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;
- unsigned cluster_start_char_index = _flow._characters.size();
- double font_size_multiplier = new_span.font_size / (PANGO_SCALE * _font_factory_size_multiplier);
+ 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();
- if (unbroken_span.glyph_string->glyphs[glyph_index].attr.is_cluster_start)
- cluster_start_char_index = _flow._characters.size();
-
+ 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) {
@@ -611,7 +658,7 @@ class Layout::Calculator
// create the Layout::Glyph
Layout::Glyph new_glyph;
new_glyph.glyph = unbroken_span.glyph_string->glyphs[glyph_index].glyph;
- new_glyph.in_character = cluster_start_char_index;
+ new_glyph.in_character = _flow._characters.size();
new_glyph.rotation = glyph_rotate;
/* put something like this back in when we do glyph-rotation-horizontal/vertical
@@ -652,28 +699,42 @@ class Layout::Calculator
}
new_glyph.x -= cluster_width;
}
- _flow._glyphs.push_back(new_glyph);
+ _flow._glyphs.push_back(new_glyph);
// create the Layout::Character(s)
double advance_width = new_glyph.width;
- unsigned end_byte;
- if (glyph_index == (unsigned)unbroken_span.glyph_string->num_glyphs - 1)
- end_byte = it_span->start.iter_span->text_bytes;
- else {
- // output chars for the whole cluster that is commenced by this glyph
- if (unbroken_span.glyph_string->glyphs[glyph_index].attr.is_cluster_start) {
- int next_cluster_glyph_index = glyph_index + 1;
- while (next_cluster_glyph_index < unbroken_span.glyph_string->num_glyphs
- && !unbroken_span.glyph_string->glyphs[next_cluster_glyph_index].attr.is_cluster_start)
- next_cluster_glyph_index++;
- if (next_cluster_glyph_index < unbroken_span.glyph_string->num_glyphs)
- end_byte = unbroken_span.glyph_string->log_clusters[next_cluster_glyph_index];
- else
- end_byte = it_span->start.iter_span->text_bytes;
- } else
- end_byte = char_byte; // don't output any chars if we're not at the start of a cluster
+ if (newcluster){
+ // find where the text ends for this log_cluster
+ end_byte = it_span->start.iter_span->text_bytes; // Upper limit
+ for(unsigned next_glyph_index = glyph_index+1; next_glyph_index < it_span->end_glyph_index; 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();
+ }
}
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;
@@ -687,23 +748,27 @@ class Layout::Calculator
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--;
}
- advance_width *= direction_sign;
- if (new_span.direction != para.direction) {
- counter_directional_width_remaining -= advance_width;
- x -= advance_width;
- x_in_span -= advance_width;
- } else {
- x += advance_width;
- x_in_span += advance_width;
+ if (newcluster){
+ advance_width *= direction_sign;
+ if (new_span.direction != para.direction) {
+ counter_directional_width_remaining -= advance_width;
+ x -= advance_width;
+ x_in_span_last -= advance_width;
+ } else {
+ x += advance_width;
+ x_in_span_last += advance_width;
+ }
}
+ newcluster = 0;
}
} else if (_flow._input_stream[unbroken_span.input_index]->Type() == CONTROL_CODE) {
x += static_cast<InputStreamControlCode const *>(_flow._input_stream[unbroken_span.input_index])->width;
}
- new_span.x_end = new_span.x_start + x_in_span;
+ new_span.x_end = new_span.x_start + x_in_span_last;
_flow._spans.push_back(new_span);
previous_direction = new_span.direction;
}
@@ -879,8 +944,9 @@ void Layout::Calculator::ParagraphInfo::free()
*
* Input: para.first_input_index.
* Output: para.direction, para.pango_items, para.char_attributes.
+ * Returns: the number of spans created by pango_itemize
*/
-void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) const
+void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) const
{
Glib::ustring para_text;
PangoAttrList *attributes_list;
@@ -974,8 +1040,9 @@ void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font
line_height->setZero();
*line_height_multiplier = 1.0;
}
- else
+ else {
font->FontMetrics(line_height->ascent, line_height->descent, line_height->leading);
+ }
*line_height *= font_size;
// yet another borked SPStyle member that we're going to have to fix ourselves
@@ -1008,6 +1075,11 @@ void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font
*line_height_multiplier = LINE_HEIGHT_NORMAL * font_size / line_height->total();
}
+bool compareGlyphWidth(const PangoGlyphInfo &a, const PangoGlyphInfo &b)
+{
+ return (a.geometry.width > b.geometry.width);
+}
+
/**
* Split the paragraph into spans. Also call pango_shape() on them.
@@ -1034,13 +1106,13 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const
break; // stop at the end of the paragraph
else if (control_code->code == ARBITRARY_GAP) {
UnbrokenSpan new_span;
- new_span.pango_item_index = -1;
- new_span.input_index = input_index;
- new_span.line_height.ascent = control_code->ascent;
+ new_span.pango_item_index = -1;
+ new_span.input_index = input_index;
+ new_span.line_height.ascent = control_code->ascent;
new_span.line_height.descent = control_code->descent;
new_span.line_height.leading = 0.0;
- new_span.text_bytes = 0;
- new_span.char_index_in_para = char_index_in_para;
+ new_span.text_bytes = 0;
+ new_span.char_index_in_para = char_index_in_para;
para->unbroken_spans.push_back(new_span);
TRACE(("add gap span %d\n", para->unbroken_spans.size() - 1));
}
@@ -1123,6 +1195,13 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const
g_assert( span_start_byte_in_source + new_span.text_bytes <= text_source->text->bytes() );
g_assert( memchr(text_source->text->data() + span_start_byte_in_source, '\0', static_cast<size_t>(new_span.text_bytes))
== NULL );
+ /* Notes as of 4/29/13. Pango_shape is not generating English language ligatures, but it is generating
+ them for Hebrew (and probably other similar languages). In the case observed 3 unicode characters (a base
+ and 2 Mark, nonspacings) are merged into two glyphs (the base + first Mn, the 2nd Mn). All of these map
+ from glyph to first character of the log_cluster range. This destroys the 1:1 correspondence between
+ characters and glyphs. A big chunk of the conditional code which immediately follows this call
+ is there to clean up the resulting mess.
+ */
pango_shape(text_source->text->data() + span_start_byte_in_source,
new_span.text_bytes,
&para->pango_items[pango_item_index].item->analysis,
@@ -1134,24 +1213,55 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const
// let's reverse the glyphstring on a cluster-by-cluster basis
const unsigned nglyphs = new_span.glyph_string->num_glyphs;
std::vector<PangoGlyphInfo> infos(nglyphs);
- std::vector<gint> clusters(nglyphs);
- unsigned i, cluster_start = 0;
-
- for (i = 0 ; i < nglyphs ; ++i) {
- if (new_span.glyph_string->glyphs[i].attr.is_cluster_start) {
- if (i != cluster_start) {
- std::copy(&new_span.glyph_string->glyphs[cluster_start], &new_span.glyph_string->glyphs[i], infos.end() - i);
- std::copy(&new_span.glyph_string->log_clusters[cluster_start], &new_span.glyph_string->log_clusters[i], clusters.end() - i);
- }
- cluster_start = i;
+ std::vector<gint> clusters(nglyphs);
+ unsigned i, j;
+ for (i = 0 ; i < nglyphs ; i++)new_span.glyph_string->glyphs[i].attr.is_cluster_start = 0;
+ for (i = 0 ; i < nglyphs ; i++) {
+ j=i;
+ while( (j < nglyphs-1) &&
+ (new_span.glyph_string->log_clusters[j+1] == new_span.glyph_string->log_clusters[i])
+ )j++;
+ /*
+ CAREFUL, within a log_cluster the order of glyphs may not map 1:1, or
+ even in the same order, to the original unicode characters!!! Among
+ other things, diacritical mark glyphs can end up in front of the base
+ character. That makes determining kerning, even approximately, difficult
+ later on. To resolve this somewhat sort the glyphs with the same
+ log_cluster into descending order by width. In theory there should be 1
+ that is nonzero, and N that are zero. The order of the zero width ones
+ does not matter. Sort the glyphs before copying. If ligatures other than with
+ Mark, nonspacing are ever implemented in Pango this will screw up, for instance
+ changing "fi" to "if".
+ */
+ if(j - i){
+ std::sort(&(new_span.glyph_string->glyphs[i]), &(new_span.glyph_string->glyphs[j+1]), compareGlyphWidth);
}
- }
- if (i != cluster_start) {
- std::copy(&new_span.glyph_string->glyphs[cluster_start], &new_span.glyph_string->glyphs[i], infos.end() - i);
- std::copy(&new_span.glyph_string->log_clusters[cluster_start], &new_span.glyph_string->log_clusters[i], clusters.end() - i);
+
+ new_span.glyph_string->glyphs[i].attr.is_cluster_start = 1;
+ std::copy(&new_span.glyph_string->glyphs[ i], &new_span.glyph_string->glyphs[ j+1], infos.end() - j -1);
+ std::copy(&new_span.glyph_string->log_clusters[i], &new_span.glyph_string->log_clusters[j+1], clusters.end() - j -1);
+ i = j;
}
std::copy(infos.begin(), infos.end(), new_span.glyph_string->glyphs);
std::copy(clusters.begin(), clusters.end(), new_span.glyph_string->log_clusters);
+ /* glyphs[].x_offset values are probably out of order within any log_clusters, apparently harmless */
+ }
+ else { // ltr sections are in order but glyphs in a log_cluster following a ligature may not be. Sort, but no block swapping.
+ const unsigned nglyphs = new_span.glyph_string->num_glyphs;
+ unsigned i, j;
+ for (i = 0 ; i < nglyphs ; i++)new_span.glyph_string->glyphs[i].attr.is_cluster_start = 0;
+ for (i = 0 ; i < nglyphs ; i++) {
+ j=i;
+ while( (j < nglyphs-1) &&
+ (new_span.glyph_string->log_clusters[j+1] == new_span.glyph_string->log_clusters[i])
+ )j++;
+ if(j - i){
+ std::sort(&(new_span.glyph_string->glyphs[i]), &(new_span.glyph_string->glyphs[j+1]), compareGlyphWidth);
+ }
+ new_span.glyph_string->glyphs[i].attr.is_cluster_start = 1;
+ i = j;
+ }
+ /* glyphs[].x_offset values may be out of order within any log_clusters, apparently harmless */
}
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);
@@ -1469,7 +1579,7 @@ bool Layout::Calculator::calculate()
if (_scanline_maker == NULL)
break; // we're trying to flow past the last wrap shape
- _buildPangoItemizationForPara(&para);
+ _buildPangoItemizationForPara(&para);
unsigned para_end_input_index = _buildSpansForPara(&para);
if (_flow._input_stream[para.first_input_index]->Type() == TEXT_SOURCE)
diff --git a/src/libnrtype/Layout-TNG-Output.cpp b/src/libnrtype/Layout-TNG-Output.cpp
index 8ede0a38e..153ef1ef0 100644
--- a/src/libnrtype/Layout-TNG-Output.cpp
+++ b/src/libnrtype/Layout-TNG-Output.cpp
@@ -39,35 +39,44 @@ namespace Text {
/*
dx array (character widths) and
- ky (vertical kerning for entire span)
+ ky (vertical kerning for entire span)
+ rtl (+1 for LTR, -1 RTL)
are smuggled through to the EMF (ignored by others) as:
text<nul>N w1 w2 w3 ...wN<nul>y1 y2 y3 .. yN<nul><nul>
- where the widths and y kern values are floats 7 characters wide, including the space
+ The ndx, widths, y kern, and rtl are all 7 characters wide. ndx and rtl are ints, the widths and ky are
+ formatted as ' 6f'.
*/
-char *smuggle_adxky_in(const char *string, int ndx, float *adx, float ky){
- int slen=strlen(string);
+char *smuggle_adxkyrtl_in(const char *string, int ndx, float *adx, float ky, float rtl){
+ int slen = strlen(string);
/* holds: string
fake terminator (one \0)
- Number of widths (ndxy)
- series of widths (ndxy entries)
+ Number of widths (ndx)
+ series of widths (ndx entries)
fake terminator (one \0)
y kern value (one float)
+ rtl value (one float)
real terminator (two \0)
*/
- int newsize=slen + 1 + 7 + 7*ndx + 1 + 7 + 2;
+ int newsize=slen + 1 + 7 + 7*ndx + 1 + 7 + 7 + 2;
newsize = 8*((7 + newsize)/8); // suppress valgrind messages if it is a multiple of 8 bytes???
- char *smuggle=(char *)calloc(newsize,1); // initialize all bytes, inluding terminators
- strcpy(smuggle,string); // text to pass
+ char *smuggle=(char *)malloc(newsize);
+ strcpy(smuggle,string); // text to pass, includes the first fake terminator
char *cptr = smuggle + slen + 1; // immediately after the first fake terminator
sprintf(cptr,"%07d",ndx); // number of widths to pass
- cptr+=7;
+ cptr+=7; // advance over ndx
for(int i=0; i<ndx ; i++){ // all the widths
sprintf(cptr," %6f",adx[i]);
- cptr+=7;
+ cptr+=7; // advance over space + width
}
+ *cptr='\0';
cptr++; // second fake terminator
- sprintf(cptr," %6f",ky); // y kern for span
+ sprintf(cptr," %6f",ky); // y kern for span
+ cptr+=7; // advance over space + ky
+ sprintf(cptr," %6d",(int) rtl); // rtl multiplier for span
+ cptr+=7; // advance over rtl
+ *cptr++ = '\0'; // Set the real terminators
+ *cptr = '\0';
return(smuggle);
}
@@ -112,21 +121,66 @@ void Layout::_getGlyphTransformMatrix(int glyph_index, Geom::Affine *matrix) con
void Layout::show(DrawingGroup *in_arena, Geom::OptRect const &paintbox) const
{
int glyph_index = 0;
+ double phase0 = 0.0;
for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) {
if (_input_stream[_spans[span_index].in_input_stream_item]->Type() != TEXT_SOURCE) continue;
InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[_spans[span_index].in_input_stream_item]);
+ text_source->style->text_decoration_data.tspan_width = _spans[span_index].width();
+ text_source->style->text_decoration_data.ascender = _spans[span_index].line_height.getAscent();
+ text_source->style->text_decoration_data.descender = _spans[span_index].line_height.getDescent();
+ text_source->style->text_decoration_data.line_gap = _spans[span_index].line_height.getLeading();
+ if(!span_index ||
+ (_chunks[_spans[span_index].in_chunk].in_line != _chunks[_spans[span_index-1].in_chunk].in_line)){
+ text_source->style->text_decoration_data.tspan_line_start = true;
+ }
+ else {
+ text_source->style->text_decoration_data.tspan_line_start = false;
+ }
+ if((span_index == _spans.size() -1) ||
+ (_chunks[_spans[span_index].in_chunk].in_line != _chunks[_spans[span_index+1].in_chunk].in_line)){
+ text_source->style->text_decoration_data.tspan_line_end = true;
+ }
+ else {
+ text_source->style->text_decoration_data.tspan_line_end = false;
+ }
+ if(_spans[span_index].font){
+ double underline_thickness, underline_position, line_through_thickness,line_through_position;
+ _spans[span_index].font->FontDecoration(underline_position, underline_thickness, line_through_position, line_through_thickness);
+ text_source->style->text_decoration_data.underline_thickness = underline_thickness;
+ text_source->style->text_decoration_data.underline_position = underline_position;
+ text_source->style->text_decoration_data.line_through_thickness = line_through_thickness;
+ text_source->style->text_decoration_data.line_through_position = line_through_position;
+ }
+ else { // can this case ever occur?
+ text_source->style->text_decoration_data.underline_thickness =
+ text_source->style->text_decoration_data.underline_position =
+ text_source->style->text_decoration_data.line_through_thickness =
+ text_source->style->text_decoration_data.line_through_position = 0.0;
+ }
+
DrawingText *nr_text = new DrawingText(in_arena->drawing());
- nr_text->setStyle(text_source->style);
+ bool first_line_glyph = true;
while (glyph_index < (int)_glyphs.size() && _characters[_glyphs[glyph_index].in_character].in_span == span_index) {
if (_characters[_glyphs[glyph_index].in_character].in_glyph != -1) {
Geom::Affine glyph_matrix;
_getGlyphTransformMatrix(glyph_index, &glyph_matrix);
- nr_text->addComponent(_spans[span_index].font, _glyphs[glyph_index].glyph, glyph_matrix);
+ if(first_line_glyph && text_source->style->text_decoration_data.tspan_line_start){
+ first_line_glyph = false;
+ phase0 = glyph_matrix.translation()[Geom::X];
+ }
+ // save the starting coordinates for the line - these are needed for figuring out dot/dash/wave phase
+ (void) nr_text->addComponent(_spans[span_index].font, _glyphs[glyph_index].glyph, glyph_matrix,
+ _glyphs[glyph_index].width,
+ _spans[span_index].line_height.getAscent(),
+ _spans[span_index].line_height.getDescent(),
+ glyph_matrix.translation()[Geom::X] - phase0
+ );
}
glyph_index++;
}
+ nr_text->setStyle(text_source->style);
nr_text->setItemBounds(paintbox);
in_arena->prependChild(nr_text);
}
@@ -158,36 +212,30 @@ Geom::OptRect Layout::bounds(Geom::Affine const &transform, int start, int lengt
return bbox;
}
+/* This version is much simpler than the old one
+*/
void Layout::print(SPPrintContext *ctx,
Geom::OptRect const &pbox, Geom::OptRect const &dbox, Geom::OptRect const &bbox,
Geom::Affine const &ctm) const
{
-int doUTN=0;
-int lasttarget=0;
-int newtarget=0;
+bool text_to_path = ctx->module->textToPath();
+int oldtarget = 0;
+int newtarget = 0;
#define MAX_DX 2048
-float hold_dx[MAX_DX]; // For smuggling dx values (character widths) into print functions, unlikely any simple text output will be longer than this.
-float ky; // For smuggling y kern value for span
-int ndx=0;
+float hold_dx[MAX_DX]; // For smuggling dx values (character widths) into print functions, unlikely any simple text output will be longer than this.
+float ky; // For smuggling y kern value for span
+int ndx = 0;
+double rtl = 1.0; // 1 L->R, -1 R->L, constant across a span. 1.0 for t->b b->t???
+Geom::Affine glyph_matrix;
if (_input_stream.empty()) return;
-
- Direction block_progression = _blockProgression();
- bool text_to_path = ctx->module->textToPath();
- doUTN = CanUTN(); // Unicode to Nonunicode translation enabled if true
- for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; ) {
- if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1) {
- // invisible glyphs
- unsigned same_character = _glyphs[glyph_index].in_character;
- while (_glyphs[glyph_index].in_character == same_character)
- glyph_index++;
- continue;
- }
- Geom::Affine glyph_matrix;
- Span const &span = _spans[_characters[_glyphs[glyph_index].in_character].in_span];
- InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
- if (text_to_path || _path_fitted) {
+ if (!_glyphs.size()) return; // yes, this can happen.
+ if (text_to_path || _path_fitted) {
+ for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; glyph_index++) {
+ if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1)continue; //invisible glyphs
+ Span const &span = _spans[_characters[_glyphs[glyph_index].in_character].in_span];
Geom::PathVector const * pv = span.font->PathVector(_glyphs[glyph_index].glyph);
+ InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
if (pv) {
_getGlyphTransformMatrix(glyph_index, &glyph_matrix);
Geom::PathVector temp_pv = (*pv) * glyph_matrix;
@@ -196,9 +244,28 @@ int ndx=0;
if (!text_source->style->stroke.isNone())
sp_print_stroke(ctx, temp_pv, ctm, text_source->style, pbox, dbox, bbox);
}
- glyph_index++;
- } else {
- Geom::Point g_pos(0,0); // all strings are output at (0,0) because we do the translation using the matrix
+ }
+ }
+ else {
+ /* index by characters, referencing glyphs and spans only as needed */
+ double char_x;
+ int doUTN = CanUTN(); // Unicode to Nonunicode translation enabled if true
+ Direction block_progression = _blockProgression();
+
+ for (unsigned char_index = 0 ; char_index < _characters.size() ; ) {
+ Glib::ustring text_string; // accumulate text for record in this
+ Geom::Point g_pos(0,0); // all strings are output at (0,0) because we do the translation using the matrix
+ int glyph_index = _characters[char_index].in_glyph;
+ if(glyph_index == -1){ // if the character maps to an invisible glyph we cannot know its geometry, so skip it and move on
+ char_index++;
+ continue;
+ }
+ ky = _glyphs[glyph_index].y; // same value for all positions in a span
+ unsigned span_index = _characters[char_index].in_span;
+ Span const &span = _spans[span_index];
+ char_x = 0.0;
+ Glib::ustring::const_iterator text_iter = span.input_stream_first_character;
+ InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
glyph_matrix = Geom::Scale(1.0, -1.0) * (Geom::Affine)Geom::Rotate(_glyphs[glyph_index].rotation);
if (block_progression == LEFT_TO_RIGHT || block_progression == RIGHT_TO_LEFT) {
glyph_matrix[4] = span.line(this).baseline_y + span.baseline_shift;
@@ -208,78 +275,113 @@ int ndx=0;
glyph_matrix[4] = span.chunk(this).left_x + span.x_start + _characters[_glyphs[glyph_index].in_character].x;
glyph_matrix[5] = span.line(this).baseline_y + span.baseline_shift;
}
- Glib::ustring::const_iterator span_iter = span.input_stream_first_character;
- unsigned char_index = _glyphs[glyph_index].in_character;
- unsigned original_span = _characters[char_index].in_span;
- while (char_index && _characters[char_index - 1].in_span == original_span) {
- char_index--;
- span_iter++;
+ switch(span.direction){
+ case Layout::TOP_TO_BOTTOM:
+ case Layout::BOTTOM_TO_TOP:
+ case Layout::LEFT_TO_RIGHT: rtl = 1.0; break;
+ case Layout::RIGHT_TO_LEFT: rtl = -1.0; break;
}
+ if(doUTN)oldtarget=SingleUnicodeToNon(*text_iter); // this should only ever be with a 1:1 glyph:character situation
+
+ // accumulate a record to write
+
+ unsigned lc_index = char_index;
+ unsigned hold_iisi = _spans[span_index].in_input_stream_item;
+ while(1){
+ glyph_index = _characters[lc_index].in_glyph;
+ if(glyph_index == -1){ // end of a line within a paragraph, for instance
+ lc_index++;
+ break;
+ }
- // try to output as many characters as possible in one go by detecting kerning and stopping when we encounter it
- // also break spans at changes in Unicode->nonunicode translations, so that each span
- // sent down from here is translated the same way. The translation happens much later
- // in the emf-print code.
- // Note that the incoming stream has a predefined notion of what is in each "span" and it is not
- // entirely clear why. For instance, the string "%%% text %%%%", where % is the Unicode "Sagittarius"
- // character has 3 spans, with the first two ending on the spaces. Yet in the XML there is only one tspan.
- // Consequently when the Unicode->NonUnicode detection is on the first space will go out by itself,
- // because it is at the end of a span, whereas the second space goes with the "text".
-
- Glib::ustring span_string;
- double char_x = _characters[_glyphs[glyph_index].in_character].x;
- unsigned this_span_index = _characters[_glyphs[glyph_index].in_character].in_span;
- if(doUTN)newtarget=lasttarget=SingleUnicodeToNon(*span_iter);
-
- do {
+ // always append if here
+ text_string += *text_iter;
+
+ // figure out char widths, used by EMF, not currently used elsewhere
+ double cwidth;
+ if(lc_index == _glyphs[glyph_index].in_character){ // Glyph width is used only for the first character, these may be 0
+ cwidth = rtl * _glyphs[glyph_index].width; // width might be zero
+ }
+ else {
+ cwidth = 0;
+ }
+ char_x += cwidth;
/*
-std::cout << "glyph info at:" << glyph_index
-<< " glyphNo:" << _glyphs[glyph_index].glyph
-<< " in_character:" << _glyphs[glyph_index].in_character
-<< " x:" << _glyphs[glyph_index].x
-<< " y:" << _glyphs[glyph_index].y
-<< " rotation:" << _glyphs[glyph_index].rotation
-<< " width:" << _glyphs[glyph_index].width
-<< std::endl;
+std:: cout << "DEBUG Layout::print in while "
+<< " char_index " << char_index
+<< " lc_index " << lc_index
+<< " character " << std::hex << (int) *text_iter << std::dec
+<< " glyph_index " << glyph_index
+<< " glyph_xy " << _glyphs[glyph_index].x << " , " << _glyphs[glyph_index].y
+<< " span_index " << span_index
+<< " hold_iisi " << hold_iisi
+<< std::endl; //DEBUG
*/
- span_string += *span_iter;
- span_iter++;
- if(doUTN)newtarget=SingleUnicodeToNon(*span_iter);
-
- unsigned same_character = _glyphs[glyph_index].in_character;
- ky = _glyphs[glyph_index].y; // same value for all positions in a span
- while (glyph_index < _glyphs.size() && _glyphs[glyph_index].in_character == same_character) {
- char_x += _glyphs[glyph_index].width;
- if(ndx < MAX_DX){
- hold_dx[ndx++] = _glyphs[glyph_index].width;
- }
- else { // silently truncate any text line silly enough to be longer than MAX_DX
- break;
- }
- glyph_index++;
+ if(ndx < MAX_DX){
+ hold_dx[ndx++] = fabs(cwidth);
+ }
+ else { // silently truncate any text line silly enough to be longer than MAX_DX
+ lc_index = _characters.size();
+ break;
}
- } while (glyph_index < _glyphs.size()
- && _path_fitted == NULL
- && _characters[_glyphs[glyph_index].in_character].in_span == this_span_index
- && fabs(char_x - _characters[_glyphs[glyph_index].in_character].x) < 1e-4
- && (doUTN ? (lasttarget==newtarget ? 1 : 0) : 1 )
- );
+
+
+ // conditions that prevent this character from joining the record
+ lc_index++;
+ if(lc_index >= _characters.size()) break; // nothing more to process, so it must be the end of the record
+ text_iter++;
+ if(doUTN)newtarget=SingleUnicodeToNon(*text_iter); // this should only ever be with a 1:1 glyph:character situation
+ if(newtarget != oldtarget)break; // change in unicode to nonunicode translation status
+ // MUST exit on any major span change, but not on some little events, like a font substitution event irrelvant for the file save
+ unsigned next_span_index = _characters[lc_index].in_span;
+ if(span_index != next_span_index){
+ /* on major changes break out of loop.
+ 1st case usually indicates an entire input line has been processed (out of several in a paragraph)
+ 2nd case usually indicates that a format change within a line (font/size/color/etc) is present.
+ */
+/*
+std:: cout << "DEBUG Layout::print in while --- "
+<< " char_index " << char_index
+<< " lc_index " << lc_index
+<< " cwidth " << cwidth
+<< " _char.x (next) " << (lc_index < _characters.size() ? _characters[lc_index].x : -1)
+<< " char_x (end this)" << char_x
+<< " diff " << fabs(char_x - _characters[lc_index].x)
+<< " oldy " << ky
+<< " nexty " << _glyphs[_characters[lc_index].in_glyph].y
+<< std::endl; //DEBUG
+*/
+ if(hold_iisi != _spans[next_span_index].in_input_stream_item)break; // major change, font, size, color, etc, must exit
+ if(fabs(char_x - _spans[next_span_index].x_start) >= 1e-4)break; // xkerning change
+ if(ky != _glyphs[_characters[lc_index].in_glyph].y)break; // ykerning change
+ /*
+ None of the above? Then this is a minor "pangito", update span_index and keep going.
+ The font used by the display may have failed over, but print does not care and can continue to use
+ whatever was specified in the XML.
+ */
+ span_index = next_span_index;
+ text_iter = _spans[span_index].input_stream_first_character;
+ }
+
+ }
+ // write it
sp_print_bind(ctx, glyph_matrix, 1.0);
- // the dx array is smuggled through to the EMF (ignored by others) as:
+ // the dx array is smuggled through to the EMF driver (ignored by others) as:
// text<nul>w1 w2 w3 ...wn<nul><nul>
// where the widths are floats 7 characters wide, including the space
- char *smuggle_string=smuggle_adxky_in(span_string.c_str(),ndx, &hold_dx[0], ky);
-// sp_print_text(ctx, span_string.c_str(), g_pos, text_source->style);
+ char *smuggle_string=smuggle_adxkyrtl_in(text_string.c_str(),ndx, &hold_dx[0], ky, rtl);
sp_print_text(ctx, smuggle_string, g_pos, text_source->style);
free(smuggle_string);
sp_print_release(ctx);
ndx=0;
+ char_index = lc_index;
}
}
}
+
#ifdef HAVE_CAIRO_PDF
void Layout::showGlyphs(CairoRenderContext *ctx) const
{
@@ -432,10 +534,38 @@ Glib::ustring Layout::getFontFamily(unsigned span_index) const
Glib::ustring Layout::dumpAsText() const
{
Glib::ustring result;
+ char line[256];
+
+ Glib::ustring::const_iterator icc;
+
+ snprintf(line, sizeof(line), "spans %d\n", _spans.size());
+ result += line;
+ snprintf(line, sizeof(line), "chars %d\n", _characters.size());
+ result += line;
+ snprintf(line, sizeof(line), "glyphs %d\n", _glyphs.size());
+ result += line;
+ unsigned lastspan=5000;
+ if(_characters.size() > 1){
+ for(unsigned j = 0; j < _characters.size() ; j++){
+ if(lastspan != _characters[j].in_span){
+ lastspan = _characters[j].in_span;
+ icc = _spans[lastspan].input_stream_first_character;
+ }
+ snprintf(line, sizeof(line), "char %4d: '%c' 0x%4.4x x=%8.4f glyph=%3d span=%3d\n", j, *icc, *icc, _characters[j].x, _characters[j].in_glyph, _characters[j].in_span);
+ result += line;
+ icc++;
+ }
+ }
+ if(_glyphs.size()){
+ for(unsigned j = 0; j < _glyphs.size() ; j++){
+ snprintf(line, sizeof(line), "glyph %4d: %4d (%8.4f,%8.4f) rot=%8.4f cx=%8.4f char=%4d\n",
+ j, _glyphs[j].glyph, _glyphs[j].x, _glyphs[j].y, _glyphs[j].rotation, _glyphs[j].width, _glyphs[j].in_character);
+ result += line;
+ }
+ }
for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) {
- char line[256];
- snprintf(line, sizeof(line), "==== span %d\n", span_index);
+ snprintf(line, sizeof(line), "==== span %d \n", span_index);
result += line;
snprintf(line, sizeof(line), " in para %d (direction=%s)\n", _lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph,
direction_to_text(_paragraphs[_lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph].base_direction));
@@ -469,8 +599,8 @@ Glib::ustring Layout::dumpAsText() const
if (_characters[char_index].in_span != span_index) continue;
if (_input_stream[_spans[span_index].in_input_stream_item]->Type() != TEXT_SOURCE) {
snprintf(line, sizeof(line), " %d: control x=%f flags=%03x glyph=%d\n", char_index, _characters[char_index].x, *u.uattr, _characters[char_index].in_glyph);
- } else {
- snprintf(line, sizeof(line), " %d: '%c' x=%f flags=%03x glyph=%d\n", char_index, *iter_char, _characters[char_index].x, *u.uattr, _characters[char_index].in_glyph);
+ } else { // some text has empty tspans, iter_char cannot be dereferenced
+ snprintf(line, sizeof(line), " %d: '%c' 0x%4.4x x=%f flags=%03x glyph=%d\n", char_index, *iter_char, *iter_char, _characters[char_index].x, *u.uattr, _characters[char_index].in_glyph);
iter_char++;
}
result += line;
diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h
index 0f5f08a53..573301c69 100644
--- a/src/libnrtype/Layout-TNG.h
+++ b/src/libnrtype/Layout-TNG.h
@@ -568,6 +568,9 @@ public:
inline void setZero() {ascent = descent = leading = 0.0;}
inline LineHeight& operator*=(double x) {ascent *= x; descent *= x; leading *= x; return *this;}
void max(LineHeight const &other); /// makes this object contain the largest of all three members between this object and other
+ inline double getAscent() const {return ascent; }
+ inline double getDescent() const {return descent; }
+ inline double getLeading() const {return leading; }
};
/// see _enum_converter()
@@ -717,6 +720,7 @@ private:
float font_size;
float x_start; /// relative to the start of the chunk
float x_end; /// relative to the start of the chunk
+ inline float width() const {return std::abs(x_start - x_end);}
LineHeight line_height;
double baseline_shift; /// relative to the line's baseline
Direction direction; /// See CSS3 section 3.2. Either rtl or ltr
diff --git a/src/libnrtype/font-instance.h b/src/libnrtype/font-instance.h
index d00569984..ef10a04d0 100644
--- a/src/libnrtype/font-instance.h
+++ b/src/libnrtype/font-instance.h
@@ -59,6 +59,8 @@ public:
double Advance(int glyph_id, bool vertical);
// nominal advance of the font.
bool FontMetrics(double &ascent, double &descent, double &leading);
+ bool FontDecoration(double &underline_position, double &underline_thickness,
+ double &linethrough_position, double &linethrough_thickness);
bool FontSlope(double &run, double &rise);
// for generating slanted cursors for oblique fonts
Geom::OptRect BBox(int glyph_id);