summaryrefslogtreecommitdiffstats
path: root/src/libnrtype
diff options
context:
space:
mode:
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);