summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/2geom/generic-rect.h2
-rw-r--r--src/attributes-test.h3
-rw-r--r--src/attributes.cpp3
-rw-r--r--src/attributes.h5
-rw-r--r--src/display/drawing-context.h15
-rw-r--r--src/display/drawing-text.cpp304
-rw-r--r--src/display/drawing-text.h15
-rw-r--r--src/display/nr-style.cpp58
-rw-r--r--src/display/nr-style.h37
-rw-r--r--src/extension/internal/emf-inout.cpp23
-rw-r--r--src/extension/internal/emf-print.cpp50
-rw-r--r--src/extension/internal/emf-print.h3
-rw-r--r--src/extension/internal/text_reassemble.c1215
-rw-r--r--src/extension/internal/text_reassemble.h125
-rw-r--r--src/extension/internal/wmf-inout.cpp44
-rw-r--r--src/extension/internal/wmf-print.cpp41
-rw-r--r--src/extension/internal/wmf-print.h3
-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
-rw-r--r--src/sp-image.cpp1
-rw-r--r--src/style.cpp380
-rw-r--r--src/style.h44
-rw-r--r--src/text-editing.cpp58
26 files changed, 2338 insertions, 690 deletions
diff --git a/src/2geom/generic-rect.h b/src/2geom/generic-rect.h
index bb6f2d2b8..eb9705a4b 100644
--- a/src/2geom/generic-rect.h
+++ b/src/2geom/generic-rect.h
@@ -261,7 +261,7 @@ public:
expandBy(amount, amount);
}
/** @brief Expand the rectangle in both directions.
- * Note that this is different from scaling. Negative values wil shrink the
+ * Note that this is different from scaling. Negative values will shrink the
* rectangle. If <code>-x</code> is larger than
* half of the width, the X interval will contain only the X coordinate
* of the midpoint; same for height. */
diff --git a/src/attributes-test.h b/src/attributes-test.h
index 78e0ea48f..548278212 100644
--- a/src/attributes-test.h
+++ b/src/attributes-test.h
@@ -276,6 +276,9 @@ struct {char const *attr; bool supported;} const all_attrs[] = {
{"text-align", true},
{"text-anchor", true},
{"text-decoration", true},
+ {"text-decoration-line", false},
+ {"text-decoration-style", false},
+ {"text-decoration-color", false},
{"text-indent", true},
{"text-rendering", true},
{"text-transform", true},
diff --git a/src/attributes.cpp b/src/attributes.cpp
index 30cf30f30..b93bcfd57 100644
--- a/src/attributes.cpp
+++ b/src/attributes.cpp
@@ -414,6 +414,9 @@ static SPStyleProp const props[] = {
{SP_PROP_TEXT_INDENT, "text-indent"},
{SP_PROP_TEXT_ALIGN, "text-align"},
{SP_PROP_TEXT_DECORATION, "text-decoration"},
+ {SP_PROP_TEXT_DECORATION_LINE, "text-decoration-line"},
+ {SP_PROP_TEXT_DECORATION_STYLE,"text-decoration-style"},
+ {SP_PROP_TEXT_DECORATION_COLOR,"text-decoration-color"},
{SP_PROP_LINE_HEIGHT, "line-height"},
{SP_PROP_LETTER_SPACING, "letter-spacing"},
{SP_PROP_WORD_SPACING, "word-spacing"},
diff --git a/src/attributes.h b/src/attributes.h
index 87cd9ed45..24244c237 100644
--- a/src/attributes.h
+++ b/src/attributes.h
@@ -414,7 +414,10 @@ enum SPAttributeEnum {
/* Text */
SP_PROP_TEXT_INDENT,
SP_PROP_TEXT_ALIGN,
- SP_PROP_TEXT_DECORATION,
+ SP_PROP_TEXT_DECORATION, /* SVG 1 underline etc.( no color or style) OR SVG2 with _LINE, _STYLE, _COLOR values */
+ SP_PROP_TEXT_DECORATION_LINE, /* SVG 2 underline etc. */
+ SP_PROP_TEXT_DECORATION_STYLE, /* SVG 2 proposed solid [SVG 1], dotted, etc.)*/
+ SP_PROP_TEXT_DECORATION_COLOR, /* SVG 2 proposed same as text [SVG 1], specified*/
SP_PROP_LINE_HEIGHT,
SP_PROP_LETTER_SPACING,
SP_PROP_WORD_SPACING,
diff --git a/src/display/drawing-context.h b/src/display/drawing-context.h
index b8c4667c2..d4f0dbfc3 100644
--- a/src/display/drawing-context.h
+++ b/src/display/drawing-context.h
@@ -67,6 +67,21 @@ public:
void rectangle(Geom::IntRect const &r) {
cairo_rectangle(_ct, r.left(), r.top(), r.width(), r.height());
}
+ // Used in drawing-text.cpp to overwrite glyphs, which have the opposite path rotation as a regular rect
+ void revrectangle(Geom::Rect const &r) {
+ cairo_move_to ( _ct, r.left(), r.top() );
+ cairo_rel_line_to (_ct, 0, r.height() );
+ cairo_rel_line_to (_ct, r.width(), 0 );
+ cairo_rel_line_to (_ct, 0, -r.height() );
+ cairo_close_path ( _ct);
+ }
+ void revrectangle(Geom::IntRect const &r) {
+ cairo_move_to ( _ct, r.left(), r.top() );
+ cairo_rel_line_to (_ct, 0, r.height() );
+ cairo_rel_line_to (_ct, r.width(), 0 );
+ cairo_rel_line_to (_ct, 0, -r.height() );
+ cairo_close_path ( _ct);
+ }
void newPath() { cairo_new_path(_ct); }
void newSubpath() { cairo_new_sub_path(_ct); }
void path(Geom::PathVector const &pv);
diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp
index 2a6505c67..234006983 100644
--- a/src/display/drawing-text.cpp
+++ b/src/display/drawing-text.cpp
@@ -18,9 +18,11 @@
#include "helper/geom.h"
#include "libnrtype/font-instance.h"
#include "style.h"
+#include "2geom/pathvector.h"
namespace Inkscape {
+
DrawingGlyphs::DrawingGlyphs(Drawing &drawing)
: DrawingItem(drawing)
, _font(NULL)
@@ -61,12 +63,34 @@ unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext
return STATE_ALL;
}
+
_pick_bbox = Geom::IntRect();
_bbox = Geom::IntRect();
- Geom::OptRect b = bounds_exact_transformed(*_font->PathVector(_glyph), ctx.ctm);
+ Geom::OptRect b;
+
+/* orignally it did the one line below,
+ but it did not handle ws characters at all, and it had problems with scaling for overline/underline.
+ Replaced with the section below, which seems to be much more stable.
+
+ b = bounds_exact_transformed(*_font->PathVector(_glyph), ctx.ctm);
+*/
+ /* Make a bounding box that is a little taller and lower (currently 10% extra) than the font's drawing box. Extra space is
+ to hold overline or underline, if present. All characters in a font use the same ascent and descent,
+ but different widths. This lets leading and trailing spaces have text decorations. If it is not done
+ the bounding box is limited to the box surrounding the drawn parts of visible glyphs only, and draws outside are ignored.
+ */
+
+ float scale = 1.0;
+ if(_transform){ scale /= _transform->descrim(); }
+
+ Geom::Rect bigbox(Geom::Point(0.0, _asc*scale*1.1),Geom::Point(_width*scale, -_dsc*scale*1.1));
+ b = bigbox * ctx.ctm;
+
if (b && (ggroup->_nrstyle.stroke.type != NRStyle::PAINT_NONE)) {
float width, scale;
+
+ // this expands the selection box for cases where the stroke is "thick"
scale = ctx.ctm.descrim();
if (_transform) {
scale /= _transform->descrim(); // FIXME temporary hack
@@ -75,7 +99,8 @@ unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext
if ( fabs(ggroup->_nrstyle.stroke_width * scale) > 0.01 ) { // FIXME: this is always true
b->expandBy(0.5 * width);
}
- // save bbox without miters for picking
+
+ // save bbox without miters for picking
_pick_bbox = b->roundOutwards();
float miterMax = width * ggroup->_nrstyle.miter_limit;
@@ -89,7 +114,12 @@ unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext
_bbox = b->roundOutwards();
_pick_bbox = *_bbox;
}
-
+/*
+std::cout << "DEBUG _bbox"
+<< " { " << _bbox->min()[Geom::X] << " , " << _bbox->min()[Geom::Y]
+<< " } , { " << _bbox->max()[Geom::X] << " , " << _bbox->max()[Geom::Y]
+<< " }" << std::endl;
+*/
return STATE_ALL;
}
@@ -106,7 +136,7 @@ DrawingGlyphs::_pickItem(Geom::Point const &p, double delta, unsigned /*flags*/)
return NULL;
}
- // With text we take a simple approach: pick if the point is in a characher bbox
+ // With text we take a simple approach: pick if the point is in a character bbox
Geom::Rect expanded(_pick_bbox);
expanded.expandBy(delta);
if (expanded.contains(p)) return this;
@@ -129,17 +159,28 @@ DrawingText::clear()
_children.clear_and_dispose(DeleteDisposer());
}
-void
-DrawingText::addComponent(font_instance *font, int glyph, Geom::Affine const &trans)
+bool
+DrawingText::addComponent(font_instance *font, int glyph, Geom::Affine const &trans,
+ float width, float ascent, float descent, float phase_length)
{
+/* original, did not save a glyph for white space characters, causes problems for text-decoration
if (!font || !font->PathVector(glyph)) {
- return;
+ return(false);
}
+*/
+ if (!font)return(false);
_markForRendering();
DrawingGlyphs *ng = new DrawingGlyphs(_drawing);
ng->setGlyph(font, glyph, trans);
+ if(font->PathVector(glyph)){ ng->_drawable = true; }
+ else { ng->_drawable = false; }
+ ng->_width = width; // only used when _drawable = false
+ ng->_asc = ascent; // of font, not of this one character
+ ng->_dsc = descent; // of font, not of this one character
+ ng->_pl = phase_length; // used for phase of dots, dashes, and wavy
appendChild(ng);
+ return(true);
}
void
@@ -156,9 +197,175 @@ DrawingText::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, un
return DrawingGroup::_updateItem(area, ctx, flags, reset);
}
+void DrawingText::decorateStyle(DrawingContext &ct, double vextent, double xphase, Geom::Point p1, Geom::Point p2)
+{
+ double wave[16]={
+ 0.000000, 0.382499, 0.706825, 0.923651, 1.000000, 0.923651, 0.706825, 0.382499,
+ 0.000000, -0.382499, -0.706825, -0.923651, -1.000000, -0.923651, -0.706825, -0.382499,
+ };
+ int dashes[16]={
+ 8, 7, 6, 5,
+ 4, 3, 2, 1,
+ -8, -7, -6, -5
+ -4, -3, -2, -1
+ };
+ int dots[16]={
+ 4, 3, 2, 1,
+ -4, -3, -2, -1,
+ 4, 3, 2, 1,
+ -4, -3, -2, -1
+ };
+ Geom::Point p3,p4,ps,pf;
+ double step = vextent/32.0;
+ unsigned i = 15 & (unsigned) round(xphase/step); // xphase is >= 0.0
+
+ /* For most spans draw the last little bit right to p2 or even a little beyond.
+ This allows decoration continuity within the line, and does not step outside the clip box off the end
+ For the first/last section on the line though, stay well clear of the edge, or when the
+ text is dragged it may "spray" pixels.
+ if(_nrstyle.tspan_line_end){ pf = p2 - Geom::Point(2*step, 0.0); }
+ else { pf = p2; }
+ if(_nrstyle.tspan_line_start){ ps = p1 + Geom::Point(2*step, 0.0);
+ i = 15 & (i + 2);
+ }
+ else { ps = p1; }
+ */
+ /* snap to nearest step in X */
+ps = Geom::Point(step * round(p1[Geom::X]/step),p1[Geom::Y]);
+pf = Geom::Point(step * round(p2[Geom::X]/step),p2[Geom::Y]);
+
+ if(_nrstyle.text_decoration_style & TEXT_DECORATION_STYLE_ISDOUBLE){
+ ps -= Geom::Point(0, vextent/12.0);
+ pf -= Geom::Point(0, vextent/12.0);
+ ct.moveTo(ps);
+ ct.lineTo(pf);
+ ps += Geom::Point(0, vextent/6.0);
+ pf += Geom::Point(0, vextent/6.0);
+ ct.moveTo(ps);
+ ct.lineTo(pf);
+ }
+ /* The next three have a problem in that they are phase dependent. The bits of a line are not
+ necessarily passing through this routine in order, so we have to use the xphase information
+ to figure where in each of their cycles to start. Only accurate to 1 part in 16.
+ Huge possitive offset should keep the phase calculation from ever being negative.
+ */
+ else if(_nrstyle.text_decoration_style & TEXT_DECORATION_STYLE_DOTTED){
+ while(1){
+ if(dots[i]>0){
+ if(ps[Geom::X]> pf[Geom::X])break;
+ ct.moveTo(ps);
+ ps += Geom::Point(step * (double)dots[i], 0.0);
+ if(ps[Geom::X]>= pf[Geom::X]){
+ ct.lineTo(pf);
+ break;
+ }
+ else {
+ ct.lineTo(ps);
+ }
+ ps += Geom::Point(step * 4.0, 0.0);
+ }
+ else {
+ ps += Geom::Point(step * -(double)dots[i], 0.0);
+ }
+ i = 0; // once in phase, it stays in phase
+ }
+ }
+ else if(_nrstyle.text_decoration_style & TEXT_DECORATION_STYLE_DASHED){
+ while(1){
+ if(dashes[i]>0){
+ if(ps[Geom::X]> pf[Geom::X])break;
+ ct.moveTo(ps);
+ ps += Geom::Point(step * (double)dashes[i], 0.0);
+ if(ps[Geom::X]>= pf[Geom::X]){
+ ct.lineTo(pf);
+ break;
+ }
+ else {
+ ct.lineTo(ps);
+ }
+ ps += Geom::Point(step * 8.0, 0.0);
+ }
+ else {
+ ps += Geom::Point(step * -(double)dashes[i], 0.0);
+ }
+ i = 0; // once in phase, it stays in phase
+ }
+ }
+ else if(_nrstyle.text_decoration_style & TEXT_DECORATION_STYLE_WAVY){
+ double amp = vextent/10.0;
+ double x = ps[Geom::X];
+ double y = ps[Geom::Y];
+ ct.moveTo(Geom::Point(x, y + amp * wave[i]));
+ while(1){
+ i = ((i + 1) & 15);
+ x += step;
+ ct.lineTo(Geom::Point(x, y + amp * wave[i]));
+ if(x >= pf[Geom::X])break;
+ }
+ }
+ else { // TEXT_DECORATION_STYLE_SOLID, also default in case it was not set for some reason
+ ct.moveTo(ps);
+ ct.lineTo(pf);
+// ct.revrectangle(Geom::Rect(ps,pf));
+ }
+}
+
+/* returns scaled line thickness */
+double DrawingText::decorateItem(DrawingContext &ct, Geom::Affine aff, double phase_length)
+{
+ double tsp_width_adj, tsp_asc_adj, tsp_size_adj;
+ double final_underline_thickness, final_line_through_thickness;
+ double thickness;
+
+ tsp_width_adj = _nrstyle.tspan_width / _nrstyle.font_size;
+ tsp_asc_adj = _nrstyle.ascender / _nrstyle.font_size;
+ tsp_size_adj = (_nrstyle.ascender + _nrstyle.descender) / _nrstyle.font_size;
+#define VALTRUNC(A,B,C) (A < B ? B : ( A > C ? C : A ))
+ final_underline_thickness = VALTRUNC(_nrstyle.underline_thickness, tsp_size_adj/30.0, tsp_size_adj/10.0);
+ final_line_through_thickness = VALTRUNC(_nrstyle.line_through_thickness, tsp_size_adj/30.0, tsp_size_adj/10.0);
+ Inkscape::DrawingContext::Save save(ct);
+
+ double scale = aff.descrim();
+ double xphase = phase_length/ _nrstyle.font_size; // used to figure out phase of patterns
+
+ ct.transform(aff); // must be leftmost affine in span
+ Geom::Point p1;
+ Geom::Point p2;
+ // All lines must be the same thickness, in combinations, line_through trumps underline
+ thickness = final_underline_thickness;
+ if(_nrstyle.text_decoration_line & TEXT_DECORATION_LINE_UNDERLINE){
+ p1 = Geom::Point(0.0, -_nrstyle.underline_position);
+ p2 = Geom::Point(tsp_width_adj,-_nrstyle.underline_position);
+ decorateStyle(ct, tsp_size_adj, xphase, p1, p2);
+ }
+ if(_nrstyle.text_decoration_line & TEXT_DECORATION_LINE_OVERLINE){
+ p1 = Geom::Point(0.0, tsp_asc_adj -_nrstyle.underline_position + 1 * final_underline_thickness);
+ p2 = Geom::Point(tsp_width_adj,tsp_asc_adj -_nrstyle.underline_position + 1 * final_underline_thickness);
+ decorateStyle(ct, tsp_size_adj, xphase, p1, p2);
+ }
+ if(_nrstyle.text_decoration_line & TEXT_DECORATION_LINE_LINETHROUGH){
+ thickness = final_line_through_thickness;
+ p1 = Geom::Point(0.0, _nrstyle.line_through_position);
+ p2 = Geom::Point(tsp_width_adj,_nrstyle.line_through_position);
+ decorateStyle(ct, tsp_size_adj, xphase, p1, p2);
+ }
+ // Obviously this does not blink, but it does indicate which text has been set with that attribute
+ if(_nrstyle.text_decoration_line & TEXT_DECORATION_LINE_BLINK){
+ thickness = final_line_through_thickness;
+ p1 = Geom::Point(0.0, _nrstyle.line_through_position - 2*final_line_through_thickness);
+ p2 = Geom::Point(tsp_width_adj,_nrstyle.line_through_position - 2*final_line_through_thickness);
+ decorateStyle(ct, tsp_size_adj, xphase, p1, p2);
+ p1 = Geom::Point(0.0, _nrstyle.line_through_position + 2*final_line_through_thickness);
+ p2 = Geom::Point(tsp_width_adj,_nrstyle.line_through_position + 2*final_line_through_thickness);
+ decorateStyle(ct, tsp_size_adj, xphase, p1, p2);
+ }
+ thickness *= scale;
+ return(thickness);
+}
+
unsigned DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &/*area*/, unsigned /*flags*/, DrawingItem * /*stop_at*/)
{
- if (_drawing.outline()) {
+ if (_drawing.outline()) {
guint32 rgba = _drawing.outlinecolor;
Inkscape::DrawingContext::Save save(ct);
ct.setSource(rgba);
@@ -169,34 +376,72 @@ unsigned DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &/*are
if (!g) throw InvalidItemException();
Inkscape::DrawingContext::Save save(ct);
- // skip glpyhs with singular transforms
+ // skip glyphs with singular transforms
if (g->_ctm.isSingular()) continue;
ct.transform(g->_ctm);
- ct.path(*g->_font->PathVector(g->_glyph));
- ct.fill();
+ if(g->_drawable){
+ ct.path(*g->_font->PathVector(g->_glyph));
+ ct.fill();
+ }
}
return RENDER_OK;
}
// NOTE: this is very similar to drawing-shape.cpp; the only difference is in path feeding
- bool has_stroke, has_fill;
-
- has_fill = _nrstyle.prepareFill(ct, _item_bbox);
+ double leftmost = DBL_MAX;
+ double phase_length = 0.0;
+ bool firsty = true;
+ bool decorate = true;
+ double starty = 0.0;
+ bool has_stroke, has_fill;
+ Geom::Affine aff;
+ using Geom::X;
+ using Geom::Y;
+
+ has_fill = _nrstyle.prepareFill( ct, _item_bbox);
has_stroke = _nrstyle.prepareStroke(ct, _item_bbox);
if (has_fill || has_stroke) {
+ Geom::Affine rotinv;
+ bool invset=false;
for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
DrawingGlyphs *g = dynamic_cast<DrawingGlyphs *>(&*i);
if (!g) throw InvalidItemException();
+ if(!invset){
+ rotinv = g->_ctm.withoutTranslation().inverse();
+ invset = true;
+ }
Inkscape::DrawingContext::Save save(ct);
if (g->_ctm.isSingular()) continue;
ct.transform(g->_ctm);
- ct.path(*g->_font->PathVector(g->_glyph));
+ if(g->_drawable){
+ ct.path(*g->_font->PathVector(g->_glyph));
+ }
+ // get the leftmost affine transform (leftmost defined with respect to the x axis of the first transform).
+ // That way the decoration will work no matter what mix of L->R, R->L text is in the span.
+ if(_nrstyle.text_decoration_line != TEXT_DECORATION_LINE_CLEAR){
+ Geom::Point pt =g->_ctm.translation() * rotinv;
+ if(pt[X] < leftmost){
+ leftmost = pt[X];
+ aff = g->_ctm;
+ phase_length = g->_pl;
+ }
+ /* If the text has been mapped onto a path, which causes y to vary, drop the text decorations.
+ To handle that properly would need a conformal map
+ */
+ if(firsty){
+ firsty = false;
+ starty = pt[Y];
+ }
+ else {
+ if(fabs(pt[Y] - starty) > 1.0e-6)decorate=false;
+ }
+ }
}
Inkscape::DrawingContext::Save save(ct);
- ct.transform(_ctm);
+// ct.transform(_ctm); // Seems to work fine without this line, which was in the original.
if (has_fill) {
_nrstyle.applyFill(ct);
ct.fillPreserve();
@@ -206,6 +451,29 @@ unsigned DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &/*are
ct.strokePreserve();
}
ct.newPath(); // clear path
+ if(_nrstyle.text_decoration_line != TEXT_DECORATION_LINE_CLEAR && decorate){
+ guint32 ergba;
+ if(_nrstyle.text_decoration_useColor){ // color different from the glyph
+ ergba = SP_RGBA32_F_COMPOSE(
+ _nrstyle.text_decoration_color.color.v.c[0],
+ _nrstyle.text_decoration_color.color.v.c[1],
+ _nrstyle.text_decoration_color.color.v.c[2],
+ 1.0);
+ }
+ else { // whatever the current fill color is
+ ergba = SP_RGBA32_F_COMPOSE(
+ _nrstyle.fill.color.v.c[0],
+ _nrstyle.fill.color.v.c[1],
+ _nrstyle.fill.color.v.c[2],
+ 1.0);
+ }
+ ct.setSource(ergba);
+ ct.setTolerance(0.5);
+ double thickness = decorateItem(ct, aff, phase_length);
+ ct.setLineWidth(thickness);
+ ct.strokePreserve();
+ ct.newPath(); // clear path
+ }
}
return RENDER_OK;
}
@@ -231,7 +499,9 @@ void DrawingText::_clipItem(DrawingContext &ct, Geom::IntRect const &/*area*/)
Inkscape::DrawingContext::Save save(ct);
ct.transform(g->_ctm);
- ct.path(*g->_font->PathVector(g->_glyph));
+ if(g->_drawable){
+ ct.path(*g->_font->PathVector(g->_glyph));
+ }
}
ct.fill();
}
diff --git a/src/display/drawing-text.h b/src/display/drawing-text.h
index 79b1b097c..99b46bc8a 100644
--- a/src/display/drawing-text.h
+++ b/src/display/drawing-text.h
@@ -35,8 +35,13 @@ protected:
virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags);
font_instance *_font;
- int _glyph;
- Geom::IntRect _pick_bbox;
+ int _glyph;
+ bool _drawable;
+ float _width; // These three are used to set up bounding box
+ float _asc; //
+ float _dsc; //
+ float _pl; // phase length
+ Geom::IntRect _pick_bbox;
friend class DrawingText;
};
@@ -49,9 +54,11 @@ public:
~DrawingText();
void clear();
- void addComponent(font_instance *font, int glyph, Geom::Affine const &trans);
+ bool addComponent(font_instance *font, int glyph, Geom::Affine const &trans,
+ float width, float ascent, float descent, float phase_length);
void setStyle(SPStyle *style);
+
protected:
virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx,
unsigned flags, unsigned reset);
@@ -61,6 +68,8 @@ protected:
virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags);
virtual bool _canClip();
+ double decorateItem(DrawingContext &ct, Geom::Affine aff, double phase_length);
+ void decorateStyle(DrawingContext &ct, double vextent, double xphase, Geom::Point p1, Geom::Point p2);
NRStyle _nrstyle;
friend class DrawingGlyphs;
diff --git a/src/display/nr-style.cpp b/src/display/nr-style.cpp
index 26d70ad15..a18bc0523 100644
--- a/src/display/nr-style.cpp
+++ b/src/display/nr-style.cpp
@@ -54,6 +54,20 @@ NRStyle::NRStyle()
, line_join(CAIRO_LINE_JOIN_MITER)
, fill_pattern(NULL)
, stroke_pattern(NULL)
+ , text_decoration_line(TEXT_DECORATION_LINE_CLEAR)
+ , text_decoration_style(TEXT_DECORATION_STYLE_CLEAR)
+ , phase_length(0.0)
+ , tspan_line_start(false)
+ , tspan_line_end(false)
+ , tspan_width(0)
+ , ascender(0)
+ , descender(0)
+ , line_gap(0)
+ , underline_thickness(0)
+ , underline_position(0)
+ , line_through_thickness(0)
+ , line_through_position(0)
+ , font_size(0)
{}
NRStyle::~NRStyle()
@@ -144,6 +158,50 @@ void NRStyle::set(SPStyle *style)
dash = NULL;
}
+ text_decoration_line = TEXT_DECORATION_LINE_CLEAR;
+ if(style->text_decoration_line.inherit ){ text_decoration_line |= TEXT_DECORATION_LINE_INHERIT; }
+ if(style->text_decoration_line.underline ){ text_decoration_line |= TEXT_DECORATION_LINE_UNDERLINE + TEXT_DECORATION_LINE_SET; }
+ if(style->text_decoration_line.overline ){ text_decoration_line |= TEXT_DECORATION_LINE_OVERLINE + TEXT_DECORATION_LINE_SET; }
+ if(style->text_decoration_line.line_through){ text_decoration_line |= TEXT_DECORATION_LINE_LINETHROUGH + TEXT_DECORATION_LINE_SET; }
+ if(style->text_decoration_line.blink ){ text_decoration_line |= TEXT_DECORATION_LINE_BLINK + TEXT_DECORATION_LINE_SET; }
+
+ text_decoration_style = TEXT_DECORATION_STYLE_CLEAR;
+ if(style->text_decoration_style.inherit ){ text_decoration_style |= TEXT_DECORATION_STYLE_INHERIT; }
+ if(style->text_decoration_style.solid ){ text_decoration_style |= TEXT_DECORATION_STYLE_SOLID + TEXT_DECORATION_STYLE_SET; }
+ if(style->text_decoration_style.isdouble ){ text_decoration_style |= TEXT_DECORATION_STYLE_ISDOUBLE + TEXT_DECORATION_STYLE_SET; }
+ if(style->text_decoration_style.dotted ){ text_decoration_style |= TEXT_DECORATION_STYLE_DOTTED + TEXT_DECORATION_STYLE_SET; }
+ if(style->text_decoration_style.dashed ){ text_decoration_style |= TEXT_DECORATION_STYLE_DASHED + TEXT_DECORATION_STYLE_SET; }
+ if(style->text_decoration_style.wavy ){ text_decoration_style |= TEXT_DECORATION_STYLE_WAVY + TEXT_DECORATION_STYLE_SET; }
+
+ if( style->text_decoration_color.set ||
+ style->text_decoration_color.inherit ||
+ style->text_decoration_color.currentcolor ||
+ style->text_decoration_color.colorSet){
+ text_decoration_color.set(style->text_decoration_color.value.color);
+ text_decoration_useColor = true;
+ }
+ else {
+ text_decoration_color.clear();
+ text_decoration_useColor = false;
+ }
+
+ if(text_decoration_line != TEXT_DECORATION_LINE_CLEAR){
+ phase_length = style->text_decoration_data.phase_length;
+ tspan_line_start = style->text_decoration_data.tspan_line_start;
+ tspan_line_end = style->text_decoration_data.tspan_line_end;
+ tspan_width = style->text_decoration_data.tspan_width;
+ ascender = style->text_decoration_data.ascender;
+ descender = style->text_decoration_data.descender;
+ line_gap = style->text_decoration_data.line_gap;
+ underline_thickness = style->text_decoration_data.underline_thickness;
+ underline_position = style->text_decoration_data.underline_position;
+ line_through_thickness = style->text_decoration_data.line_through_thickness;
+ line_through_position = style->text_decoration_data.line_through_position;
+ font_size = style->font_size.computed;
+ }
+
+ text_direction = style->direction.computed;
+
update();
}
diff --git a/src/display/nr-style.h b/src/display/nr-style.h
index cd0bd208f..df4c4f921 100644
--- a/src/display/nr-style.h
+++ b/src/display/nr-style.h
@@ -67,6 +67,43 @@ struct NRStyle {
cairo_pattern_t *fill_pattern;
cairo_pattern_t *stroke_pattern;
+
+#define TEXT_DECORATION_LINE_CLEAR 0x00
+#define TEXT_DECORATION_LINE_SET 0x01
+#define TEXT_DECORATION_LINE_INHERIT 0x02
+#define TEXT_DECORATION_LINE_UNDERLINE 0x04
+#define TEXT_DECORATION_LINE_OVERLINE 0x08
+#define TEXT_DECORATION_LINE_LINETHROUGH 0x10
+#define TEXT_DECORATION_LINE_BLINK 0x20
+
+#define TEXT_DECORATION_STYLE_CLEAR 0x00
+#define TEXT_DECORATION_STYLE_SET 0x01
+#define TEXT_DECORATION_STYLE_INHERIT 0x02
+#define TEXT_DECORATION_STYLE_SOLID 0x04
+#define TEXT_DECORATION_STYLE_ISDOUBLE 0x08
+#define TEXT_DECORATION_STYLE_DOTTED 0x10
+#define TEXT_DECORATION_STYLE_DASHED 0x20
+#define TEXT_DECORATION_STYLE_WAVY 0x40
+
+ int text_decoration_line;
+ int text_decoration_style;
+ Paint text_decoration_color;
+ bool text_decoration_useColor; // if false, use whatever the glyph color was
+ // These are the same as in style.h
+ float phase_length;
+ bool tspan_line_start;
+ bool tspan_line_end;
+ float tspan_width;
+ float ascender;
+ float descender;
+ float line_gap;
+ float underline_thickness;
+ float underline_position;
+ float line_through_thickness;
+ float line_through_position;
+ float font_size;
+
+ int text_direction;
};
#endif
diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp
index aaa817c61..c14393cc2 100644
--- a/src/extension/internal/emf-inout.cpp
+++ b/src/extension/internal/emf-inout.cpp
@@ -1400,8 +1400,10 @@ Emf::select_font(PEMF_CALLBACK_DATA d, int index)
pEmr->elfw.elfLogFont.lfWeight == U_FW_EXTRABOLD ? SP_CSS_FONT_WEIGHT_BOLDER :
U_FW_NORMAL;
d->dc[d->level].style.font_style.value = (pEmr->elfw.elfLogFont.lfItalic ? SP_CSS_FONT_STYLE_ITALIC : SP_CSS_FONT_STYLE_NORMAL);
- d->dc[d->level].style.text_decoration.underline = pEmr->elfw.elfLogFont.lfUnderline;
- d->dc[d->level].style.text_decoration.line_through = pEmr->elfw.elfLogFont.lfStrikeOut;
+ d->dc[d->level].style.text_decoration_line.underline = pEmr->elfw.elfLogFont.lfUnderline;
+ d->dc[d->level].style.text_decoration_line.line_through = pEmr->elfw.elfLogFont.lfStrikeOut;
+ d->dc[d->level].style.text_decoration_line.set = true;
+ d->dc[d->level].style.text_decoration_line.inherit = false;
// malformed EMF with empty filename may exist, ignore font change if encountered
char *ctmp = U_Utf16leToUtf8((uint16_t *) (pEmr->elfw.elfLogFont.lfFaceName), U_LF_FACESIZE, NULL);
if(ctmp){
@@ -2988,6 +2990,7 @@ std::cout << "BEFORE DRAW"
y1 = pEmr->emrtext.ptlReference.y;
cChars = 0;
}
+ uint32_t fOptions = pEmr->emrtext.fOptions;
if (d->dc[d->level].textAlign & U_TA_UPDATECP) {
x1 = d->dc[d->level].cur.x;
@@ -3078,6 +3081,10 @@ std::cout << "BEFORE DRAW"
case SP_CSS_FONT_WEIGHT_BOLDER: tsp.weight = FC_WEIGHT_EXTRABOLD ; break;
default: tsp.weight = FC_WEIGHT_NORMAL ; break;
}
+ // EMF only supports two types of text decoration
+ tsp.decoration = TXTDECOR_NONE;
+ if(d->dc[d->level].style.text_decoration_line.underline){ tsp.decoration |= TXTDECOR_UNDER; }
+ if(d->dc[d->level].style.text_decoration_line.line_through){ tsp.decoration |= TXTDECOR_STRIKE;}
// EMF textalignment is a bit strange: 0x6 is center, 0x2 is right, 0x0 is left, the value 0x4 is also drawn left
tsp.taln = ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_CENTER) ? ALICENTER :
@@ -3086,13 +3093,19 @@ std::cout << "BEFORE DRAW"
tsp.taln |= ((d->dc[d->level].textAlign & U_TA_BASEBIT) ? ALIBASE :
((d->dc[d->level].textAlign & U_TA_BOTTOM) ? ALIBOT :
ALITOP));
- tsp.ldir = (d->dc[d->level].textAlign & U_TA_RTLREADING ? LDIR_RL : LDIR_LR); // language direction
+
+ // language direction can be encoded two ways, U_TA_RTLREADING is preferred
+ if( (fOptions & U_ETO_RTLREADING) || (d->dc[d->level].textAlign & U_TA_RTLREADING) ){ tsp.ldir = LDIR_RL; }
+ else{ tsp.ldir = LDIR_LR; }
+
tsp.condensed = FC_WIDTH_NORMAL; // Not implemented well in libTERE (yet)
tsp.ori = d->dc[d->level].style.baseline_shift.value; // For now orientation is always the same as escapement
tsp.ori += 180.0 * current_rotation(d)/ M_PI; // radians to degrees
tsp.string = (uint8_t *) U_strdup(escaped_text); // this will be free'd much later at a trinfo_clear().
tsp.fs = d->dc[d->level].style.font_size.computed * 0.8; // Font size in points
- (void) trinfo_load_fontname(d->tri, (uint8_t *)d->dc[d->level].font_name, &tsp);
+ char *fontspec = TR_construct_fontspec(&tsp, d->dc[d->level].font_name);
+ tsp.fi_idx = ftinfo_load_fontname(d->tri->fti,fontspec);
+ free(fontspec);
// when font name includes narrow it may not be set to "condensed". Narrow fonts do not work well anyway though
// as the metrics from fontconfig may not match, or the font may not be present.
if(0<= TR_findcasesub(d->dc[d->level].font_name, (char *) "Narrow")){ tsp.co=1; }
@@ -3101,7 +3114,7 @@ std::cout << "BEFORE DRAW"
int status = trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ori is actually escapement
if(status==-1){ // change of escapement, emit what we have and reset
TR_layout_analyze(d->tri);
- TR_layout_2_svg(d->tri);
+ TR_layout_2_svg(d->tri);
ts << d->tri->out;
*(d->outsvg) += ts.str().c_str();
d->tri = trinfo_clear(d->tri);
diff --git a/src/extension/internal/emf-print.cpp b/src/extension/internal/emf-print.cpp
index 2a9f734d1..7440b5380 100644
--- a/src/extension/internal/emf-print.cpp
+++ b/src/extension/internal/emf-print.cpp
@@ -212,7 +212,7 @@ void PrintEmf::search_short_fflist(const char *fontname, double *f1, double *f2,
search_long_fflist(fontname, f1, f2, f3);
}
-void PrintEmf::smuggle_adxky_out(const char *string, uint32_t **adx, double *ky, int *ndx, float scale){
+void PrintEmf::smuggle_adxkyrtl_out(const char *string, uint32_t **adx, double *ky, int *rtl, int *ndx, float scale){
float fdx;
int i;
uint32_t *ladx;
@@ -233,6 +233,8 @@ void PrintEmf::smuggle_adxky_out(const char *string, uint32_t **adx, double *ky,
cptr++; // skip 2nd fake terminator
sscanf(cptr,"%7f",&fdx);
*ky=fdx;
+ cptr += 7; // advance over ky and its space
+ sscanf(cptr,"%07d",rtl);
}
/* convert an 0RGB color to EMF U_COLORREF.
@@ -313,6 +315,7 @@ unsigned int PrintEmf::begin (Inkscape::Extension::Print *mod, SPDocument *doc)
// initialize a few global variables
hbrush = hbrushOld = hpen = 0;
+ htextalignment = U_TA_BASELINE | U_TA_LEFT;
use_stroke = use_fill = simple_shape = usebk = false;
Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
@@ -418,33 +421,34 @@ unsigned int PrintEmf::begin (Inkscape::Extension::Print *mod, SPDocument *doc)
rec = U_EMRSETBKMODE_set(U_TRANSPARENT);
if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){
- g_error("Fatal programming error in PrintEmf::text at U_EMRSETBKMODE_set");
+ g_error("Fatal programming error in PrintEmf::begin at U_EMRSETBKMODE_set");
}
hpolyfillmode=U_WINDING;
rec = U_EMRSETPOLYFILLMODE_set(U_WINDING);
if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){
- g_error("Fatal programming error in PrintEmf::text at U_EMRSETPOLYFILLMODE_set");
+ g_error("Fatal programming error in PrintEmf::begin at U_EMRSETPOLYFILLMODE_set");
}
- // Text alignment: (Never changes)
+ // Text alignment: (only changed if RTL text is encountered )
// - (x,y) coordinates received by this filter are those of the point where the text
// actually starts, and already takes into account the text object's alignment;
// - for this reason, the EMF text alignment must always be TA_BASELINE|TA_LEFT.
+ htextalignment = U_TA_BASELINE | U_TA_LEFT;
rec = U_EMRSETTEXTALIGN_set(U_TA_BASELINE | U_TA_LEFT);
if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){
- g_error("Fatal programming error in PrintEmf::text at U_EMRSETTEXTALIGN_set");
+ g_error("Fatal programming error in PrintEmf::begin at U_EMRSETTEXTALIGN_set");
}
htextcolor_rgb[0] = htextcolor_rgb[1] = htextcolor_rgb[2] = 0.0;
rec = U_EMRSETTEXTCOLOR_set(U_RGB(0,0,0));
if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){
- g_error("Fatal programming error in PrintEmf::text at U_EMRSETTEXTCOLOR_set");
+ g_error("Fatal programming error in PrintEmf::begin at U_EMRSETTEXTCOLOR_set");
}
rec = U_EMRSETROP2_set(U_R2_COPYPEN);
if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){
- g_error("Fatal programming error in PrintEmf::text at U_EMRSETROP2_set");
+ g_error("Fatal programming error in PrintEmf::begin at U_EMRSETROP2_set");
}
/* miterlimit is set with eah pen, so no need to check for it changes as in WMF */
@@ -1415,8 +1419,8 @@ unsigned int PrintEmf::fill(
}
}
if (
- (style->stroke.noneSet || style->stroke_width.computed == 0.0) ||
- (style->stroke_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) ||
+ (style->stroke.isNone() || style->stroke.noneSet || style->stroke_width.computed == 0.0) ||
+ (style->stroke_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) ||
!all_closed
){
print_pathv(pathv, fill_transform); // do any fills. side effect: clears fill_pathv
@@ -1977,10 +1981,21 @@ unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *te
double ky;
// the dx array is smuggled in like: text<nul>w1 w2 w3 ...wn<nul><nul>, where the widths are floats 7 characters wide, including the space
- int ndx;
+ int ndx, rtl;
uint32_t *adx;
- smuggle_adxky_out(text, &adx, &ky, &ndx, PX2WORLD * std::min(tf.expansionX(),tf.expansionY())); // side effect: free() adx
+ smuggle_adxkyrtl_out(text, &adx, &ky, &rtl, &ndx, PX2WORLD * std::min(tf.expansionX(),tf.expansionY())); // side effect: free() adx
+ uint32_t textalignment;
+ if(rtl > 0){ textalignment = U_TA_BASELINE | U_TA_LEFT; }
+ else { textalignment = U_TA_BASELINE | U_TA_RIGHT | U_TA_RTLREADING; }
+ if(textalignment != htextalignment){
+ htextalignment = textalignment;
+ rec = U_EMRSETTEXTALIGN_set(textalignment);
+ if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){
+ g_error("Fatal programming error in PrintEmf::text at U_EMRSETTEXTALIGN_set");
+ }
+ }
+
char *text2 = strdup(text); // because U_Utf8ToUtf16le calls iconv which does not like a const char *
uint16_t *unicode_text = U_Utf8ToUtf16le( text2, 0, NULL );
free(text2);
@@ -2043,8 +2058,8 @@ unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *te
round(rot),
transweight(style->font_weight.computed),
(style->font_style.computed == SP_CSS_FONT_STYLE_ITALIC),
- style->text_decoration.underline,
- style->text_decoration.line_through,
+ style->text_decoration_line.underline,
+ style->text_decoration_line.line_through,
U_DEFAULT_CHARSET,
U_OUT_DEFAULT_PRECIS,
U_CLIP_DEFAULT_PRECIS,
@@ -2109,6 +2124,7 @@ unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *te
int32_t const xpos = (int32_t) round(p2[Geom::X]);
int32_t const ypos = (int32_t) round(p2[Geom::Y]);
+
// The number of characters in the string is a bit fuzzy. ndx, the number of entries in adx is
// the number of VISIBLE characters, since some may combine from the UTF (8 originally,
// now 16) encoding. Conversely strlen() or wchar16len() would give the absolute number of
@@ -2117,7 +2133,13 @@ unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *te
// This is currently being smuggled in from caller as part of text, works
// MUCH better than the fallback hack below
// uint32_t *adx = dx_set(textheight, U_FW_NORMAL, slen); // dx is needed, this makes one up
- char *rec2 = emrtext_set( (U_POINTL) {xpos, ypos}, ndx, 2, unicode_text, U_ETO_NONE, U_RCL_DEF, adx);
+ char *rec2;
+ if(rtl>0){
+ rec2 = emrtext_set( (U_POINTL) {xpos, ypos}, ndx, 2, unicode_text, U_ETO_NONE, U_RCL_DEF, adx);
+ }
+ else { // RTL text, U_TA_RTLREADING should be enough, but set this one too just in case
+ rec2 = emrtext_set( (U_POINTL) {xpos, ypos}, ndx, 2, unicode_text, U_ETO_RTLREADING, U_RCL_DEF, adx);
+ }
free(unicode_text);
free(adx);
rec = U_EMREXTTEXTOUTW_set(U_RCL_DEF,U_GM_COMPATIBLE,1.0,1.0,(PU_EMRTEXT)rec2);
diff --git a/src/extension/internal/emf-print.h b/src/extension/internal/emf-print.h
index 6cfe5e46d..d42ba8a58 100644
--- a/src/extension/internal/emf-print.h
+++ b/src/extension/internal/emf-print.h
@@ -38,6 +38,7 @@ class PrintEmf : public Inkscape::Extension::Implementation::Implementation
double _height;
U_RECTL rc;
+ uint32_t htextalignment;
uint32_t hbrush, hbrushOld, hpen, hpenOld;
uint32_t hpolyfillmode; // used to minimize redundant records that set this
float htextcolor_rgb[3]; // used to minimize redundant records that set this
@@ -93,7 +94,7 @@ protected:
static void read_system_fflist(void);
static void search_long_fflist(const char *fontname, double *f1, double *f2, double *f3);
static void search_short_fflist(const char *fontname, double *f1, double *f2, double *f3);
- static void smuggle_adxky_out(const char *string, uint32_t **adx, double *ky, int *ndx, float scale);
+ static void smuggle_adxkyrtl_out(const char *string, uint32_t **adx, double *ky, int *rtl, int *ndx, float scale);
U_COLORREF gethexcolor(uint32_t color);
uint32_t transweight(const unsigned int inkweight);
diff --git a/src/extension/internal/text_reassemble.c b/src/extension/internal/text_reassemble.c
index 2518795b6..d16c060de 100644
--- a/src/extension/internal/text_reassemble.c
+++ b/src/extension/internal/text_reassemble.c
@@ -67,8 +67,8 @@ Optional compiler switches for development:
File: text_reassemble.c
-Version: 0.0.6
-Date: 12-MAR-2013
+Version: 0.0.10
+Date: 06-MAY-2013
Author: David Mathog, Biology Division, Caltech
email: mathog@caltech.edu
Copyright: 2013 David Mathog and California Institute of Technology (Caltech)
@@ -80,6 +80,115 @@ extern "C" {
#include "text_reassemble.h"
#include "uemf_utf.h" /* For a couple of text functions. Exact copy from libUEMF. */
+#include <float.h>
+
+/* Code generated by make_ucd_mn_table.c using:
+ cat mnlist.txt | ./make_ucd_mn_table >generated.c
+*/
+#include <stdint.h>
+int is_mn_unicode(int test){
+#define MN_TEST_LIMIT 921600
+#define N_SPAGES 225
+#define N_CPAGES 192
+#define N_CPDATA 344
+#define C_PER_S 16
+ uint8_t superpages[N_SPAGES]={
+ 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x06,
+ 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0B};
+
+ uint8_t cpages[N_CPAGES]={
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x19, 0x00, 0x00,
+ 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00,
+ 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x24, 0x25, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27,
+ 0x00, 0x28, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ uint32_t cpage_data[N_CPDATA]={
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000F8, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFE0000, 0xBFFFFFFF, 0x000000B6, 0x00000000,
+ 0x07FF0000, 0x00000000, 0xFFFFF800, 0x00010000, 0x00000000, 0x00000000, 0x9FC00000, 0x00003D9F,
+ 0x00020000, 0xFFFF0000, 0x000007FF, 0x00000000, 0x00000000, 0x0001FFC0, 0x00000000, 0x000FF800,
+ 0xFBC00000, 0x00003EEF, 0x0E000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x7FFFFFF0,
+ 0x00000007, 0x14000000, 0x00FE21FE, 0x0000000C, 0x00000002, 0x10000000, 0x0000201E, 0x0000000C,
+ 0x00000006, 0x10000000, 0x00023986, 0x00230000, 0x00000006, 0x10000000, 0x000021BE, 0x0000000C,
+ 0x00000002, 0x90000000, 0x0040201E, 0x0000000C, 0x00000004, 0x00000000, 0x00002001, 0x00000000,
+ 0x00000000, 0xC0000000, 0x00603DC1, 0x0000000C, 0x00000000, 0x90000000, 0x00003040, 0x0000000C,
+ 0x00000000, 0x00000000, 0x0000201E, 0x0000000C, 0x00000000, 0x00000000, 0x005C0400, 0x00000000,
+ 0x00000000, 0x07F20000, 0x00007F80, 0x00000000, 0x00000000, 0x1BF20000, 0x00003F00, 0x00000000,
+ 0x03000000, 0x02A00000, 0x00000000, 0x7FFE0000, 0xFEFFE0DF, 0x1FFFFFFF, 0x00000040, 0x00000000,
+ 0x00000000, 0x66FDE000, 0xC3000000, 0x001E0001, 0x20002064, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0xE0000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x001C0000, 0x001C0000, 0x000C0000, 0x000C0000, 0x00000000, 0x3FB00000, 0x200FFE40, 0x00000000,
+ 0x00003800, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000200, 0x00000000, 0x00000000,
+ 0x00000000, 0x0E040187, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x01800000, 0x00000000, 0x7F400000, 0x9FF81FE5, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x0000000F, 0x17D00000, 0x00000004, 0x000FF800, 0x00000003, 0x00000B3C, 0x00000000, 0x0003A340,
+ 0x00000000, 0x00CFF000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFF70000, 0x001021FD,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xF000007F,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1FFF0000, 0x0001FFE2,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00038000,
+ 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF,
+ 0x00000000, 0x00003C00, 0x00000000, 0x00000000, 0x06000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x3FF08000, 0x80000000, 0x00000000, 0x00000000, 0x00030000,
+ 0x00000844, 0x00000060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000010, 0x0003FFFF,
+ 0x00000000, 0x00003FC0, 0x0003FF80, 0x00000000, 0x00000007, 0x13C80000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00667E00, 0x00001008, 0x00000000, 0x00000000, 0xC19D0000, 0x00000002, 0x00403000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00002120,
+ 0x40000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x0000FFFF, 0x0000007F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x20000000,
+ 0x0000F06E, 0x87000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000002, 0xFF000000, 0x0000007F, 0x00000000, 0x00000003, 0x06780000, 0x00000000, 0x00000000,
+ 0x00000007, 0x001FEF80, 0x00000000, 0x00000000, 0x00000003, 0x7FC00000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00BF2800, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00078000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0xF8000380, 0x00000FE7, 0x00003C00, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x0000001C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF};
+
+ int result=0;
+
+ int spage_idx;
+ int cpage_row, cpage_column, cpage_idx;
+ int cd_row, cd_column, cd_idx, cd_bit;
+
+ if(test<MN_TEST_LIMIT){
+ spage_idx = test >> 12;
+ cpage_row = superpages[spage_idx];
+ cpage_column = (test>>8) & 15;
+ cpage_idx = C_PER_S*cpage_row + cpage_column;
+ cd_row = cpages[cpage_idx];
+ cd_column = test>>5 & 7;
+ cd_idx = 8*cd_row + cd_column;
+ cd_bit = test & 31;
+ result = cpage_data[cd_idx] & (1 << cd_bit);
+ }
+ return(result);
+}
+
+
/**
\brief Find a (sub)string in a caseinvariant manner, used for locating "Narrow" in font name
@@ -87,7 +196,7 @@ extern "C" {
\param string Text to search
\param sub Text to find
*/
-int TR_findcasesub(char *string, char *sub){
+int TR_findcasesub(const char *string, const char *sub){
int i,j;
int match=0;
for(i=0; string[i]; i++){
@@ -103,9 +212,117 @@ int TR_findcasesub(char *string, char *sub){
}
/**
+ \brief Constrouct a fontspec from a TCHUNK_SPECS and a fontname
+ \return Returns NULL on error, new fontspec on success
+ \param tsp pointer to TCHUNK_SPECS to use for information
+ \param fontname Fontname to use in the new fontspec
+*/
+ /* construct a font name */
+char *TR_construct_fontspec(const TCHUNK_SPECS *tsp, const char *fontname){
+ int newlen = 128 + strlen(fontname); /* too big, but not by much */
+ char *newfs = NULL;
+ newfs = (char *) malloc(newlen);
+ sprintf(newfs,"%s:slant=%d:weight=%d:size=%lf:width=%d",fontname,tsp->italics,tsp->weight,tsp->fs,(tsp->co ? 75 : tsp->condensed));
+ return(newfs);
+}
+
+
+/**
+ \brief Reconstrouct a fontspec by substituting a font name into an existing spec
+ \return Returns NULL on error, new fontspec on success
+ \param fontspec Original fontspec, only the name will be changed
+ \param fontname Fontname to substitute into the new fontspec
+*/
+ /* construct a font name */
+char *TR_reconstruct_fontspec(const char *fontspec, const char *fontname){
+ int colon;
+ int newlen = strlen(fontspec) + strlen(fontname) + 1; /* too big, but not by much */
+ char *newfs = NULL;
+ newfs = (char *) malloc(newlen);
+ colon = strcspn(fontspec,":");
+ if(colon){ sprintf(newfs,"%s%s",fontname,&fontspec[colon]); }
+ return(newfs);
+}
+
+/**
+ \brief Find a font in the list that has a glyph for this character, change alternate to match
+ \return Returns 0 if no match or an error, else returns the glyph index in the new alternate font
+ \param fti pointer to the FT_INFO structure, may be modified if alternate font is added
+ \param efsp Pointer to a Pointer to the original FNT_SPECS struct. On return contains the FNT_SPECS corresponding to the glyph_index..
+ \param wc Current character (32 bit int)
+*/
+int TR_find_alternate_font(FT_INFO *fti, FNT_SPECS **efsp, uint32_t wc){
+ int glyph_index=0; /* this is the unknown character glyph */
+ uint32_t i;
+ FcCharSet *cs;
+ FcResult result = FcResultMatch;
+ FcPattern *pattern, *fpat;
+ char *filename;
+ char *fontname;
+ char *newfontspec;
+ int fi_idx;
+ FNT_SPECS *fsp,*fsp2;
+ if(!fti || !efsp || !*efsp)return(0);
+ fsp = *efsp;
+ for(i=0;i<fsp->used;i++){ /* first check in alts */
+ fsp2 = &fti->fonts[fsp->alts[i].fi_idx]; /* these are in order of descending previous usage */
+ glyph_index = FT_Get_Char_Index( fsp2->face, wc); /* we have the face, might as well check that directly */
+ if (glyph_index){ /* found a glyph for the character in this font */
+ (void) fsp_alts_weight(fsp, i);
+ *efsp = fsp2;
+ return(glyph_index);
+ }
+ }
+
+ /* it was not in alts, now go through fontset and see if it is in there */
+ for(i=1; i< (unsigned int) fsp->fontset->nfont;i++){ /* already know the primary does not have this character */
+ result = FcPatternGetCharSet(fsp->fontset->fonts[i], FC_CHARSET, 0, &cs);
+ if(result != FcResultMatch) return(0); /* some terrible problem, this should never happen */
+ if (FcCharSetHasChar(cs, wc)){ /* found a glyph for the character in this font */
+ glyph_index = i;
+
+ /* Do a lot of work to find the filename corresponding to the fontset entry.
+ None of these should ever fail, but if one does, return 0
+ */
+ if(
+ !(pattern = FcNameParse((const FcChar8 *)&(fsp->fontspec))) ||
+ !FcConfigSubstitute(NULL, pattern, FcMatchPattern)
+ )return(0);
+ FcDefaultSubstitute(pattern);
+ if(
+ !(fpat = FcFontRenderPrepare(NULL, pattern, fsp->fontset->fonts[i])) ||
+ (FcPatternGetString( fpat, FC_FILE, 0, (FcChar8 **)&filename) != FcResultMatch) ||
+ (FcPatternGetString( fsp->fontset->fonts[i], FC_FULLNAME, 0, (FcChar8 **)&fontname) != FcResultMatch)
+ )return(0);
+
+ /* find the font (added from an unrelated fontset, for instance) or insert it as new */
+ fi_idx = ftinfo_find_loaded_by_src(fti, (uint8_t *) filename);
+ if(fi_idx < 0){
+ newfontspec = TR_reconstruct_fontspec((char *) fsp->fontspec, fontname);
+ fi_idx = ftinfo_load_fontname(fti, newfontspec);
+ free(newfontspec);
+ if(fi_idx < 0)return(0); /* This could happen if we run out of memory*/
+ }
+
+ /* add the new font index to the alts list on the (current) fsp. */
+ (void) fsp_alts_insert(fsp, fi_idx);
+
+ /* release FC's own memory related to this call that does not need to be kept around so that face will work */
+ FcPatternDestroy(pattern);
+
+ *efsp = &(fti->fonts[fi_idx]);
+ return(glyph_index);
+ }
+ }
+
+ return(0);
+}
+
+/**
\brief Get the advance for the 32 bit character
\return Returns -1 on error, or advance in units of 1/64th of a Point.
+ \param fti pointer to the FT_INFO structure, may be modified if alternate font is required
\param fsp Pointer to FNT_SPECS struct.
\param wc Current character (32 bit int)
\param pc Previous character
@@ -117,23 +334,31 @@ int TR_findcasesub(char *string, char *sub){
\param ymin If the pointer is defined, the value is adjusted if ymin of wc character is less than the current value.
\param ymax If the pointer is defined, the value is adjusted if ymin of wc character is more than the current value.
*/
-int TR_getadvance(FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int load_flags, int kern_mode, int *ymin, int *ymax){
+int TR_getadvance(FT_INFO *fti, FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int load_flags, int kern_mode, int *ymin, int *ymax){
FT_Glyph glyph;
int glyph_index;
int advance=-1;
FT_BBox bbox;
+ if(is_mn_unicode(wc))return(0); /* no advance on Unicode Mn characters */
+
glyph_index = FT_Get_Char_Index( fsp->face, wc);
- if (!FT_Load_Glyph( fsp->face, glyph_index, load_flags )){
- if ( !FT_Get_Glyph( fsp->face->glyph, &glyph ) ) {
- advance = fsp->face->glyph->advance.x;
- FT_Glyph_Get_CBox( glyph, FT_GLYPH_BBOX_UNSCALED, &bbox );
- if(ymin && (bbox.yMin < *ymin))*ymin=bbox.yMin;
- if(ymax && (bbox.yMax > *ymax))*ymax=bbox.yMax;
- if(pc)advance += TR_getkern2(fsp, wc, pc, kern_mode);
- FT_Done_Glyph(glyph);
+ if(!glyph_index){ /* not in primary font, check alternates */
+ glyph_index = TR_find_alternate_font(fti, &fsp, wc);
+ }
+ if(glyph_index){
+ if (!FT_Load_Glyph( fsp->face, glyph_index, load_flags )){
+ if ( !FT_Get_Glyph( fsp->face->glyph, &glyph ) ) {
+ advance = fsp->face->glyph->advance.x;
+ FT_Glyph_Get_CBox( glyph, FT_GLYPH_BBOX_UNSCALED, &bbox );
+ if(ymin && (bbox.yMin < *ymin))*ymin=bbox.yMin;
+ if(ymax && (bbox.yMax > *ymax))*ymax=bbox.yMax;
+ if(pc)advance += TR_getkern2(fsp, wc, pc, kern_mode);
+ FT_Done_Glyph(glyph);
+ }
}
}
+ /* If there was no way to determine the width, this returns the error value */
return(advance);
}
@@ -164,7 +389,7 @@ int TR_getkern2(FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int kern_mode){
}
/**
- \brief Get the kerning for a pair of 32 bit characters, where one is the last charcter in the previous text block, and the other is the first in the current text block.
+ \brief Get the kerning for a pair of 32 bit characters, where one is the last character in the previous text block, and the other is the first in the current text block.
\return Returns 0 on error, or kerning value (which may be 0) for the pair in units of 1/64th of a point.
\param fsp Pointer to FNT_SPECS struct.
\param tsp current text object
@@ -214,17 +439,16 @@ int TR_kern_gap(FNT_SPECS *fsp, TCHUNK_SPECS *tsp, TCHUNK_SPECS *ptsp, int kern_
*/
double TR_baseline(TR_INFO *tri, int src, double *ymax, double *ymin){
double baseline=0;
- double tmp;
+ volatile double tmp=0.0; /* This MUST be volatile */
double yheight;
int last;
int i;
int trec;
- FNT_SPECS *fsp;
- static int depth=0;
CX_INFO *cxi=tri->cxi;
BR_INFO *bri=tri->bri;
TP_INFO *tpi=tri->tpi;
FT_INFO *fti=tri->fti;
+ FNT_SPECS *fsp;
last = cxi->cx[src].kids.used - 1;
switch (cxi->cx[src].type){
case TR_TEXT:
@@ -242,19 +466,24 @@ double TR_baseline(TR_INFO *tri, int src, double *ymax, double *ymin){
}
break;
case TR_LINE:
- for(i=last;i>=0;i--){ /* here last is the count of */
+ for(i=last;i>=0;i--){ /* here last is the count of text objects in the complex */
trec = cxi->cx[src].kids.members[i];
fsp = &(fti->fonts[tpi->chunks[trec].fi_idx]);
yheight = fsp->face->bbox.yMax - fsp->face->bbox.yMin;
if(ymax){
- tmp = tpi->chunks[trec].fs * ((double)fsp->face->bbox.yMax/yheight);
+ tmp = tpi->chunks[trec].fs * (((double)fsp->face->bbox.yMax)/yheight);
+ /* gcc 4.6.3 had a bizarre optimization error for -O2 and -O3 where *ymax <= tmp was
+ not true when *ymax == tmp, as verified by examining the binary representations.
+ This was apparently due to retained excess precision. Making tmp volatile
+ forces it to be stored into a 64 bit location, dropping the extra 12 bits from
+ the 80 bit register. */
if(*ymax <= tmp){
*ymax = tmp;
baseline = bri->rects[trec].yll - tpi->chunks[trec].boff;
}
}
else if(ymin){
- tmp = tpi->chunks[trec].fs * ((double)-fsp->face->bbox.yMin/yheight); /* yMin in face is negative */
+ tmp = tpi->chunks[trec].fs * (((double)-fsp->face->bbox.yMin)/yheight); /* yMin in face is negative */
if(*ymin <= tmp){
*ymin = tmp;
baseline = bri->rects[trec].yll - tpi->chunks[trec].boff;
@@ -266,7 +495,6 @@ double TR_baseline(TR_INFO *tri, int src, double *ymax, double *ymin){
case TR_PARA_LJ:
case TR_PARA_CJ:
case TR_PARA_RJ:
- depth++;
trec = cxi->cx[src].kids.members[last];
baseline = TR_baseline(tri, trec, ymax, ymin);
break;
@@ -301,8 +529,8 @@ int TR_check_set_vadvance(TR_INFO *tri, int src, int lines){
int status = 0;
CX_INFO *cxi = tri->cxi;
TP_INFO *tpi = tri->tpi;
- double ymax = 0.0;
- double ymin = 0.0;
+ double ymax = DBL_MIN;
+ double ymin = DBL_MIN;
double prevbase;
double thisbase;
double weight;
@@ -374,10 +602,7 @@ FT_INFO *ftinfo_init(void){
int ftinfo_make_insertable(FT_INFO *fti){
int status=0;
if(!fti)return(2);
- if(fti->used < fti->space){
- /* already insertable */
- }
- else {
+ if(fti->used >= fti->space){
fti->space += ALLOCINFO_CHUNK;
if((fti->fonts = (FNT_SPECS *) realloc(fti->fonts, fti->space * sizeof(FNT_SPECS) ))){
memset(&fti->fonts[fti->used],0,(fti->space - fti->used)*sizeof(FNT_SPECS));
@@ -416,22 +641,12 @@ int ftinfo_insert(FT_INFO *fti, FNT_SPECS *fsp){
\returns NULL.
*/
FT_INFO *ftinfo_release(FT_INFO *fti){
- int i;
- if(fti){
- for(i=0;i<fti->used;i++){
- FT_Done_Face(fti->fonts[i].face); /* release memory for face controlled by FreeType */
- free(fti->fonts[i].file); /* release memory holding copies of paths */
- free(fti->fonts[i].fname); /* release memory holding copies of font names */
- FcPatternDestroy(fti->fonts[i].fpat); /* release memory for FontConfit fpats */
- }
- free(fti->fonts);
- FT_Done_FreeType(fti->library); /* release all other FreeType memory */
- free(fti);
- FcFini(); /* shut down FontConfig, release memory, patterns must have already been released or boom! */
- }
+ (void) ftinfo_clear(fti);
+ FcFini(); /* shut down FontConfig, release memory, patterns must have already been released or boom! */
return NULL;
}
+
/**
\brief Clear an FT_INFO structure. Release all Freetype memory but does not release Fontconfig.
This would be called in preference to ftinfo_release() if some other part of the program needed
@@ -441,13 +656,17 @@ FT_INFO *ftinfo_release(FT_INFO *fti){
\returns NULL.
*/
FT_INFO *ftinfo_clear(FT_INFO *fti){
- int i;
+ uint32_t i;
+ FNT_SPECS *fsp;
if(fti){
for(i=0;i<fti->used;i++){
- FT_Done_Face(fti->fonts[i].face); /* release memory for face controlled by FreeType */
- free(fti->fonts[i].file); /* release memory holding copies of paths */
- free(fti->fonts[i].fname); /* release memory holding copies of font names */
- FcPatternDestroy(fti->fonts[i].fpat); /* release memory for FontConfit fpats */
+ fsp = &(fti->fonts[i]);
+ FT_Done_Face(fsp->face); /* release memory for face controlled by FreeType */
+ free(fsp->file); /* release memory holding copies of paths */
+ free(fsp->fontspec); /* release memory holding copies of font names */
+ FcPatternDestroy(fsp->fpat); /* release memory for FontConfig fpats */
+ FcFontSetDestroy(fti->fonts[i].fontset);
+ if(fsp->alts){ free(fsp->alts); }
}
free(fti->fonts);
FT_Done_FreeType(fti->library); /* release all other FreeType memory */
@@ -458,6 +677,227 @@ FT_INFO *ftinfo_clear(FT_INFO *fti){
/**
+ \brief Find the loaded font matching fontspec
+ \returns index of font on success, -1 if not found
+ \param tri pointer to the TR_INFO structure.
+ \param fontspec UTF-8 description of the font, as constructed in trinfo_load_fontname
+*/
+
+int ftinfo_find_loaded_by_spec(const FT_INFO *fti, const uint8_t *fontspec){
+ uint32_t i;
+ int status = -1;
+ /* If it is already loaded, do not load it again */
+ for(i=0;i<fti->used;i++){
+ if(0==strcmp((char *) fti->fonts[i].fontspec, (char *)fontspec)){
+ status=i;
+ break;
+ }
+ }
+ return(status);
+}
+
+/**
+ \brief Find the loaded font matching the source file
+ \returns index of font on success, -1 if not found
+ \param tri pointer to the TR_INFO structure.
+ \param filename UTF-8 file name for the font
+*/
+
+int ftinfo_find_loaded_by_src(const FT_INFO *fti, const uint8_t *filename){
+ uint32_t i;
+ int status = -1;
+ /* If it is already loaded, do not load it again */
+ for(i=0;i<fti->used;i++){
+ if(0==strcmp((char *) fti->fonts[i].file, (char *) filename)){
+ status=i;
+ break;
+ }
+ }
+ return(status);
+}
+
+
+/**
+ \brief Load a (new) font by name into a TR_INFO structure or find it if it is already loaded
+ \returns fi_idx of inserted (or found) font on success, <0 on error.
+ \param fti pointer to the FT_INFO structure.
+ \param fontname UTF-8 font name
+ \param fontspec UTF-8 font specification used for query string.
+*/
+
+int ftinfo_load_fontname(FT_INFO *fti, const char *fontspec){
+ FcPattern *pattern, *fpat;
+ FcFontSet *fontset;
+ FcResult result = FcResultMatch;
+ char *filename;
+ double fd;
+ FNT_SPECS *fsp;
+ int status;
+ int fi_idx;
+
+ if(!fti)return(-1);
+
+ /* If it is already loaded, do not load it again */
+ if((status = ftinfo_find_loaded_by_spec(fti, (uint8_t *) fontspec))>=0){
+ return(status);
+ }
+
+ ftinfo_make_insertable(fti);
+ fi_idx = fti->used;
+
+ if(!(pattern = FcNameParse((const FcChar8 *)fontspec)) )return(2);
+ if(!FcConfigSubstitute(NULL, pattern, FcMatchPattern) )return(3);
+ FcDefaultSubstitute(pattern);
+ /* get a fontset, trimmed to only those with new glyphs as needed, so that missing glyph's may be handled */
+ if(!(fontset = FcFontSort (NULL,pattern, FcTrue, NULL, &result))
+ || result != FcResultMatch)return(4);
+ if(!(fpat = FcFontRenderPrepare(NULL, pattern, fontset->fonts[0])) )return(405);
+ if(FcPatternGetString( fpat, FC_FILE, 0, (FcChar8 **)&filename) != FcResultMatch)return(5);
+ if(FcPatternGetDouble( fpat, FC_SIZE, 0, &fd) != FcResultMatch)return(6);
+
+ /* copy these into memory for external use */
+ fsp = &(fti->fonts[fti->used]);
+ fsp->fontset = fontset;
+ fsp->alts = NULL; /* Initially no links to alternate fonts */
+ fsp->space = 0;
+ fsp->file = (uint8_t *) U_strdup((char *) filename);
+ fsp->fontspec = (uint8_t *) U_strdup((char *) fontspec);
+ fsp->fpat = fpat;
+ fsp->fsize = fd;
+
+ /* release FC's own memory related to this call that does not need to be kept around so that face will work */
+ FcPatternDestroy(pattern);
+
+ /* get the current face */
+ if(FT_New_Face( fti->library, (const char *) fsp->file, 0, &(fsp->face) )){ return(8); }
+
+ if(FT_Set_Char_Size(
+ fsp->face, /* handle to face object */
+ 0, /* char_width in 1/64th of points */
+ fd*64, /* char_height in 1/64th of points */
+ 72, /* horizontal device resolution, DPI */
+ 72) /* vebrical device resolution, DPI */
+ ){ return(9); }
+
+ /* The space advance is needed in various places. Get it now, and get it in the font units,
+ so that it can be scaled later with the text size */
+ status = TR_getadvance(fti, fsp,' ',0,FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP, FT_KERNING_UNSCALED, NULL, NULL);
+ if(status < 0)return(7);
+ fsp->spcadv = ((double) status)/(64.0);
+
+ fti->used++;
+
+/*
+ char *fs;
+ int fb;
+ if(FcPatternGetBool( fpat, FC_OUTLINE, 0, &fb)== FcResultMatch){ printf("outline: %d\n",fb);fflush(stdout); }
+ if(FcPatternGetBool( fpat, FC_SCALABLE, 0, &fb)== FcResultMatch){ printf("scalable: %d\n",fb);fflush(stdout); }
+ if(FcPatternGetDouble( fpat, FC_DPI, 0, &fd)== FcResultMatch){ printf("DPI: %lf\n",fd);fflush(stdout); }
+ if(FcPatternGetInteger( fpat, FC_FONTVERSION, 0, &fb)== FcResultMatch){ printf("fontversion: %d\n",fb);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_FULLNAME , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FULLNAME : %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_FAMILY , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FAMILY : %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_STYLE , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("STYLE : %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_FOUNDRY , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FOUNDRY : %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_FAMILYLANG , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FAMILYLANG : %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_STYLELANG , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("STYLELANG : %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_FULLNAMELANG, 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FULLNAMELANG: %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_CAPABILITY , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("CAPABILITY : %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_FONTFORMAT , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FONTFORMAT : %s\n",fs);fflush(stdout); }
+*/
+
+ return(fi_idx);
+}
+
+/**
+ \brief Dump the contents of the TR_INFO structure to stdout. For debugging purposes,not used in production code.
+ \param tri pointer to the TR_INFO structure.
+*/
+void ftinfo_dump(const FT_INFO *fti){
+ uint32_t i,j;
+ FNT_SPECS *fsp;
+ printf("fti space: %d\n",fti->space);
+ printf("fti used: %d\n",fti->used);
+ for(i=0; i< fti->used; i++){
+ fsp = &(fti->fonts[i]);
+ printf("fti font: %6d space: %6d used: %6d spcadv %8lf fsize %8lf \n",i,fsp->space,fsp->used,fsp->spcadv,fsp->fsize);
+ printf(" file: %s\n",fsp->file);
+ printf(" fspc: %s\n",fsp->fontspec);
+ for(j=0;j<fsp->used;j++){
+ printf(" alts: %6d fi_idx: %6d wgt: %6d\n",j,fsp->alts[j].fi_idx,fsp->alts[j].weight);
+ }
+ }
+
+}
+
+/**
+ \brief Make the FNT_SPECS alts structure insertable. Adds storage as needed.
+ \param fti pointer to the FT_INFO structure
+ \returns 0 on success, !0 on error.
+*/
+int fsp_alts_make_insertable(FNT_SPECS *fsp){
+ int status=0;
+ if(!fsp)return(2);
+ if(fsp->used >= fsp->space){
+ fsp->space += ALLOCINFO_CHUNK;
+ if((fsp->alts = (ALT_SPECS *) realloc(fsp->alts, fsp->space * sizeof(ALT_SPECS) ))){
+ memset(&fsp->alts[fsp->used],0,(fsp->space - fsp->used)*sizeof(ALT_SPECS));
+ }
+ else {
+ status=1;
+ }
+ }
+ return(status);
+}
+
+
+/**
+ \brief Insert a new ALT_SPECS into the FNT_SPECS alts list.
+ \param fsp pointer to the FNT_SPECS structure.
+ \param fi_idx font index to add to the alts list
+ \returns 0 on success, !0 on error.
+*/
+int fsp_alts_insert(FNT_SPECS *fsp, uint32_t fi_idx){
+ int status=1;
+ ALT_SPECS alt;
+ if(!fsp)return(3);
+ alt.fi_idx = fi_idx;
+ alt.weight = 1; /* new ones start with this weight, it can only go up */
+ if(!(status = fsp_alts_make_insertable(fsp))){
+ fsp->alts[fsp->used] = alt;
+ fsp->used++;
+ }
+ return(status);
+}
+
+/**
+ \brief Increment the weight of an alts entry by 1, readjust order if necessary
+ \param fsp pointer to the FNT_SPECS structure.
+ \param idx index of the alts entry to increment
+ \returns 0 on success, !0 on error.
+*/
+int fsp_alts_weight(FNT_SPECS *fsp, uint32_t a_idx){
+ uint32_t i;
+ ALT_SPECS alt;
+ if(!fsp)return(1);
+ if(!fsp->used)return(2);
+ if(a_idx >= fsp->used)return(3);
+ /* If a counter hits the limit divide all counts in half. */
+ if(fsp->alts[a_idx].weight == UINT32_MAX){
+ for(i=0; i<fsp->used; i++){ fsp->alts[i].weight /= 2; }
+ }
+ fsp->alts[a_idx].weight++;
+ for(i=a_idx; i>0; i--){
+ if(fsp->alts[i-1].weight >= fsp->alts[a_idx].weight)break;
+ alt = fsp->alts[i-1];
+ fsp->alts[i-1] = fsp->alts[a_idx];
+ fsp->alts[a_idx] = alt;
+ }
+ return(0);
+}
+
+
+
+/**
\brief Make a CHILD_SPECS structure insertable. Adds storage as needed.
\param csp pointer to the CHILD_SPECS structure
\returns 0 on success, !0 on error.
@@ -465,10 +905,7 @@ FT_INFO *ftinfo_clear(FT_INFO *fti){
int csp_make_insertable(CHILD_SPECS *csp){
int status=0;
if(!csp)return(2);
- if(csp->used < csp->space){
- /* already insertable */
- }
- else {
+ if(csp->used >= csp->space){
csp->space += ALLOCINFO_CHUNK;
if((csp->members = (int *) realloc(csp->members, csp->space * sizeof(int) ))){
memset(&csp->members[csp->used],0,(csp->space - csp->used)*sizeof(int));
@@ -505,8 +942,8 @@ int csp_insert(CHILD_SPECS *dst, int src){
\returns 0 on success, !0 on error.
*/
int csp_merge(CHILD_SPECS *dst, CHILD_SPECS *src){
- int i;
- int status=1;
+ uint32_t i;
+ int status=1;
if(!dst)return(2);
if(!src)return(3);
for(i=0;i<src->used;i++){
@@ -529,6 +966,15 @@ void csp_release(CHILD_SPECS *csp){
}
}
+/**
+ \brief Clear a CHILD_SPECS structure, making all allocated slots usable. Does not release associated memory.
+ \param csp pointer to the CHILD_SPECS structure.
+ \returns NULL.
+*/
+void csp_clear(CHILD_SPECS *csp){
+ csp->used = 0;
+}
+
/**
\brief Initialize an CX_INFO structure. Holds complexes (multiple text objects in known positions and order.)
@@ -553,10 +999,7 @@ CX_INFO *cxinfo_init(void){
*/
int cxinfo_make_insertable(CX_INFO *cxi){
int status=0;
- if(cxi->used < cxi->space){
- /* already insertable */
- }
- else {
+ if(cxi->used >= cxi->space){
cxi->space += ALLOCINFO_CHUNK;
if((cxi->cx = (CX_SPECS *) realloc(cxi->cx, cxi->space * sizeof(CX_SPECS) ))){
memset(&cxi->cx[cxi->used],0,(cxi->space - cxi->used)*sizeof(CX_SPECS));
@@ -617,19 +1060,37 @@ int cxinfo_append(CX_INFO *cxi, int src, enum tr_classes type){
int cxinfo_merge(CX_INFO *cxi, int dst, int src, enum tr_classes type){
int status =1;
if(!cxi)return(2);
- if(dst < 0 || dst >= cxi->used)return(3);
- if(src < 0)return(4);
+ if(!cxi->used)return(3);
+ if(dst < 0 || dst >= (int) cxi->used)return(4);
+ if(src < 0)return(5);
cxi->cx[dst].type = type;
status = csp_merge(&(cxi->cx[dst].kids), &(cxi->cx[src].kids));
return(status);
}
/**
+ \brief Trim the last complex from thelist of complexes.
+ \param cxi pointer to the CX_INFO structure (complexes).
+ \returns 0 on success, !0 on error.
+*/
+int cxinfo_trim(CX_INFO *cxi){
+ int status = 0;
+ int last ;
+ if(!cxi)return(1);
+ if(!cxi->used)return(2);
+ last = cxi->used - 1;
+ csp_clear(&(cxi->cx[last].kids));
+ cxi->used--;
+ return(status);
+}
+
+
+/**
\brief Dump the contents of the TR_INFO structure to stdout. For debugging purposes,not used in production code.
\param tri pointer to the TR_INFO structure.
*/
-void cxinfo_dump(TR_INFO *tri){
- int i,j,k;
+void cxinfo_dump(const TR_INFO *tri){
+ uint32_t i,j,k;
CX_INFO *cxi = tri->cxi;
BR_INFO *bri = tri->bri;
TP_INFO *tpi = tri->tpi;
@@ -641,17 +1102,23 @@ void cxinfo_dump(TR_INFO *tri){
printf("cxi phase1: %d\n",cxi->phase1);
printf("cxi lines: %d\n",cxi->lines);
printf("cxi paras: %d\n",cxi->paras);
+ printf("cxi xy: %lf , %lf\n",tri->x,tri->y);
for(i=0;i<cxi->used;i++){
csp = &(cxi->cx[i]);
bsp = &(bri->rects[csp->rt_cidx]);
- printf("cxi cx[%d] type:%d rt_tidx:%d kids_used:%d kids:space:%d\n",i, csp->type, csp->rt_cidx, csp->kids.used, csp->kids.space);
+ printf("cxi cx[%d] type:%d rt_tidx:%d kids_used:%d kids_space:%d\n",i, csp->type, csp->rt_cidx, csp->kids.used, csp->kids.space);
printf("cxi cx[%d] br (LL,UR) (%lf,%lf),(%lf,%lf)\n",i,bsp->xll,bsp->yll,bsp->xur,bsp->yur);
for(j=0;j<csp->kids.used;j++){
k = csp->kids.members[j];
bsp = &(bri->rects[k]);
if(csp->type == TR_TEXT || csp->type == TR_LINE){
- printf("cxi cx[%d] member:%d tp_idx:%d rt_tidx:%d br (LL,UR) (%8.3lf,%8.3lf),(%8.3lf,%8.3lf) text:<%s>\n",i, j, k, tpi->chunks[k].rt_tidx, bsp->xll,bsp->yll,bsp->xur,bsp->yur, tpi->chunks[k].string);
+ printf("cxi cx[%d] member:%3d tp_idx:%3d ldir:%d rt_tidx:%3d br (LL,UR) (%8.3lf,%8.3lf),(%8.3lf,%8.3lf) xy (%8.3lf,%8.3lf) kern (%8.3lf,%8.3lf) text:<%s> decor:%5.5x\n",
+ i, j, k, tpi->chunks[k].ldir, tpi->chunks[k].rt_tidx,
+ bsp->xll,bsp->yll,bsp->xur,bsp->yur,
+ tpi->chunks[k].x, tpi->chunks[k].y,
+ tpi->chunks[k].xkern, tpi->chunks[k].ykern,
+ tpi->chunks[k].string, tpi->chunks[k].decoration );
}
else { /* TR_PARA_* */
printf("cxi cx[%d] member:%d cx_idx:%d\n",i, j, k);
@@ -669,7 +1136,7 @@ void cxinfo_dump(TR_INFO *tri){
\returns NULL.
*/
CX_INFO *cxinfo_release(CX_INFO *cxi){
- int i;
+ uint32_t i;
if(cxi){
for(i=0;i<cxi->used;i++){ csp_release(&cxi->cx[i].kids); }
free(cxi->cx);
@@ -721,13 +1188,16 @@ int tpinfo_make_insertable(TP_INFO *tpi){
\param tpi pointer to the TP_INFO structure
\param tsp pointer to the TCHUNK_SPECS structure
*/
-int tpinfo_insert(TP_INFO *tpi, TCHUNK_SPECS *tsp){
+int tpinfo_insert(TP_INFO *tpi, const TCHUNK_SPECS *tsp){
int status=1;
+ TCHUNK_SPECS *ltsp;
if(!tpi)return(2);
if(!tsp)return(3);
if(!(status = tpinfo_make_insertable(tpi))){
- memcpy(&(tpi->chunks[tpi->used]),tsp,sizeof(TCHUNK_SPECS));
- if(tsp->co)tpi->chunks[tpi->used].condensed = 75; /* Narrow was set in the font name */
+ ltsp = &(tpi->chunks[tpi->used]);
+ memcpy(ltsp,tsp,sizeof(TCHUNK_SPECS));
+ if(tsp->co)ltsp->condensed = 75; /* Narrow was set in the font name */
+ ltsp->xkern = ltsp->ykern = 0.0; /* kerning will be calculated from the derived layout */
tpi->used++;
}
return(status);
@@ -740,7 +1210,7 @@ int tpinfo_insert(TP_INFO *tpi, TCHUNK_SPECS *tsp){
\param tpi pointer to the TP_INFO structure.
*/
TP_INFO *tpinfo_release(TP_INFO *tpi){
- int i;
+ uint32_t i;
if(tpi){
for(i=0;i<tpi->used;i++){
free(tpi->chunks[i].string); }
@@ -787,7 +1257,7 @@ int brinfo_make_insertable(BR_INFO *bri){
\param bri pointer to the BR_INFO structure
\param element pointer to the BRECT_SPECS structure
*/
-int brinfo_insert(BR_INFO *bri, BRECT_SPECS *element){
+int brinfo_insert(BR_INFO *bri, const BRECT_SPECS *element){
int status=1;
if(!bri)return(2);
if(!(status=brinfo_make_insertable(bri))){
@@ -806,8 +1276,9 @@ int brinfo_insert(BR_INFO *bri, BRECT_SPECS *element){
*/
int brinfo_merge(BR_INFO *bri, int dst, int src){
if(!bri)return(1);
- if(dst<0 || dst>= bri->used)return(2);
- if(src<0 || src>= bri->used)return(3);
+ if(!bri->used)return(2);
+ if(dst<0 || dst >= (int) bri->used)return(3);
+ if(src<0 || src >= (int) bri->used)return(4);
bri->rects[dst].xll = TEREMIN(bri->rects[dst].xll, bri->rects[src].xll);
bri->rects[dst].yll = TEREMAX(bri->rects[dst].yll, bri->rects[src].yll); /* MAX because Y is positive DOWN */
bri->rects[dst].xur = TEREMAX(bri->rects[dst].xur, bri->rects[src].xur);
@@ -841,13 +1312,14 @@ printf("bri_Merge into rect:%d (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%l
\param rp_dst Pointer to edge padding values for dst.
\param rp_src Pointer to edge padding values for src.
*/
-int brinfo_overlap(BR_INFO *bri, int dst, int src, RT_PAD *rp_dst, RT_PAD *rp_src){
+int brinfo_overlap(const BR_INFO *bri, int dst, int src, RT_PAD *rp_dst, RT_PAD *rp_src){
int status;
BRECT_SPECS *br_dst;
BRECT_SPECS *br_src;
if(!bri || !rp_dst || !rp_src)return(2);
- if(dst<0 || dst>= bri->used)return(3);
- if(src<0 || src>= bri->used)return(4);
+ if(!bri->used)return(3);
+ if(dst<0 || dst>= (int) bri->used)return(4);
+ if(src<0 || src>= (int) bri->used)return(5);
br_dst=&bri->rects[dst];
br_src=&bri->rects[src];
if( /* Test all conditions that exclude overlap, if any are true, then no overlap */
@@ -908,6 +1380,36 @@ printf("Overlap rprect (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%lf,%lf)\n
}
/**
+ \brief Check for a text element upstream from the start element and in the reversed direction.
+ \returns 0 on success (not upstream), 1 if upstream, anything else is an error.
+ \param bri pointer to the BR_INFO structure
+ \param dst index of the destination bounding rectangle.
+ \param src index of the source bounding rectangle.
+ \param ddir direction of dst
+ \param sdir direction of src
+*/
+
+int brinfo_upstream(BR_INFO *bri, int dst, int src, int ddir, int sdir){
+ int status=0;
+ BRECT_SPECS *br_dst;
+ BRECT_SPECS *br_src;
+ if(!bri)return(2);
+ if(!bri->used)return(3);
+ if(dst<0 || dst>= (int) bri->used)return(4);
+ if(src<0 || src>= (int) bri->used)return(5);
+ br_dst=&bri->rects[dst];
+ br_src=&bri->rects[src];
+ if( ddir == LDIR_RL && sdir == LDIR_LR){
+ if(br_dst->xur <= (br_src->xll + br_src->xur)/2.0){ status = 1; }
+ }
+ else if( ddir == LDIR_LR && sdir == LDIR_RL){
+ if((br_src->xll + br_src->xur)/2.0 <= br_dst->xll ){ status = 1; }
+ }
+ return(status);
+}
+
+
+/**
\brief Try to deduce justification of a paragraph from the bounding rectangles for two successive lines.
\returns one of TR_PARA_ UJ (unknown justified), LJ, CJ, or RJ (left, center, or right justified).
\param bri pointer to the BR_INFO structure
@@ -917,7 +1419,7 @@ printf("Overlap rprect (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%lf,%lf)\n
\param type Preexisting justification for dst, if any. Justification of dst and src must match this or
TR_PARA_UJ is returned even if dst and src have some (other) alignment.
*/
-enum tr_classes brinfo_pp_alignment(BR_INFO *bri, int dst, int src, double slop, enum tr_classes type){
+enum tr_classes brinfo_pp_alignment(const BR_INFO *bri, int dst, int src, double slop, enum tr_classes type){
enum tr_classes newtype;
BRECT_SPECS *br_dst = & bri->rects[dst];
BRECT_SPECS *br_src = & bri->rects[src];
@@ -1073,91 +1575,6 @@ TR_INFO *trinfo_clear(TR_INFO *tri){
return(tri);
}
-/**
- \brief Load a (new) font by name into a TR_INFO structure.
- \returns index of font. If the font was already loaded return the existing index.
- \param tri pointer to the TR_INFO structure.
- \param fontname UTF-8 font name
- \param tsp pointer to the TCHUNK_SPECS structure - used to construct a font query string.
-*/
-
-int trinfo_load_fontname(TR_INFO *tri, uint8_t *fontname, TCHUNK_SPECS *tsp){
- FcPattern *pattern, *fpat;
- FcResult result = FcResultMatch;
- char *filename;
- double fd;
- int i;
- FT_INFO *fti;
- char buffer[512]; /* big enough */
- FNT_SPECS *fsp;
-
- if(!tri || !(tri->fti))return(1);
-
- fti = tri->fti;
- /* construct a font name */
- sprintf(buffer,"%s:slant=%d:weight=%d:size=%lf:width=%d",fontname,tsp->italics,tsp->weight,tsp->fs,(tsp->co ? 75 : tsp->condensed));
-
- for(i=0;i<fti->used;i++){
- if(0==strcmp((char *) fti->fonts[i].fname,buffer)){
- tsp->fi_idx=i;
- return(0);
- }
- }
-
- ftinfo_make_insertable(fti);
- tsp->fi_idx = fti->used;
-
- if((pattern = FcNameParse((const FcChar8 *)buffer)) == NULL)return(2);
- if(!FcConfigSubstitute(NULL, pattern, FcMatchPattern))return(3);
- FcDefaultSubstitute(pattern);
- if((fpat = FcFontMatch(NULL, pattern, &result)) == NULL || result != FcResultMatch)return(4);
- if(FcPatternGetString( fpat, FC_FILE, 0, (FcChar8 **)&filename) != FcResultMatch)return(5);
- if(FcPatternGetDouble( fpat, FC_SIZE, 0, &fd) != FcResultMatch)return(7);
-
- /* copy these into memory for external use */
- fsp = &(fti->fonts[fti->used]);
- fsp->file = (uint8_t *) U_strdup((char *) filename);
- fsp->fname = (uint8_t *) U_strdup((char *) buffer);
- fsp->fpat = fpat;
- fsp->fsize = fd;
-
- /* release FC's own memory related to this call that does not need to be kept around so that face will work */
- FcPatternDestroy(pattern);
-
- /* get the face */
- if(FT_New_Face( fti->library, (const char *) fsp->file, 0, &(fsp->face) )){ return(8); }
-
- if(FT_Set_Char_Size(
- fsp->face, /* handle to face object */
- 0, /* char_width in 1/64th of points */
- fd*64, /* char_height in 1/64th of points */
- 72, /* horizontal device resolution, DPI */
- 72) /* vebrical device resolution, DPI */
- ){ return(9); }
-
- fti->used++;
-
-/*
- char *fs;
- int fb;
- if(FcPatternGetBool( fpat, FC_OUTLINE, 0, &fb)== FcResultMatch){ printf("outline: %d\n",fb);fflush(stdout); }
- if(FcPatternGetBool( fpat, FC_SCALABLE, 0, &fb)== FcResultMatch){ printf("scalable: %d\n",fb);fflush(stdout); }
- if(FcPatternGetDouble( fpat, FC_DPI, 0, &fd)== FcResultMatch){ printf("DPI: %lf\n",fd);fflush(stdout); }
- if(FcPatternGetInteger( fpat, FC_FONTVERSION, 0, &fb)== FcResultMatch){ printf("fontversion: %d\n",fb);fflush(stdout); }
- if(FcPatternGetString( fpat, FC_FULLNAME , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FULLNAME : %s\n",fs);fflush(stdout); }
- if(FcPatternGetString( fpat, FC_FAMILY , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FAMILY : %s\n",fs);fflush(stdout); }
- if(FcPatternGetString( fpat, FC_STYLE , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("STYLE : %s\n",fs);fflush(stdout); }
- if(FcPatternGetString( fpat, FC_FOUNDRY , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FOUNDRY : %s\n",fs);fflush(stdout); }
- if(FcPatternGetString( fpat, FC_FAMILYLANG , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FAMILYLANG : %s\n",fs);fflush(stdout); }
- if(FcPatternGetString( fpat, FC_STYLELANG , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("STYLELANG : %s\n",fs);fflush(stdout); }
- if(FcPatternGetString( fpat, FC_FULLNAMELANG, 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FULLNAMELANG: %s\n",fs);fflush(stdout); }
- if(FcPatternGetString( fpat, FC_CAPABILITY , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("CAPABILITY : %s\n",fs);fflush(stdout); }
- if(FcPatternGetString( fpat, FC_FONTFORMAT , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FONTFORMAT : %s\n",fs);fflush(stdout); }
-*/
-
- return(0);
-}
-
/**
\brief Set the quantization error value for a TR_INFO structure.
@@ -1239,14 +1656,11 @@ int trinfo_load_ft_opts(TR_INFO *tri, int use_kern, int load_flags, int kern_mod
\param tri pointer to a TR_INFO structure
\param src Pointer to a text string.
*/
-int trinfo_append_out(TR_INFO *tri, char *src){
+int trinfo_append_out(TR_INFO *tri, const char *src){
size_t slen;
if(!src)return(-1);
slen = strlen(src);
- if(tri->outused + (int) slen + 1 < tri->outspace){
- /* already insertable */
- }
- else {
+ if(tri->outused + (int) slen + 1 >= tri->outspace){
tri->outspace += TEREMAX(ALLOCOUT_CHUNK,slen+1);
if(!(tri->out = realloc(tri->out, tri->outspace )))return(-1);
}
@@ -1265,7 +1679,7 @@ int trinfo_append_out(TR_INFO *tri, char *src){
\param flags special processing flags:
TR_EMFBOT calculate Y coordinates of ALIBOT object compatible with EMF files TA_BOTTOM alignment.
*/
-int trinfo_load_textrec(TR_INFO *tri, TCHUNK_SPECS *tsp, double escapement, int flags){
+int trinfo_load_textrec(TR_INFO *tri, const TCHUNK_SPECS *tsp, double escapement, int flags){
int status;
double x,y,xe;
@@ -1285,12 +1699,14 @@ int trinfo_load_textrec(TR_INFO *tri, TCHUNK_SPECS *tsp, double escapement, int
if(!tri)return(1);
if(!tsp)return(2);
if(!tsp->string)return(3);
- fti = tri->fti;
- tpi = tri->tpi;
- bri = tri->bri;
- idx = tsp->fi_idx;
- taln = tsp->taln;
- if(idx <0 || idx >= tri->fti->used)return(4);
+ fti = tri->fti;
+ tpi = tri->tpi;
+ bri = tri->bri;
+ idx = tsp->fi_idx;
+ taln = tsp->taln;
+ if(!fti->used)return(4);
+ if(idx <0 || idx >= (int) fti->used)return(5);
+ fsp = &(fti->fonts[idx]);
if(!tri->dirty){
tri->x = tsp->x;
@@ -1318,10 +1734,11 @@ int trinfo_load_textrec(TR_INFO *tri, TCHUNK_SPECS *tsp, double escapement, int
tpi->chunks[current].x = x * cos(escapement) - y * sin(escapement); /* coordinate transformation */
tpi->chunks[current].y = x * sin(escapement) + y * cos(escapement);
- fsp = &(fti->fonts[idx]);
/* Careful! face bbox does NOT scale with FT_Set_Char_Size
printf("Face idx:%d bbox: xMax/Min:%ld,%ld yMax/Min:%ld,%ld UpEM:%d asc/des:%d,%d height:%d size:%lf\n",
- idx, fsp->face->bbox.xMax,fsp->face->bbox.xMin,fsp->face->bbox.yMax,fsp->face->bbox.yMin,
+ idx,
+ fsp->face->bbox.xMax,fsp->face->bbox.xMin,
+ fsp->face->bbox.yMax,fsp->face->bbox.yMin,
fsp->face->units_per_EM,fsp->face->ascender,fsp->face->descender,fsp->face->height,fsp->fsize);
*/
@@ -1330,17 +1747,22 @@ printf("Face idx:%d bbox: xMax/Min:%ld,%ld yMax/Min:%ld,%ld UpEM:%d asc/des:%d,%
text32 = U_Latin1ToUtf32le((char *) tsp->string,0,NULL);
if(!text32)return(5);
}
- fsp->spcadv = 0.0;
/* baseline advance is independent of character orientation */
for(xe=0.0, prev=0, tptr=text32; *tptr; tptr++){
- status = TR_getadvance(fsp, *tptr, (tri->use_kern ? prev: 0), tri->load_flags, tri->kern_mode, &ymin, &ymax);
+ status = TR_getadvance(fti, fsp, *tptr, (tri->use_kern ? prev: 0), tri->load_flags, tri->kern_mode, &ymin, &ymax);
if(status>=0){
xe += ((double) status)/64.0;
- if(*tptr==' ')fsp->spcadv = ((double) status)/64.0;
}
else { return(6); }
prev=*tptr;
}
+
+ /* Some glyphs in fonts have no vertical extent, for instance, Hebrew glyphs in Century Schoolbook L.
+ Use the 3/4 of the font size as a (very bad) approximation for the actual values. */
+ if(ymin==0 && ymax==0){
+ ymax = 0.75 * fsp->fsize * 64.0;
+ }
+
asc = ((double) (ymax))/64.0;
dsc = ((double) (ymin))/64.0; /* This is negative */
/* This did not work very well because the ascender/descender went well beyond the actual characters, causing
@@ -1350,22 +1772,12 @@ printf("Face idx:%d bbox: xMax/Min:%ld,%ld yMax/Min:%ld,%ld UpEM:%d asc/des:%d,%
*/
free(text32);
-
- /* get the advance on a space if it has not already been set */
- if(fsp->spcadv==0.0){
- status = TR_getadvance(fsp,' ',0, tri->load_flags, tri->kern_mode, NULL, NULL);
- if(status>=0){ fsp->spcadv = ((double) status)/64.0; }
- else { return(7); }
- }
/* find the font ascender descender (general one, not specific for current text) */
fasc = ((double) (fsp->face->ascender) )/64.0;
fdsc = ((double) (fsp->face->descender))/64.0;
- if(tri->load_flags & FT_LOAD_NO_SCALE){
- xe *= tsp->fs/32.0;
- fsp->spcadv *= tsp->fs/32.0;
- }
+ if(tri->load_flags & FT_LOAD_NO_SCALE) xe *= tsp->fs/32.0;
/* now place the rectangle using ALN information */
if( taln & ALIHORI & ALILEFT ){
@@ -1380,6 +1792,7 @@ printf("Face idx:%d bbox: xMax/Min:%ld,%ld yMax/Min:%ld,%ld UpEM:%d asc/des:%d,%
bsp.xll = tpi->chunks[current].x - xe;
bsp.xur = tpi->chunks[current].x;
}
+ tpi->chunks[current].ldir = tsp->ldir;
if(tri->load_flags & FT_LOAD_NO_SCALE){
asc *= tsp->fs/32.0;
@@ -1458,9 +1871,6 @@ void TR_layout_2_svg(TR_INFO *tri){
double x = tri->x;
double y = tri->y;
double dx,dy;
- double lastx = 0.0;
- double lasty = 0.0;
- double qsp;
double esc;
double recenter; /* horizontal offset to set things up correctly for CJ and RJ text, is 0 for LJ*/
double lineheight=1.25;
@@ -1470,22 +1880,26 @@ void TR_layout_2_svg(TR_INFO *tri){
BR_INFO *bri=tri->bri; /* bounding Rectangle Info storage */
CX_INFO *cxi=tri->cxi; /* Complexes deduced for this text */
TCHUNK_SPECS *tsp; /* current text object */
- TCHUNK_SPECS *ptsp; /* previous text object in the same line as current text object, if any */
- FNT_SPECS *fsp;
CX_SPECS *csp;
CX_SPECS *cline_sp;
- int i,j,k,jdx,kdx;
- int status;
+ unsigned int i,j,k,jdx,kdx;
+ int ldir;
char obuf[1024]; /* big enough for style and so forth */
+ char cbuf[16]; /* big enough for one hex color */
char stransform[128];
- double newx,newy;
+ double newx,newy,tmpx;
+ uint32_t utmp;
+/*
+#define DBG_TR_PARA 0
+#define DBG_TR_INPUT 1
+*/
/* The debug section below is difficult to see if usebk is anything other than BKCLR_NONE */
-#if defined(DBG_TR_PARA) || defined(DBG_TR_INPUT) /* enable debugging code, writes extra information into SVG */
+#if DBG_TR_PARA || DBG_TR_INPUT /* enable debugging code, writes extra information into SVG */
/* put rectangles down for each text string - debugging!!! This will not work properly for any Narrow fonts */
- esc = tri->esc;
- esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */
+ esc = tri->esc;
+ esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */
sprintf(stransform,"transform=\"matrix(%lf,%lf,%lf,%lf,%lf,%lf)\"\n",cos(esc),-sin(esc),sin(esc),cos(esc), 1.25*x,1.25*y);
for(i=cxi->phase1; i<cxi->used;i++){ /* over all complex members from phase2 == TR_PARA_* complexes */
csp = &(cxi->cx[i]);
@@ -1494,10 +1908,11 @@ void TR_layout_2_svg(TR_INFO *tri){
for(k=0; k<cxi->cx[jdx].kids.used; k++){ /* over all members of the phase1 complex */
kdx = cxi->cx[jdx].kids.members[k]; /* index for text objects in tpi */
tsp = &tpi->chunks[kdx];
+ ldir = tsp->ldir;
if(!j && !k){
-#ifdef DBG_TR_PARA
+#if DBG_TR_PARA
TRPRINT(tri, "<rect\n");
- TRPRINT(tri, "style=\"color:#0000FF;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:none;stroke:#000000;stroke-width:0.30000001;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero\"\n");
+ TRPRINT(tri, "style=\"color:#0000FF;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:none;stroke:#000000;stroke-width:0.8;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero\"\n");
sprintf(obuf,"width=\"%lf\"\n", 1.25*(bri->rects[csp->rt_cidx].xur - bri->rects[csp->rt_cidx].xll));
TRPRINT(tri, obuf);
sprintf(obuf,"height=\"%lf\"\n",1.25*(bri->rects[csp->rt_cidx].yll - bri->rects[csp->rt_cidx].yur));
@@ -1508,16 +1923,16 @@ void TR_layout_2_svg(TR_INFO *tri){
TRPRINT(tri, "/>\n");
#endif /* DBG_TR_PARA */
}
-#ifdef DBG_TR_INPUT /* debugging code, this section writes the original text objects */
- newx = 1.25*(bri->rects[tsp->rt_tidx].xll);
+#if DBG_TR_INPUT /* debugging code, this section writes the original text objects */
+ newx = 1.25*(ldir == LDIR_RL ? bri->rects[tsp->rt_tidx].xur : bri->rects[tsp->rt_tidx].xll);
newy = 1.25*(bri->rects[tsp->rt_tidx].yur);
TRPRINT(tri, "<rect\n");
- TRPRINT(tri, "style=\"color:#000000;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:none;stroke:#000000;stroke-width:0.30000001;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero\"\n");
+ TRPRINT(tri, "style=\"color:#000000;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:none;stroke:#00FF00;stroke-width:0.3;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero\"\n");
sprintf(obuf,"width=\"%lf\"\n", 1.25*(bri->rects[tsp->rt_tidx].xur - bri->rects[tsp->rt_tidx].xll));
TRPRINT(tri, obuf);
sprintf(obuf,"height=\"%lf\"\n",1.25*(bri->rects[tsp->rt_tidx].yll - bri->rects[tsp->rt_tidx].yur));
TRPRINT(tri, obuf);
- sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n",newx,newy);
+ sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n",1.25*(bri->rects[tsp->rt_tidx].xll),newy);
TRPRINT(tri, obuf);
TRPRINT(tri, stransform);
TRPRINT(tri, "/>\n");
@@ -1538,9 +1953,10 @@ void TR_layout_2_svg(TR_INFO *tri){
TRPRINT(tri, obuf);
sprintf(obuf,"font-stretch:%s;",(tsp->condensed==100 ? "Normal" : "Condensed"));
TRPRINT(tri, obuf);
- cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fname,":");
- fti->fonts[tsp->fi_idx].fname[cutat]='\0';
- sprintf(obuf,"font-family:%s;",fti->fonts[tsp->fi_idx].fname);
+ sprintf(obuf,"text-anchor:%s;",(tsp->ldir == LDIR_RL ? "end" : "start"));
+ TRPRINT(tri, obuf);
+ cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fontspec,":");
+ sprintf(obuf,"font-family:%.*s;",cutat,fti->fonts[tsp->fi_idx].fontspec);
TRPRINT(tri, obuf);
sprintf(obuf,"\n\">%s</text>\n",tsp->string);
TRPRINT(tri, obuf);
@@ -1552,8 +1968,8 @@ void TR_layout_2_svg(TR_INFO *tri){
if(tri->usebk){
- esc = tri->esc;
- esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */
+ esc = tri->esc;
+ esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */
sprintf(stransform,"transform=\"matrix(%lf,%lf,%lf,%lf,%lf,%lf)\"\n",cos(esc),-sin(esc),sin(esc),cos(esc), 1.25*x,1.25*y);
for(i=cxi->phase1; i<cxi->used;i++){ /* over all complex members from phase2 == TR_PARA_* complexes */
@@ -1579,6 +1995,7 @@ void TR_layout_2_svg(TR_INFO *tri){
for(k=0; k<cxi->cx[jdx].kids.used; k++){ /* over all members of the phase1 complex */
kdx = cxi->cx[jdx].kids.members[k]; /* index for text objects in tpi */
tsp = &tpi->chunks[kdx];
+ ldir = tsp->ldir;
if(!j && !k){
if(tri->usebk == BKCLR_ALL){
TRPRINT(tri, "<rect\n");
@@ -1595,7 +2012,7 @@ void TR_layout_2_svg(TR_INFO *tri){
}
}
if(tri->usebk == BKCLR_FRAG){
- newx = 1.25*(bri->rects[tsp->rt_tidx].xll);
+ newx = 1.25*(ldir == LDIR_RL ? bri->rects[tsp->rt_tidx].xur : bri->rects[tsp->rt_tidx].xll);
newy = 1.25*(bri->rects[tsp->rt_tidx].yur);
TRPRINT(tri, "<rect\n");
sprintf(obuf,"style=\"color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#%2.2X%2.2X%2.2X;;stroke:none;;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero\"\n",tri->bkcolor.Red,tri->bkcolor.Green,tri->bkcolor.Blue);
@@ -1616,12 +2033,12 @@ void TR_layout_2_svg(TR_INFO *tri){
}
- tsp=tpi->chunks;
+ tsp = tpi->chunks;
/* over all complex members from phase2. Paragraphs == TR_PARA_* */
for(i=cxi->phase1; i<cxi->used;i++){
- csp = &(cxi->cx[i]);
- esc = tri->esc;
- esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */
+ csp = &(cxi->cx[i]);
+ esc = tri->esc;
+ esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */
/* over all members of the present Paragraph. Each of these is a line and a phase 1 complex.
It may be either TR_TEXT or TR_LINE */
@@ -1630,37 +2047,34 @@ void TR_layout_2_svg(TR_INFO *tri){
sprintf(obuf,"</tspan>");
TRPRINT(tri, obuf);
}
- jdx = csp->kids.members[j]; /* index of phase1 complex (all are TR_TEXT or TR_LINE) */
- lastx = bri->rects[jdx].xur;
- lasty = bri->rects[jdx].yll - tsp->boff;
- recenter = 0; /* mostly to quiet a compiler warning, should always be set below */
+ jdx = csp->kids.members[j]; /* index of phase1 complex (all are TR_TEXT or TR_LINE) */
+ recenter = 0; /* mostly to quiet a compiler warning, should always be set below */
+
/* over all members of the present Line. These are the original text objects which were reassembled.
There will be one for TR_TEXT, more than one for TR_LINE */
- for(ptsp = NULL, k=0; k<cxi->cx[jdx].kids.used; k++){
- if(k){
- ptsp=tsp; /* previous text object in this line */
- fsp = &(fti->fonts[tpi->chunks[kdx].fi_idx]); /* font spec for previous text object */
- }
- kdx = cxi->cx[jdx].kids.members[k]; /* index for text objects in tpi */
- tsp = &tpi->chunks[kdx];
- if(!k){
- switch(csp->type){ /* set up the alignment, if there is one */
+ for(k=0; k<cxi->cx[jdx].kids.used; k++){
+ kdx = cxi->cx[jdx].kids.members[k]; /* index for text objects in tpi, for this k */
+ tsp = &tpi->chunks[kdx]; /* text chunk for this k */
+ ldir = tsp->ldir; /* language direction for this k */
+ if(!k){ /* first iteration */
+ switch(csp->type){ /* set up the alignment, if there is one */
case TR_TEXT:
case TR_LINE:
/* these should never occur, this section quiets a compiler warning */
break;
case TR_PARA_UJ:
- recenter=0.0;
- break;
case TR_PARA_LJ:
- recenter=0.0;
+ if(ldir == LDIR_RL){ recenter = -(bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll); }
+ else { recenter = 0.0; }
break;
case TR_PARA_CJ:
- recenter=(bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll)/2.0;
+ if(ldir == LDIR_RL){ recenter = -(bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll)/2.0; }
+ else { recenter = +(bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll)/2.0; }
break;
case TR_PARA_RJ:
- recenter=bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll;
+ if(ldir == LDIR_RL){ recenter = 0.0; }
+ else { recenter = +(bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll); }
break;
}
if(!j){
@@ -1685,9 +2099,8 @@ void TR_layout_2_svg(TR_INFO *tri){
TRPRINT(tri, "fill:#000000;");
TRPRINT(tri, "fill-opacity:1;");
TRPRINT(tri, "stroke:none;");
- cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fname,":");
- fti->fonts[tsp->fi_idx].fname[cutat]='\0';
- sprintf(obuf,"font-family:%s;",fti->fonts[tsp->fi_idx].fname);
+ cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fontspec,":");
+ sprintf(obuf,"font-family:%.*s;",cutat,fti->fonts[tsp->fi_idx].fontspec);
TRPRINT(tri, obuf);
switch(csp->type){ /* set up the alignment, if there is one */
case TR_TEXT:
@@ -1695,8 +2108,6 @@ void TR_layout_2_svg(TR_INFO *tri){
/* these should never occur, this section quiets a compiler warning */
break;
case TR_PARA_UJ:
- *obuf='\0';
- break;
case TR_PARA_LJ:
sprintf(obuf,"text-align:start;text-anchor:start;");
break;
@@ -1704,42 +2115,30 @@ void TR_layout_2_svg(TR_INFO *tri){
sprintf(obuf,"text-align:center;text-anchor:middle;");
break;
case TR_PARA_RJ:
- sprintf(obuf,"text-align:end;text-anchor:end;");
+ sprintf(obuf,"text-align:end;text-anchor:end;");
break;
}
TRPRINT(tri, obuf);
TRPRINT(tri, "\"\n"); /* End of style specification */
sprintf(obuf,"transform=\"matrix(%lf,%lf,%lf,%lf,%lf,%lf)\"\n",cos(esc),-sin(esc),sin(esc),cos(esc),1.25*x,1.25*y);
TRPRINT(tri, obuf);
- sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n>",1.25*(bri->rects[kdx].xll + recenter),1.25*(bri->rects[kdx].yll - tsp->boff));
+ tmpx = 1.25*((ldir == LDIR_RL ? bri->rects[kdx].xur : bri->rects[kdx].xll) + recenter);
+ sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n>",tmpx,1.25*(bri->rects[kdx].yll - tsp->boff));
TRPRINT(tri, obuf);
}
- sprintf(obuf,"<tspan sodipodi:role=\"line\"\nx=\"%lf\" y=\"%lf\"\n>",
- 1.25*(bri->rects[kdx].xll + recenter),1.25*(bri->rects[kdx].yll - tsp->boff));
+ tmpx = 1.25*((ldir == LDIR_RL ? bri->rects[kdx].xur : bri->rects[kdx].xll) + recenter);
+ sprintf(obuf,"<tspan sodipodi:role=\"line\"\nx=\"%lf\" y=\"%lf\"\n>",tmpx,1.25*(bri->rects[kdx].yll - tsp->boff));
TRPRINT(tri, obuf);
}
- TRPRINT(tri, "<tspan\n");
- dx = 1.25*(bri->rects[tsp->rt_tidx].xll - lastx);
- dy = 1.25*(bri->rects[tsp->rt_tidx].yll - tsp->boff - lasty);
+ TRPRINT(tri, "<tspan\n");
- /* Have to also take into account kerning between the last letter of the preceding rectangle
- and the first letter of the current one. Assume font values are from leading retangle's font. */
- if(ptsp && tri->use_kern){
- status = TR_kern_gap(fsp, tsp, ptsp, tri->kern_mode);
- if(status){
- dx += (tri->load_flags & FT_LOAD_NO_SCALE ? tsp->fs/32.0: 1.0) * ((double) status)/64.0;
- }
- }
+ /* Scale kerning and make any other necessary adjustments
+
+ */
+ dx = 1.25 * tsp->xkern;
+ dy = 1.25 * tsp->ykern;
- /* Sometimes a font substitution was absolutely terrible, for instance, for Arial Narrow on (most) Linux systems,
- The resulting advance may be much too large so that it overruns the next text chunk. Since overlapping text on
- the same line is almost never encountered, this may be used to detect the bad substitution so that a more appropriate
- offset can be used.
- Detect this situation as a negative dx < 1/2 a space character's width while |dy| < an entire space width. */
- qsp = 1.25 * 0.25 * fti->fonts[tsp->fi_idx].spcadv;
- if((dy <=qsp && dy >= -qsp) && dx < -2*qsp){ dx=0.0; }
- if(k==0){ sprintf(obuf,"dx=\"%lf\" dy=\"%lf\" ",0.0, 0.0); }
- else { sprintf(obuf,"dx=\"%lf\" dy=\"%lf\" ",dx, dy); }
+ sprintf(obuf,"dx=\"%lf\" dy=\"%lf\" ",dx, dy);
TRPRINT(tri, obuf);
sprintf(obuf,"style=\"fill:#%2.2X%2.2X%2.2X;",tsp->color.Red,tsp->color.Green,tsp->color.Blue);
TRPRINT(tri, obuf);
@@ -1747,38 +2146,44 @@ void TR_layout_2_svg(TR_INFO *tri){
TRPRINT(tri, obuf);
sprintf(obuf,"font-style:%s;",(tsp->italics ? "italic" : "normal"));
TRPRINT(tri, obuf);
- switch(tsp->decoration){
- case TXTDECOR_NONE:
- case TXTDECOR_STRIKE2:
- default:
- break;
- case TXTDECOR_UNDER:
- TRPRINT(tri,"text-decoration:underline;");
- break;
- case TXTDECOR_OVER:
- TRPRINT(tri,"text-decoration:overline;");
- break;
- case TXTDECOR_BLINK:
- TRPRINT(tri,"text-decoration:blink;");
- break;
- case TXTDECOR_STRIKE1:
- TRPRINT(tri,"text-decoration:line-through;");
- break;
+ if(tsp->decoration & TXTDECOR_TMASK){
+ sprintf(obuf,"text-decoration:");
+ /* multiple text decoration styles may be set */
+ utmp = tsp->decoration & TXTDECOR_TMASK;
+ if(utmp & TXTDECOR_UNDER ){ strcat(obuf,"underline"); }
+ if(utmp & TXTDECOR_OVER ){ strcat(obuf,"overline"); }
+ if(utmp & TXTDECOR_BLINK ){ strcat(obuf,"blink"); }
+ if(utmp & TXTDECOR_STRIKE){ strcat(obuf,"line-through");}
+ if(*obuf){
+ /* only a single text decoration line type may be set */
+ switch(tsp->decoration & TXTDECOR_LMASK){
+ case TXTDECOR_SOLID: break; // "solid" is the CSS 3 default, omitting it remains CSS 2 compatible
+ case TXTDECOR_DOUBLE: strcat(obuf," double"); break; // these are all CSS3
+ case TXTDECOR_DOTTED: strcat(obuf," dotted"); break;
+ case TXTDECOR_DASHED: strcat(obuf," dashed"); break;
+ case TXTDECOR_WAVY: strcat(obuf," wavy" ); break;
+ default: break;
+ }
+ if((tsp->decoration & TXTDECOR_CLRSET) && memcmp(&(tsp->decColor),&(tsp->color),sizeof(TRCOLORREF))){
+ /* CSS 3, CSS 2 implementations may choke on it. If the specified color matches text color omit, for better CSS 2 compatitiblity. */
+ sprintf(cbuf," #%2.2X%2.2X%2.2X",tsp->decColor.Red,tsp->decColor.Green,tsp->decColor.Blue);
+ strcat(obuf,cbuf);
+ }
+ }
+ strcat(obuf,";");
+ TRPRINT(tri,obuf);
}
TRPRINT(tri, "font-variant:normal;");
sprintf(obuf,"font-weight:%d;",TR_weight_FC_to_SVG(tsp->weight));
TRPRINT(tri, obuf);
sprintf(obuf,"font-stretch:%s;",(tsp->condensed==100 ? "Normal" : "Condensed"));
TRPRINT(tri, obuf);
- cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fname,":");
- fti->fonts[tsp->fi_idx].fname[cutat]='\0';
- sprintf(obuf,"font-family:%s;\"",fti->fonts[tsp->fi_idx].fname);
+ cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fontspec,":");
+ sprintf(obuf,"font-family:%.*s;\"",cutat,fti->fonts[tsp->fi_idx].fontspec);
TRPRINT(tri, obuf);
TRPRINT(tri, "\n>");
TRPRINT(tri, (char *) tsp->string);
TRPRINT(tri, "</tspan>");
- lastx=bri->rects[tsp->rt_tidx].xur;
- lasty=bri->rects[tsp->rt_tidx].yll - tsp->boff;
} /* end of k loop */
} /* end of j loop */
TRPRINT(tri,"</tspan></text>\n");
@@ -1800,7 +2205,7 @@ void TR_layout_2_svg(TR_INFO *tri){
\param tri pointer to the TR_INFO structure holding the data, which will also hold the results.
*/
int TR_layout_analyze(TR_INFO *tri){
- int i,j;
+ unsigned int i,j,k;
int ok;
int cxidx;
int src_rt;
@@ -1813,7 +2218,17 @@ int TR_layout_analyze(TR_INFO *tri){
RT_PAD rt_pad_i;
RT_PAD rt_pad_j;
double ratio;
+ double qsp,dx,dy;
+ double spcadv;
enum tr_classes type;
+ TCHUNK_SPECS *tspi;
+ TCHUNK_SPECS *tspj;
+ TCHUNK_SPECS *tspRevEnd=NULL;
+ TCHUNK_SPECS *tspRevStart=NULL;
+ CX_SPECS *csp;
+ CHILD_SPECS *kidp; /* used with preceding complex (see below) */
+ CHILD_SPECS *kidc; /* used with current complex (see below) */
+ int lastldir,ldir,rev;
if(!tri)return(-1);
if(!tri->cxi)return(-2);
@@ -1828,27 +2243,48 @@ int TR_layout_analyze(TR_INFO *tri){
cxi->paras = 0;
cxi->phase1 = 0;
+/* When debugging
+ ftinfo_dump(fti);
+*/
/* Phase 1. Working sequentially, insert text. Initially as TR_TEXT and then try to extend to TR_LINE by checking
overlaps. When done the complexes will contain a mix of TR_LINE and TR_TEXT. */
for(i=0; i<tpi->used; i++){
- memcpy(&bsp,&(bri->rects[tpi->chunks[i].rt_tidx]),sizeof(BRECT_SPECS)); /* Must make a copy as next call may reallocate rects! */
+ tspi = &(tpi->chunks[i]);
+ memcpy(&bsp,&(bri->rects[tspi->rt_tidx]),sizeof(BRECT_SPECS)); /* Must make a copy as next call may reallocate rects! */
(void) brinfo_insert(bri,&bsp);
dst_rt = bri->used-1;
(void) cxinfo_insert(cxi, i, dst_rt, TR_TEXT);
cxidx = cxi->used-1;
- /* for the leading text: pad with no leading and two trailing spaces */
- TR_rt_pad_set(&rt_pad_i,tri->qe, tri->qe, 0.0, tri->qe + 2.0 * fti->fonts[tpi->chunks[i].fi_idx].spcadv);
+
+ spcadv = fti->fonts[tspi->fi_idx].spcadv * tspi->fs/32.0; /* spcadv was always FT_LOAD_NO_SCALE */
+ /* for the leading text: pad with no leading and two trailing spaces, leading and trailing depend on direction */
+ if(tspi->ldir == LDIR_RL){ TR_rt_pad_set(&rt_pad_i,tri->qe, tri->qe, tri->qe + 2.0 * spcadv, 0.0); }
+ else { TR_rt_pad_set(&rt_pad_i,tri->qe, tri->qe, 0.0, tri->qe + 2.0 * spcadv); }
for(j=i+1; j<tpi->used; j++){
+ tspj = &(tpi->chunks[j]);
/* Reject font size changes of greater than 50%, these are almost certainly not continuous text. These happen
in math formulas, for instance, where a sum or integral is much larger than the other symbols. */
- ratio = (double)(tpi->chunks[j].fs)/(double)(tpi->chunks[i].fs);
+ ratio = (double)(tspj->fs)/(double)(tspi->fs);
if(ratio >2.0 || ratio <0.5)break;
+
+ spcadv = fti->fonts[tspj->fi_idx].spcadv * tspj->fs/32.0; /* spcadv was always FT_LOAD_NO_SCALE */
+ /* for the trailing text: pad with one leading and trailing spaces (so it should work L->R and R->L) */
+ TR_rt_pad_set(&rt_pad_j,tri->qe, tri->qe, spcadv, spcadv);
+ src_rt = tspj->rt_tidx;
+
+ /* Reject direction changes like [1 <- Hebrew][2 -> English], that is where the direction changes AND the
+ next logical piece of text is "upstream" positionally of its logical predecessor. The meaning of such
+ a construct is at best ambiguous. The test is only applied with respect to the first text chunk. This sort
+ of construct may appear when a valid initial construct like [1->English][2<-Hebrew][3->English] is edited
+ and the leading chunk of text removed.
+ */
+ if(brinfo_upstream(bri,
+ dst_rt, /* index into bri for dst */
+ src_rt, /* index into bri for src */
+ tspi->ldir,tspj->ldir))break;
- /* for the trailing text: pad with one leading and trailing spaces (so it should work L->R and R->L */
- TR_rt_pad_set(&rt_pad_j,tri->qe, tri->qe, fti->fonts[tpi->chunks[j].fi_idx].spcadv, fti->fonts[tpi->chunks[j].fi_idx].spcadv);
- src_rt = tpi->chunks[j].rt_tidx;
if(!brinfo_overlap(bri,
dst_rt, /* index into bri for dst */
src_rt, /* index into bri for src */
@@ -1856,17 +2292,146 @@ int TR_layout_analyze(TR_INFO *tri){
(void) cxinfo_append(cxi,j,TR_LINE);
(void) brinfo_merge(bri,dst_rt,src_rt);
/* for the leading text: pad with two leading and trailing spaces (so it should work L->R and R->L */
+ spcadv = fti->fonts[tspj->fi_idx].spcadv * tspj->fs/32.0; /* spcadv was always FT_LOAD_NO_SCALE */
TR_rt_pad_set(&rt_pad_i, tri->qe, tri->qe,
- tri->qe + 2.0 * fti->fonts[tpi->chunks[j].fi_idx].spcadv, tri->qe + 2.0 * fti->fonts[tpi->chunks[j].fi_idx].spcadv);
+ tri->qe + 2.0 * spcadv, tri->qe + 2.0 * spcadv);
}
else { /* either alignment ge*/
break;
}
}
- i=j-1; /* start up after the last merged entry (there may not be any) */
+
+ /* Bidirectional text will cause complexes to not assemble in one pass.
+ This happens whenever a change of direction occurs with 2 or more sequential elements in
+ the opposite direction,
+
+ Let + = LR and - = RL.
+
+ Reading left to right, this happens with +-- or -++.
+ For instance, the sequence ++-+ ---+ would break into the two complexes shown.
+ Not until the last element in the second complex is added will the bounding rectangles for the complexes overlap.
+
+ Check for this effect now if there is a preceding complex and the first element of the current complex is
+ reversed from the last in the preceding. */
+ if(cxidx >= 1){
+ kidp = &(cxi->cx[cxidx-1].kids);
+ kidc = &(cxi->cx[cxidx ].kids);
+ tspi = &(tpi->chunks[ kidp->members[kidp->used - 1] ]); /* here, the last text element in preceding complex */
+ tspj = &(tpi->chunks[ kidc->members[0 ] ]); /* here, tge first text element in current complex */
+ if(tspi->ldir != tspj->ldir){
+ spcadv = fti->fonts[tspi->fi_idx].spcadv * tspi->fs/32.0;
+ if(tspi->ldir == LDIR_RL){ TR_rt_pad_set(&rt_pad_i,tri->qe, tri->qe, tri->qe + 2.0 * spcadv, 0.0); }
+ else { TR_rt_pad_set(&rt_pad_i,tri->qe, tri->qe, 0.0, tri->qe + 2.0 * spcadv); }
+ spcadv = fti->fonts[tspj->fi_idx].spcadv * tspj->fs/32.0;
+ TR_rt_pad_set(&rt_pad_j,tri->qe, tri->qe, spcadv, spcadv);
+ if(!brinfo_overlap(bri,
+ cxi->cx[cxidx-1].rt_cidx, /* index into rt for dst cx */
+ cxi->cx[cxidx].rt_cidx, /* index into rt for src cx */
+ &rt_pad_i,&rt_pad_j)){
+ /* Merge the current complex into the preceding one*/
+ (void) cxinfo_merge(cxi, cxidx-1, cxidx, TR_LINE);
+ (void) brinfo_merge(bri,cxi->cx[cxidx-1].rt_cidx,cxi->cx[cxidx].rt_cidx); /* merge the bounding boxes*/
+ (void) cxinfo_trim(cxi);
+ cxi->lines--; /* else the normal line count value is one too high */
+ /* remove the current complex */
+ }
+ }
+ }
+
if(cxi->cx[cxidx].type == TR_LINE)cxi->lines++;
+ i=j-1; /* start up after the last merged entry (there may not be any) */
}
cxi->phase1 = cxi->used; /* total complexes defined in this phase, all TR_LINE or TR_TEXT */
+
+ /* phase 1.5, calculate kerning. This is as good a place to do it as any. At this point all kern values
+ are zero. Each of these pieces is strictly unidirectional, but each piece can have a different direction.
+ The direction of the line is set by the first text element. The ends of runs of elements which are
+ reversed with respect to the line direction are special, everything else is simple:
+ Let: + == L->R, - == R->L, $ == end of text, the rules for kerning on B are:
+ A B others xkern
+ [+|$] + + [+|$] Bll - Aur
+ [-|$] - - [-|$] All - Bur (chs)
+ + - + [-|$] Bll - Aur (chs)
+ - + - [+|$] All - Bur
+ + - -...[-=C] [+|$] All - Cur (chs)
+ - + +...[+=C] [-|$] Cll - Aur
+
+ chs = change sign, because dx is an absolute direction, and direction of text on RTL is in -x.
+
+ Kerning calculations currently seems unstable for R->L if the kerning extends to the end of the line. If
+ the first and last characters are back in sync there are no issues. When things go south R->L left justified
+ text is not justified when read in.
+ */
+
+ for(i=0; i < cxi->phase1; i++){ /* over all lines */
+ csp = &(cxi->cx[i]);
+ if(csp->kids.used < 2)continue; /* no kerning possible */
+ tspi = &tpi->chunks[csp->kids.members[0]]; /* used here as last tsp. no kerning is applied to the first element */
+ lastldir = ldir = tspi->ldir;
+ rev = 0; /* the first ldir defines forward and reverse */
+ for(j=1; j<csp->kids.used; j++){
+ tspj = &tpi->chunks[csp->kids.members[j]];
+ ldir = tspj->ldir;
+ if(ldir != lastldir){ /* direction change */
+ rev = !rev; /* reverse direction tracker */
+ if(!rev){ /* back in original orientation */
+ if(ldir == LDIR_RL){ tspj->xkern = bri->rects[tspj->rt_tidx].xur - bri->rects[tspRevStart->rt_tidx].xll; }
+ else { tspj->xkern = bri->rects[tspj->rt_tidx].xll - bri->rects[tspRevStart->rt_tidx].xur; }
+ tspj->ykern = (bri->rects[tspj->rt_tidx].yll - tspj->boff) -
+ (bri->rects[tspRevStart->rt_tidx].yll - tspRevStart->boff);
+ }
+ else { /* now in reversed orientation */
+ tspRevStart = tspj; /* Save the beginning of this run (length >=1 ) */
+ /* scan forward for the last text object in this orientation, include the first */
+ for(k=j; k <csp->kids.used; k++){
+ if(tpi->chunks[csp->kids.members[k]].ldir == ldir){ tspRevEnd = &tpi->chunks[csp->kids.members[k]]; }
+ else { break; }
+ }
+ if(lastldir == LDIR_RL){ tspj->xkern = bri->rects[tspRevEnd->rt_tidx].xur - bri->rects[tspi->rt_tidx].xll; }
+ else { tspj->xkern = bri->rects[tspRevEnd->rt_tidx].xll - bri->rects[tspi->rt_tidx].xur; }
+ tspj->ykern = (bri->rects[tspRevEnd->rt_tidx].yll - tspRevEnd->boff) -
+ (bri->rects[ tspi->rt_tidx].yll - tspi->boff );
+ }
+ }
+ else {
+ if(ldir == LDIR_RL){ tspj->xkern = bri->rects[tspj->rt_tidx].xur - bri->rects[tspi->rt_tidx].xll; }
+ else { tspj->xkern = bri->rects[tspj->rt_tidx].xll - bri->rects[tspi->rt_tidx].xur; }
+ tspj->ykern = (bri->rects[tspj->rt_tidx].yll - tspj->boff) -
+ (bri->rects[tspi->rt_tidx].yll - tspi->boff);
+ }
+
+
+ /*
+ Sometimes a font substitution was absolutely terrible, for instance, for Arial Narrow on (most) Linux systems,
+ The resulting advance (xkern) may be much too large so that it overruns the next text chunk. Since
+ overlapping text on the same line is almost never encountered, this may be used to detect the bad
+ substitution so that a more appropriate offset can be used.
+ Detect this situation as a negative dx < 1/2 a space character's width while |dy| < an entire space width.
+ The y constraints allow super and subscripts, which overlap in x but are shifted above/below in y.
+ */
+ spcadv = fti->fonts[tspj->fi_idx].spcadv * tspj->fs/32.0;
+ qsp = 0.25 * spcadv;
+ dx = tspj->xkern;
+ dy = tspj->ykern;
+ if(dy <=qsp && dy >= -qsp){
+ if(ldir==LDIR_RL){
+ if(dx > 2*qsp)tspj->xkern = 0.0;
+ }
+ else {
+ if(dx < -2*qsp)tspj->xkern = 0.0;
+ }
+ }
+
+ /* if x or y kern is less than the quantization error it is probably noise, set it to zero */
+ if(fabs(tspj->xkern)<tri->qe)tspj->xkern = 0.0;
+ if(fabs(tspj->ykern)<tri->qe)tspj->ykern = 0.0;
+
+
+ tspi = tspj;
+ lastldir = ldir;
+ }
+ }
+
/* Phase 2, try to group sequential lines. There may be "lines" that are still TR_TEXT, as in:
@@ -1885,10 +2450,13 @@ int TR_layout_analyze(TR_INFO *tri){
for(i=0; i < cxi->phase1; i++){
type = TR_PARA_UJ; /* any paragraph alignment will be acceptable */
- memcpy(&bsp,&(bri->rects[cxi->cx[i].rt_cidx]),sizeof(BRECT_SPECS)); /* Must make a copy as next call may reallocate rects! */
+ /* Must make a copy as next call may reallocate rects, so if we just passed a pointer to something in the structure
+ it would vaporize part way through the call. */
+ memcpy(&bsp,&(bri->rects[cxi->cx[i].rt_cidx]),sizeof(BRECT_SPECS));
(void) brinfo_insert(bri,&bsp);
dst_rt = bri->used-1;
(void) cxinfo_insert(cxi, i, dst_rt, type);
+
cxi->paras++;
ok = 1;
for(j=i+1; ok && (j < cxi->phase1); j++){
@@ -1910,8 +2478,8 @@ int TR_layout_analyze(TR_INFO *tri){
src_rt = cxi->cx[j].rt_cidx;
(void) cxinfo_append(cxi, j, type);
(void) brinfo_merge(bri, dst_rt, src_rt);
- }
- break;
+ }
+ break;
default:
return(-6); /* programming error */
}
@@ -1920,8 +2488,9 @@ int TR_layout_analyze(TR_INFO *tri){
i=j-1;
}
+
/* When debugging
- cxinfo_dump(tri);
+ cxinfo_dump(tri);
*/
return(cxi->used);
@@ -1932,7 +2501,7 @@ int TR_layout_analyze(TR_INFO *tri){
#if TEST
#define MAXLINE 2048 /* big enough for testing */
-enum OP_TYPES {OPCOM,OPOOPS,OPFONT,OPESC,OPORI,OPXY,OPFS,OPTEXT,OPALN,OPLDIR,OPMUL,OPITA,OPWGT,OPDEC,OPCND,OPCLR,OPBKG,OPBCLR,OPFLAGS,OPEMIT,OPDONE};
+enum OP_TYPES {OPCOM,OPOOPS,OPFONT,OPESC,OPORI,OPXY,OPFS,OPTEXT,OPALN,OPLDIR,OPMUL,OPITA,OPWGT,OPDEC,OPCND,OPBKG,OPCLR,OPDCLR,OPBCLR,OPFLAGS,OPEMIT,OPDONE};
int parseit(char *buffer,char **data){
int pre;
@@ -1954,8 +2523,9 @@ int parseit(char *buffer,char **data){
if(0==strcmp("WGT", buffer))return(OPWGT );
if(0==strcmp("DEC", buffer))return(OPDEC );
if(0==strcmp("CND", buffer))return(OPCND );
- if(0==strcmp("CLR", buffer))return(OPCLR );
if(0==strcmp("BKG", buffer))return(OPBKG );
+ if(0==strcmp("CLR", buffer))return(OPCLR );
+ if(0==strcmp("DCLR", buffer))return(OPDCLR );
if(0==strcmp("BCLR",buffer))return(OPBCLR );
if(0==strcmp("FLAG",buffer))return(OPFLAGS);
if(0==strcmp("EMIT",buffer))return(OPEMIT);
@@ -2066,6 +2636,7 @@ int main(int argc, char *argv[]){
uint32_t utmp32;
TRCOLORREF bkcolor;
int bkmode;
+ char *fontspec;
infile=malloc(strlen(argv[1])+1);
strcpy(infile,argv[1]);
@@ -2084,11 +2655,14 @@ int main(int argc, char *argv[]){
printf(" MUL:(float, multiplicative factor to convert FS,XY units to points).\n");
printf(" ITA:(Italics, 0=normal, 100=italics, 110=oblique).\n");
printf(" WGT:(Weight, 0-215: 80=normal, 200=bold, 215=ultrablack, 0=thin)).\n");
- printf(" DEC:(Decorate, 00 none, 01 underline, 02 overline, 04 blink, 08 strike1, 10 strike2. SVG only supports some, and only one at a time.)\n");
+ printf(" DEC:(this is a bit field. For color see DCLR\n");
+ printf(" style: 000 none, 001 underline,002 overline, 004 blink, 008 strike-through\n");
+ printf(" line: 000 solid, 010 double, 020 dotted, 040 dashed, 080 wavy)\n");
printf(" CND:(Condensed 50-200: 100=normal, 50=ultracondensed, 75=condensed, 200=expanded).\n");
- printf(" CLR:(Text RGB color, as 6 HEX digits, like: FF0000 (red) or 0000FF (blue)) \n");
printf(" BKG:(Background color: 0 none, 1 by input fragment, 2 by assembled line, 3 by entire assembly. Use BCLR, THEN BKG) \n");
- printf(" BCLR:(Background RGB color, as 6 HEX digits, like: FF0000 (red) or 0000FF (blue)) \n");
+ printf(" CLR:(Text RGB color, as 6 HEX digits, like: FF0000 (red) or 0000FF (blue)) \n");
+ printf(" DCLR:(Decoration color, specify like CLR, except 1000000 or higher disables.)\n");
+ printf(" BCLR:(Background RGB color, specify like CLR.) \n");
printf(" FLAG: Special processing options. 1 EMF compatible text alignment.\n");
printf(" EMIT:(Process everything up to this point, then start clean for remaining input).\n");
printf(" DONE:(no more input, process it).\n");
@@ -2096,7 +2670,7 @@ int main(int argc, char *argv[]){
printf("\n");
printf(" The output is a summary of how the pieces are to be assembled into complex text.\n");
printf("\n");
- printf(" egrep pattern: '^LOAD:|^FONT:|^ESC:|^ORI:|^FS:|^XY:|^TEXT:|^ALN:|^LDIR:|^MUL:|^ITA:|^WGT:|^CND:|^BKG:|^BCLR:|^CLR:|^FLAG:|^EMIT:^DONE:'\n");
+ printf(" egrep pattern: '^LOAD:|^FONT:|^ESC:|^ORI:|^FS:|^XY:|^TEXT:|^ALN:|^LDIR:|^MUL:|^ITA:|^WGT:|^DEC:|^CND:|^BKG:|^CLR:|^BCLR:|^DCLR:|^FLAG:|^EMIT:^DONE:'\n");
exit(EXIT_FAILURE);
}
@@ -2117,17 +2691,22 @@ int main(int argc, char *argv[]){
tsp.vadvance = 0.0; /* meaningful only when a complex contains two or more lines */
tsp.taln = ALILEFT + ALIBASE;
tsp.ldir = LDIR_LR;
- tsp.color.Red = 0; /* RGBA Black */
- tsp.color.Green = 0; /* RGBA Black */
- tsp.color.Blue = 0; /* RGBA Black */
- tsp.color.Reserved = 0; /* unused */
+ tsp.color.Red = tsp.decColor.Red = 0; /* RGBA Black */
+ tsp.color.Green = tsp.decColor.Green = 0; /* RGBA Black */
+ tsp.color.Blue = tsp.decColor.Blue = 0; /* RGBA Black */
+ tsp.color.Reserved = tsp.decColor.Reserved = 0; /* unused */
tsp.italics = 0;
tsp.weight = 80;
tsp.condensed = 100;
+ tsp.decoration = 0; /* none */
tsp.co = 0;
tsp.fi_idx = -1; /* set to an invalid */
+ tsp.rt_tidx = -1; /* set to an invalid */
+ tsp.xkern = tsp.ykern = 0.0;
/* no need to set rt_tidx */
+
+
if(!tri){
fprintf(stderr,"Fatal error, could not initialize data structures\n");
exit(EXIT_FAILURE);
@@ -2155,7 +2734,9 @@ int main(int argc, char *argv[]){
else {
tsp.co=0;
}
- if(trinfo_load_fontname(tri, (uint8_t *) data, &tsp))boom("Font load failed",lineno);
+ fontspec = TR_construct_fontspec(&tsp, data);
+ if((tsp.fi_idx = ftinfo_load_fontname(tri->fti, fontspec)) < 0 )boom("Font load failed",lineno);
+ free(fontspec);
break;
case OPESC:
if(1 != sscanf(data,"%lf",&escapement))boom("Invalid ESC:",lineno);
@@ -2218,11 +2799,15 @@ int main(int argc, char *argv[]){
if(1 != sscanf(data,"%d",&tsp.weight) || tsp.weight < 0 || tsp.weight > 215)boom("Invalid WGT:",lineno);
break;
case OPDEC:
- if(1 != sscanf(data,"%X",&tsp.decoration))boom("Invalid DEC:",lineno);
+ if(1 != sscanf(data,"%X",(unsigned int *) &tsp.decoration))boom("Invalid DEC:",lineno);
break;
case OPCND:
if(1 != sscanf(data,"%d",&tsp.condensed) || tsp.condensed < 50 || tsp.condensed > 200)boom("Invalid CND:",lineno);
break;
+ case OPBKG:
+ if(1 != sscanf(data,"%d",&bkmode) )boom("Invalid BKG:",lineno);
+ (void) trinfo_load_bk(tri,bkmode,bkcolor);
+ break;
case OPCLR:
if(1 != sscanf(data,"%x",&utmp32) )boom("Invalid CLR:",lineno);
tsp.color.Red = (utmp32 >> 16) & 0xFF;
@@ -2230,9 +2815,19 @@ int main(int argc, char *argv[]){
tsp.color.Blue = (utmp32 >> 0) & 0xFF;
tsp.color.Reserved = 0;
break;
- case OPBKG:
- if(1 != sscanf(data,"%d",&bkmode) )boom("Invalid BKG:",lineno);
- (void) trinfo_load_bk(tri,bkmode,bkcolor);
+ case OPDCLR:
+ if(1 != sscanf(data,"%x",&utmp32) )boom("Invalid DCLR:",lineno);
+ if(utmp32 >= 0x1000000){
+ tsp.decColor.Red = tsp.decColor.Green = tsp.decColor.Blue = tsp.decColor.Reserved = 0;
+ tsp.decoration &= ~TXTDECOR_CLRSET;
+ }
+ else {
+ tsp.decColor.Red = (utmp32 >> 16) & 0xFF;
+ tsp.decColor.Green = (utmp32 >> 8) & 0xFF;
+ tsp.decColor.Blue = (utmp32 >> 0) & 0xFF;
+ tsp.decColor.Reserved = 0;
+ tsp.decoration |= TXTDECOR_CLRSET;
+ }
break;
case OPBCLR:
if(1 != sscanf(data,"%x",&utmp32) )boom("Invalid BCLR:",lineno);
diff --git a/src/extension/internal/text_reassemble.h b/src/extension/internal/text_reassemble.h
index 950ce1310..d85b233be 100644
--- a/src/extension/internal/text_reassemble.h
+++ b/src/extension/internal/text_reassemble.h
@@ -4,8 +4,8 @@
See text_reassemble.c for notes
File: text_reassemble.h
-Version: 0.0.7
-Date: 12-FEB-2013
+Version: 0.0.12
+Date: 14-MAY-2013
Author: David Mathog, Biology Division, Caltech
email: mathog@caltech.edu
Copyright: 2013 David Mathog and California Institute of Technology (Caltech)
@@ -56,18 +56,33 @@ extern "C" {
/** \defgroup decoration options
One of these values may be present in the decoration field.
Unused bits may be used by end user code.
- SVG output can specify up to STRIKE1.
+ These values are SVG specific. Other applications could use the text
+ decoration field for a different set of bits, so long as it provided its own
+ output function.
@{
*/
-#define TXTDECOR_NONE 0x00 /**< text is not decorated (default) */
-#define TXTDECOR_UNDER 0x01 /**< underlined */
-#define TXTDECOR_OVER 0x02 /**< overlined */
-#define TXTDECOR_BLINK 0x04 /**< blinking text */
-#define TXTDECOR_STRIKE1 0x08 /**< single strike throug */
-#define TXTDECOR_STRIKE2 0x10 /**< double strike through */
+#define TXTDECOR_NONE 0x000 /**< text is not decorated (default) */
+#define TXTDECOR_UNDER 0x001 /**< underlined */
+#define TXTDECOR_OVER 0x002 /**< overlined */
+#define TXTDECOR_BLINK 0x004 /**< blinking text */
+#define TXTDECOR_STRIKE 0x008 /**< strike through */
+#define TXTDECOR_TMASK 0x00F /**< Mask for selecting bits above */
+
+#define TXTDECOR_SOLID 0x000 /**< draw as single solid line */
+#define TXTDECOR_DOUBLE 0x010 /**< draw as double solid line */
+#define TXTDECOR_DOTTED 0x020 /**< draw as single dotted line */
+#define TXTDECOR_DASHED 0x040 /**< draw as single dashed line */
+#define TXTDECOR_WAVY 0x080 /**< draw as single wavy line */
+#define TXTDECOR_LMASK 0x0F0 /**< Mask for selecting these bits */
+
+#define TXTDECOR_CLRSET 0x100 /**< decoration has its own color */
+
/** @} */
+
+
+
/** \defgroup text alignment types
Location of text's {X,Y} coordinate on bounding rectangle.
Values are compatible with Fontconfig.
@@ -113,15 +128,27 @@ enum tr_classes {
/** @} */
/**
+ \brief alt font entries.
+*/
+typedef struct {
+ uint32_t fi_idx; /**< index into FT_INFO fonts, for fonts added for missing glyphs */
+ uint32_t weight; /**< integer weight for alt fonts, kept sorted into descending order */
+} ALT_SPECS;
+
+/**
\brief Information for a font instance.
*/
typedef struct {
+ FcFontSet *fontset; /**< all matching fonts (for fallback on missing glyphs) */
+ ALT_SPECS *alts; /**< index into FT_INFO fonts, for fonts added for missing glyphs */
+ uint32_t space; /**< alts storage slots allocated */
+ uint32_t used; /**< alts storage slots in use */
FT_Face face; /**< font face structures (FT_FACE is a pointer!) */
- uint8_t *file; /**< pointers to font paths to files */
- uint8_t *fname; /**< pointers to font names */
- FcPattern *fpat; /**< must hang onto this or faces operations break */
- double spcadv; /**< advance equal to a space, in points */
- double fsize; /**< face size in points */
+ uint8_t *file; /**< pointer to font paths to files */
+ uint8_t *fontspec; /**< pointer to a font specification (name:italics, etc.) */
+ FcPattern *fpat; /**< current font, must hang onto this or faces operations break */
+ double spcadv; /**< advance equal to a space, in points at font's face size */
+ double fsize; /**< font's face size in points */
} FNT_SPECS;
/**
@@ -130,8 +157,8 @@ typedef struct {
typedef struct {
FT_Library library; /**< Fontconfig handle */
FNT_SPECS *fonts; /**< Array of fontinfo structures */
- int space; /**< storage slots allocated */
- int used; /**< storage slots in use */
+ uint32_t space; /**< storage slots allocated */
+ uint32_t used; /**< storage slots in use */
} FT_INFO;
typedef struct {
@@ -150,6 +177,8 @@ typedef struct {
double fs; /**< font size of text */
double x; /**< x coordinate, relative to TR_INFO x,y, in points */
double y; /**< y coordinate, relative to TR_INFO x,y, in points */
+ double xkern; /**< x kern relative to preceding text chunk in complex (if any) */
+ double ykern; /**< y kern relative to preceding text chunk in complex (if any) */
double boff; /**< Y LL corner - boff finds baseline */
double vadvance; /**< Line spacing typically 1.25 or 1.2, only set on the first text
element in a complex */
@@ -159,7 +188,8 @@ typedef struct {
int italics; /**< italics, as in FontConfig */
int weight; /**< weight, as in FontConfig */
int condensed; /**< condensed, as in FontConfig */
- int decoration; /**< text decorations, ignored during assembly,used during output */
+ int decoration; /**< text decorations, ignored during assembly, used during output */
+ TRCOLORREF decColor; /**< text decoration color, ignored during assembly, used during output */
int co; /**< condensed override, if set Font name included narrow */
int rt_tidx; /**< index of rectangle that contains it */
int fi_idx; /**< index of the font it uses */
@@ -171,8 +201,8 @@ typedef struct {
*/
typedef struct {
TCHUNK_SPECS *chunks; /**< text chunks */
- int space; /**< storage slots allocated */
- int used; /**< storage slots in use */
+ uint32_t space; /**< storage slots allocated */
+ uint32_t used; /**< storage slots in use */
} TP_INFO;
/**
@@ -192,8 +222,8 @@ typedef struct {
*/
typedef struct {
BRECT_SPECS *rects; /**< bounding rectangles */
- int space; /**< storage slots allocated */
- int used; /**< storage slots in use */
+ uint32_t space; /**< storage slots allocated */
+ uint32_t used; /**< storage slots in use */
} BR_INFO;
/**
@@ -203,8 +233,8 @@ typedef struct {
int *members; /**< array of immediate children (for TR_PARA_* these are indicies
for TR_TEXT or TR_LINE complexes also in cxi. For TR_TEXT
and TR_LINE these are indices to the actual text in tpi.) */
- int space; /**< storage slots allocated */
- int used; /**< storage slots in use */
+ uint32_t space; /**< storage slots allocated */
+ uint32_t used; /**< storage slots in use */
} CHILD_SPECS;
/**
@@ -222,11 +252,11 @@ typedef struct {
*/
typedef struct {
CX_SPECS *cx; /**< complexes */
- int space; /**< storage slots allocated */
- int used; /**< storage slots in use */
- int phase1; /**< Number of complexes (lines + text fragments) entered in phase 1 */
- int lines; /**< Number of lines in phase 1 */
- int paras; /**< Number of complexes (paras) entered in phase 2 */
+ uint32_t space; /**< storage slots allocated */
+ uint32_t used; /**< storage slots in use */
+ uint32_t phase1; /**< Number of complexes (lines + text fragments) entered in phase 1 */
+ uint32_t lines; /**< Number of lines in phase 1 */
+ uint32_t paras; /**< Number of complexes (paras) entered in phase 2 */
} CX_INFO;
/**
@@ -246,8 +276,8 @@ typedef struct {
int use_kern; /**< 1 if kerning is used, 0 if not */
int load_flags; /**< FT_LOAD_NO_SCALE or FT_LOAD_TARGET_NORMAL */
int kern_mode; /**< FT_KERNING_DEFAULT, FT_KERNING_UNFITTED, or FT_KERNING_UNSCALED */
- int outspace; /**< storage in output buffer allocated */
- int outused; /**< storage in output buffer in use */
+ uint32_t outspace; /**< storage in output buffer allocated */
+ uint32_t outused; /**< storage in output buffer in use */
int usebk; /**< On output write the background color under the text */
TRCOLORREF bkcolor; /**< RGB background color */
} TR_INFO;
@@ -279,8 +309,11 @@ typedef struct {
/** \endcond */
/* Prototypes */
-int TR_findcasesub(char *string, char *sub);
-int TR_getadvance(FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int load_flags, int kern_mode, int *ymin, int *ymax);
+int TR_findcasesub(const char *string, const char *sub);
+char *TR_construct_fontspec(const TCHUNK_SPECS *tsp, const char *fontname);
+char *TR_reconstruct_fontspec(const char *fontspec, const char *fontname);
+int TR_find_alternate_font(FT_INFO *fti, FNT_SPECS **efsp, uint32_t wc);
+int TR_getadvance(FT_INFO *fti, FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int load_flags, int kern_mode, int *ymin, int *ymax);
int TR_getkern2(FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int kern_mode);
int TR_kern_gap(FNT_SPECS *fsp, TCHUNK_SPECS *tsp, TCHUNK_SPECS *ptsp, int kern_mode);
void TR_rt_pad_set(RT_PAD *rt_pad, double up, double down, double left, double right);
@@ -295,45 +328,57 @@ int ftinfo_make_insertable(FT_INFO *fti);
int ftinfo_insert(FT_INFO *fti, FNT_SPECS *fsp);
FT_INFO *ftinfo_release(FT_INFO *fti);
FT_INFO *ftinfo_clear(FT_INFO *fti);
+int ftinfo_find_loaded_by_spec(const FT_INFO *fti, const uint8_t *fname);
+int ftinfo_find_loaded_by_src(const FT_INFO *fti, const uint8_t *filename);
+int ftinfo_load_fontname(FT_INFO *fti, const char *fontspec);
+void ftinfo_dump(const FT_INFO *fti);
+
+int fsp_alts_make_insertable(FNT_SPECS *fsp);
+int fsp_alts_insert(FNT_SPECS *fsp, uint32_t fi_idx);
+int fsp_alts_weight(FNT_SPECS *fsp, uint32_t a_idx);
int csp_make_insertable(CHILD_SPECS *csp);
int csp_insert(CHILD_SPECS *csp, int src);
int csp_merge(CHILD_SPECS *dst, CHILD_SPECS *src);
void csp_release(CHILD_SPECS *csp);
+void csp_clear(CHILD_SPECS *csp);
CX_INFO *cxinfo_init(void);
int cxinfo_make_insertable(CX_INFO *cxi);
int cxinfo_insert(CX_INFO *cxi, int src, int src_rt_idx, enum tr_classes type);
int cxinfo_append(CX_INFO *cxi, int src, enum tr_classes type);
int cxinfo_merge(CX_INFO *cxi, int dst, int src, enum tr_classes type);
+int cxinfo_trim(CX_INFO *cxi);
CX_INFO *cxinfo_release(CX_INFO *cxi);
-void cxinfo_dump(TR_INFO *tri);
+void cxinfo_dump(const TR_INFO *tri);
TP_INFO *tpinfo_init(void);
int tpinfo_make_insertable(TP_INFO *tpi);
-int tpinfo_insert(TP_INFO *tpi, TCHUNK_SPECS *tsp);
+int tpinfo_insert(TP_INFO *tpi, const TCHUNK_SPECS *tsp);
TP_INFO *tpinfo_release(TP_INFO *tpi);
BR_INFO *brinfo_init(void);
int brinfo_make_insertable(BR_INFO *bri);
-int brinfo_insert(BR_INFO *bri, BRECT_SPECS *element);
+int brinfo_insert(BR_INFO *bri, const BRECT_SPECS *element);
int brinfo_merge(BR_INFO *bri, int dst, int src);
enum tr_classes
- brinfo_pp_alignment(BR_INFO *bri, int dst, int src, double slop, enum tr_classes type);
-int brinfo_overlap(BR_INFO *bri, int dst, int src, RT_PAD *rp_dst, RT_PAD *rp_src);
+ brinfo_pp_alignment(const BR_INFO *bri, int dst, int src, double slop, enum tr_classes type);
+int brinfo_overlap(const BR_INFO *bri, int dst, int src, RT_PAD *rp_dst, RT_PAD *rp_src);
BR_INFO *brinfo_release(BR_INFO *bri);
TR_INFO *trinfo_init(TR_INFO *tri);
TR_INFO *trinfo_release(TR_INFO *tri);
TR_INFO *trinfo_release_except_FC(TR_INFO *tri);
TR_INFO *trinfo_clear(TR_INFO *tri);
-int trinfo_load_fontname(TR_INFO *tri, uint8_t *fontname, TCHUNK_SPECS *tsp);
int trinfo_load_qe(TR_INFO *tri, double qe);
int trinfo_load_bk(TR_INFO *tri, int usebk, TRCOLORREF bkcolor);
int trinfo_load_ft_opts(TR_INFO *tri, int use_kern, int load_flags, int kern_mode);
-int trinfo_load_textrec(TR_INFO *tri, TCHUNK_SPECS *tsp, double escapement, int flags);
+int trinfo_load_textrec(TR_INFO *tri, const TCHUNK_SPECS *tsp, double escapement, int flags);
int trinfo_check_bk(TR_INFO *tri, int usebk, TRCOLORREF bkcolor);
-int trinfo_append_out(TR_INFO *tri, char *src);
+int trinfo_append_out(TR_INFO *tri, const char *src);
+
+int is_mn_unicode(int test);
+
#ifdef __cplusplus
}
diff --git a/src/extension/internal/wmf-inout.cpp b/src/extension/internal/wmf-inout.cpp
index 56757d0d0..4c69c76a0 100644
--- a/src/extension/internal/wmf-inout.cpp
+++ b/src/extension/internal/wmf-inout.cpp
@@ -1278,8 +1278,10 @@ Wmf::select_font(PWMF_CALLBACK_DATA d, int index)
font.Weight == U_FW_EXTRABOLD ? SP_CSS_FONT_WEIGHT_BOLDER :
U_FW_NORMAL;
d->dc[d->level].style.font_style.value = (font.Italic ? SP_CSS_FONT_STYLE_ITALIC : SP_CSS_FONT_STYLE_NORMAL);
- d->dc[d->level].style.text_decoration.underline = font.Underline;
- d->dc[d->level].style.text_decoration.line_through = font.StrikeOut;
+ d->dc[d->level].style.text_decoration_line.underline = font.Underline;
+ d->dc[d->level].style.text_decoration_line.line_through = font.StrikeOut;
+ d->dc[d->level].style.text_decoration_line.set = true;
+ d->dc[d->level].style.text_decoration_line.inherit = false;
// malformed WMF with empty filename may exist, ignore font change if encountered
if(d->dc[d->level].font_name)free(d->dc[d->level].font_name);
@@ -1325,13 +1327,13 @@ Wmf::delete_object(PWMF_CALLBACK_DATA d, int index)
else if(index == d->dc[d->level].active_font){
d->dc[d->level].active_font = -1;
if(d->dc[d->level].font_name){ free(d->dc[d->level].font_name);}
- d->dc[d->level].font_name = strdup("Arial"); // Default font, WMF spec says device can pick whatever it wants
- d->dc[d->level].style.font_size.computed = 16.0;
- d->dc[d->level].style.font_weight.value = SP_CSS_FONT_WEIGHT_400;
- d->dc[d->level].style.font_style.value = SP_CSS_FONT_STYLE_NORMAL;
- d->dc[d->level].style.text_decoration.underline = 0;
- d->dc[d->level].style.text_decoration.line_through = 0;
- d->dc[d->level].style.baseline_shift.value = 0;
+ d->dc[d->level].font_name = strdup("Arial"); // Default font, WMF spec says device can pick whatever it wants
+ d->dc[d->level].style.font_size.computed = 16.0;
+ d->dc[d->level].style.font_weight.value = SP_CSS_FONT_WEIGHT_400;
+ d->dc[d->level].style.font_style.value = SP_CSS_FONT_STYLE_NORMAL;
+ d->dc[d->level].style.text_decoration_line.underline = 0;
+ d->dc[d->level].style.text_decoration_line.line_through = 0;
+ d->dc[d->level].style.baseline_shift.value = 0;
}
@@ -2522,6 +2524,7 @@ std::cout << "BEFORE DRAW"
dbg_str << "<!-- U_WMR_EXTTEXTOUT -->\n";
nSize = U_WMREXTTEXTOUT_get(contents, &Dst, &tlen, &Opts, &text, &dx, &rc );
}
+ uint32_t fOptions = Opts;
double x1,y1;
int cChars;
@@ -2597,11 +2600,10 @@ std::cout << "BEFORE DRAW"
case SP_CSS_FONT_WEIGHT_BOLDER: tsp.weight = FC_WEIGHT_EXTRABOLD ; break;
default: tsp.weight = FC_WEIGHT_NORMAL ; break;
}
-
- // Inkscape cannot display underline or strike-through at present, but enter it into the SVG in any case.
- if( d->dc[d->level].style.text_decoration.underline){ tsp.decoration = TXTDECOR_UNDER; }
- else if (d->dc[d->level].style.text_decoration.line_through){ tsp.decoration = TXTDECOR_STRIKE1; }
- else { tsp.decoration = TXTDECOR_NONE; }
+ // WMF only supports two types of text decoration
+ tsp.decoration = TXTDECOR_NONE;
+ if(d->dc[d->level].style.text_decoration_line.underline){ tsp.decoration |= TXTDECOR_UNDER; }
+ if(d->dc[d->level].style.text_decoration_line.line_through){ tsp.decoration |= TXTDECOR_STRIKE;}
// WMF textalignment is a bit strange: 0x6 is center, 0x2 is right, 0x0 is left, the value 0x4 is also drawn left
tsp.taln = ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_CENTER) ? ALICENTER :
@@ -2610,13 +2612,19 @@ std::cout << "BEFORE DRAW"
tsp.taln |= ((d->dc[d->level].textAlign & U_TA_BASEBIT) ? ALIBASE :
((d->dc[d->level].textAlign & U_TA_BOTTOM) ? ALIBOT :
ALITOP));
- tsp.ldir = (d->dc[d->level].textAlign & U_TA_RTLREADING ? LDIR_RL : LDIR_LR); // language direction
+
+ // language direction can be encoded two ways, U_TA_RTLREADING is preferred
+ if( (fOptions & U_ETO_RTLREADING) || (d->dc[d->level].textAlign & U_TA_RTLREADING) ){ tsp.ldir = LDIR_RL; }
+ else{ tsp.ldir = LDIR_LR; }
+
tsp.condensed = FC_WIDTH_NORMAL; // Not implemented well in libTERE (yet)
tsp.ori = d->dc[d->level].style.baseline_shift.value; // For now orientation is always the same as escapement
// There is no world transform, so ori need not be further rotated
tsp.string = (uint8_t *) U_strdup(escaped_text); // this will be free'd much later at a trinfo_clear().
tsp.fs = d->dc[d->level].style.font_size.computed * 0.8; // Font size in points
- (void) trinfo_load_fontname(d->tri, (uint8_t *)d->dc[d->level].font_name, &tsp);
+ char *fontspec = TR_construct_fontspec(&tsp, d->dc[d->level].font_name);
+ tsp.fi_idx = ftinfo_load_fontname(d->tri->fti,fontspec);
+ free(fontspec);
// when font name includes narrow it may not be set to "condensed". Narrow fonts do not work well anyway though
// as the metrics from fontconfig may not match, or the font may not be present.
if(0<= TR_findcasesub(d->dc[d->level].font_name, (char *) "Narrow")){ tsp.co=1; }
@@ -3074,8 +3082,8 @@ Wmf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri )
d.dc[0].style.font_size.computed = 16.0;
d.dc[0].style.font_weight.value = SP_CSS_FONT_WEIGHT_400;
d.dc[0].style.font_style.value = SP_CSS_FONT_STYLE_NORMAL;
- d.dc[0].style.text_decoration.underline = 0;
- d.dc[0].style.text_decoration.line_through = 0;
+ d.dc[0].style.text_decoration_line.underline = 0;
+ d.dc[0].style.text_decoration_line.line_through = 0;
d.dc[0].style.baseline_shift.value = 0;
d.dc[0].textColor = U_RGB(0, 0, 0); // default foreground color (black)
d.dc[0].bkColor = U_RGB(255, 255, 255); // default background color (white)
diff --git a/src/extension/internal/wmf-print.cpp b/src/extension/internal/wmf-print.cpp
index 5ac8d46eb..e91a74b20 100644
--- a/src/extension/internal/wmf-print.cpp
+++ b/src/extension/internal/wmf-print.cpp
@@ -211,7 +211,7 @@ void PrintWmf::search_short_fflist(const char *fontname, double *f1, double *f2
search_long_fflist(fontname, f1, f2, f3);
}
-void PrintWmf::smuggle_adxky_out(const char *string, int16_t **adx, double *ky, int *ndx, float scale){
+void PrintWmf::smuggle_adxky_out(const char *string, int16_t **adx, double *ky, int *rtl, int *ndx, float scale){
float fdx;
int i;
int16_t *ladx;
@@ -232,6 +232,8 @@ void PrintWmf::smuggle_adxky_out(const char *string, int16_t **adx, double *ky,
cptr++; // skip 2nd fake terminator
sscanf(cptr,"%7f",&fdx);
*ky=fdx;
+ cptr += 7; // advance over ky and its space
+ sscanf(cptr,"%07d",rtl);
}
/* convert an 0RGB color to EMF U_COLORREF.
@@ -307,6 +309,7 @@ unsigned int PrintWmf::begin (Inkscape::Extension::Print *mod, SPDocument *doc)
// initialize a few global variables
hbrush = hpen = 0;
+ htextalignment = U_TA_BASELINE | U_TA_LEFT;
use_stroke = use_fill = simple_shape = usebk = false;
Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
@@ -383,19 +386,19 @@ unsigned int PrintWmf::begin (Inkscape::Extension::Print *mod, SPDocument *doc)
g_error("Fatal programming error in PrintWmf::begin at U_WMRSETPOLYFILLMODE");
}
- // Text alignment: (Never changes)
+ // Text alignment: (only changed if RTL text is encountered )
// - (x,y) coordinates received by this filter are those of the point where the text
// actually starts, and already takes into account the text object's alignment;
// - for this reason, the WMF text alignment must always be TA_BASELINE|TA_LEFT.
rec = U_WMRSETTEXTALIGN_set(U_TA_BASELINE | U_TA_LEFT);
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
- g_error("Fatal programming error in PrintWmf::text at U_WMRSETTEXTALIGN_set");
+ g_error("Fatal programming error in PrintWmf::begin at U_WMRSETTEXTALIGN_set");
}
htextcolor_rgb[0] = htextcolor_rgb[1] = htextcolor_rgb[2] = 0.0;
rec = U_WMRSETTEXTCOLOR_set(U_RGB(0,0,0));
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
- g_error("Fatal programming error in PrintWmf::text at U_WMRSETTEXTCOLOR_set");
+ g_error("Fatal programming error in PrintWmf::begin at U_WMRSETTEXTCOLOR_set");
}
rec = U_WMRSETROP2_set(U_R2_COPYPEN);
@@ -1273,8 +1276,8 @@ unsigned int PrintWmf::fill(
}
}
if (
- (style->stroke.noneSet || style->stroke_width.computed == 0.0) ||
- (style->stroke_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) ||
+ (style->stroke.isNone() || style->stroke.noneSet || style->stroke_width.computed == 0.0) ||
+ (style->stroke_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) ||
!all_closed
){
print_pathv(pathv, fill_transform); // do any fills. side effect: clears fill_pathv
@@ -1767,10 +1770,21 @@ unsigned int PrintWmf::text(Inkscape::Extension::Print * /*mod*/, char const *te
double ky;
// the dx array is smuggled in like: text<nul>w1 w2 w3 ...wn<nul><nul>, where the widths are floats 7 characters wide, including the space
- int ndx;
+ int ndx, rtl;
int16_t *adx;
- smuggle_adxky_out(text, &adx, &ky, &ndx, PX2WORLD * std::min(tf.expansionX(),tf.expansionY())); // side effect: free() adx
+ smuggle_adxky_out(text, &adx, &ky, &rtl, &ndx, PX2WORLD * std::min(tf.expansionX(),tf.expansionY())); // side effect: free() adx
+ uint32_t textalignment;
+ if(rtl > 0){ textalignment = U_TA_BASELINE | U_TA_LEFT; }
+ else { textalignment = U_TA_BASELINE | U_TA_RIGHT | U_TA_RTLREADING; }
+ if(textalignment != htextalignment){
+ htextalignment = textalignment;
+ rec = U_WMRSETTEXTALIGN_set(textalignment);
+ if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
+ g_error("Fatal programming error in PrintWmf::text at U_WMRSETTEXTALIGN_set");
+ }
+ }
+
char *text2 = strdup(text); // because U_Utf8ToUtf16le calls iconv which does not like a const char *
uint16_t *unicode_text = U_Utf8ToUtf16le( text2, 0, NULL );
free(text2);
@@ -1838,8 +1852,8 @@ unsigned int PrintWmf::text(Inkscape::Extension::Print * /*mod*/, char const *te
round(rot),
transweight(style->font_weight.computed),
(style->font_style.computed == SP_CSS_FONT_STYLE_ITALIC),
- style->text_decoration.underline,
- style->text_decoration.line_through,
+ style->text_decoration_line.underline,
+ style->text_decoration_line.line_through,
U_DEFAULT_CHARSET,
U_OUT_DEFAULT_PRECIS,
U_CLIP_DEFAULT_PRECIS,
@@ -1922,7 +1936,12 @@ unsigned int PrintWmf::text(Inkscape::Extension::Print * /*mod*/, char const *te
// This is currently being smuggled in from caller as part of text, works
// MUCH better than the fallback hack below
// uint32_t *adx = dx_set(textheight, U_FW_NORMAL, slen); // dx is needed, this makes one up
- rec = U_WMREXTTEXTOUT_set((U_POINT16) {xpos, ypos}, ndx, U_ETO_NONE, latin1_text, adx, U_RCL16_DEF);
+ if(rtl>0){
+ rec = U_WMREXTTEXTOUT_set((U_POINT16) {xpos, ypos}, ndx, U_ETO_NONE, latin1_text, adx, U_RCL16_DEF);
+ }
+ else { // RTL text, U_TA_RTLREADING should be enough, but set this one too just in case
+ rec = U_WMREXTTEXTOUT_set((U_POINT16) {xpos, ypos}, ndx, U_ETO_RTLREADING, latin1_text, adx, U_RCL16_DEF);
+ }
free(latin1_text);
free(adx);
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
diff --git a/src/extension/internal/wmf-print.h b/src/extension/internal/wmf-print.h
index 02749c9de..5fe316417 100644
--- a/src/extension/internal/wmf-print.h
+++ b/src/extension/internal/wmf-print.h
@@ -38,6 +38,7 @@ class PrintWmf : public Inkscape::Extension::Implementation::Implementation
double _height;
U_RECTL rc;
+ uint32_t htextalignment;
uint32_t hbrush, hpen, hpenOld, hbrush_null, hpen_null;
uint32_t hmiterlimit; // used to minimize redundant records that set this
uint32_t hpolyfillmode; // used to minimize redundant records that set this
@@ -94,7 +95,7 @@ protected:
static void read_system_fflist(void); //this is not called by any other source files
static void search_long_fflist(const char *fontname, double *f1, double *f2, double *f3);
static void search_short_fflist(const char *fontname, double *f1, double *f2, double *f3);
- static void smuggle_adxky_out(const char *string, int16_t **adx, double *ky, int *ndx, float scale);
+ static void smuggle_adxky_out(const char *string, int16_t **adx, double *ky, int *rtl, int *ndx, float scale);
U_COLORREF gethexcolor(uint32_t color);
uint32_t transweight(const unsigned int inkweight);
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);
diff --git a/src/sp-image.cpp b/src/sp-image.cpp
index d60fbc181..10d294d5c 100644
--- a/src/sp-image.cpp
+++ b/src/sp-image.cpp
@@ -1056,6 +1056,7 @@ static void sp_image_print( SPItem *item, SPPrintContext *ctx )
t = ti * t;
sp_print_image_R8G8B8A8_N(ctx, px + trimx*pixskip + trimy*rs, trimwidth, trimheight, rs, t, item->style);
}
+ free(px); // else big memory leak on each image print!
}
}
diff --git a/src/style.cpp b/src/style.cpp
index 479f30597..b1914b40d 100644
--- a/src/style.cpp
+++ b/src/style.cpp
@@ -89,7 +89,12 @@ static void sp_style_read_ienum(SPIEnum *val, gchar const *str, SPStyleEnum cons
static void sp_style_read_istring(SPIString *val, gchar const *str);
static void sp_style_read_ilength(SPILength *val, gchar const *str);
static void sp_style_read_ilengthornormal(SPILengthOrNormal *val, gchar const *str);
-static void sp_style_read_itextdecoration(SPITextDecoration *val, gchar const *str);
+
+static void sp_style_read_itextdecoration(SPITextDecorationLine *line, SPITextDecorationStyle *style, SPIPaint *color, gchar const *str);
+static void sp_style_read_itextdecorationLine(SPITextDecorationLine *line, gchar const *str);
+static void sp_style_read_itextdecorationStyle(SPITextDecorationStyle *style, gchar const *str);
+static void sp_style_read_itextdecorationColor(SPIPaint *color, gchar const *str);
+
static void sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document);
static void sp_style_read_ifontsize(SPIFontSize *val, gchar const *str);
static void sp_style_read_ibaselineshift(SPIBaselineShift *val, gchar const *str);
@@ -110,7 +115,14 @@ static gint sp_style_write_ipaint(gchar *b, gint len, gchar const *key, SPIPaint
static gint sp_style_write_ifontsize(gchar *p, gint len, gchar const *key, SPIFontSize const *val, SPIFontSize const *base, guint flags);
static gint sp_style_write_ibaselineshift(gchar *p, gint len, gchar const *key, SPIBaselineShift const *val, SPIBaselineShift const *base, guint flags);
static gint sp_style_write_ilengthornormal(gchar *p, gint const len, gchar const *const key, SPILengthOrNormal const *const val, SPILengthOrNormal const *const base, guint const flags);
-static gint sp_style_write_itextdecoration(gchar *p, gint const len, gchar const *const key, SPITextDecoration const *const val, SPITextDecoration const *const base, guint const flags);
+static gint sp_style_write_itextdecoration(gchar *p, gint const len, gchar const *const key,
+ SPITextDecorationLine const *const line,
+ SPITextDecorationStyle const *const style,
+ SPIPaint const *const color,
+ SPITextDecorationLine const *const baseLine,
+ SPITextDecorationStyle const *const baseStyle,
+ SPIPaint const *const baseColor,
+ guint const flags);
static gint sp_style_write_ifilter(gchar *b, gint len, gchar const *key, SPIFilter const *filter, SPIFilter const *base, guint flags);
static void sp_style_filter_clear(SPStyle *style);
@@ -605,7 +617,7 @@ sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr)
/* 1. Style attribute */
gchar const *val = repr->attribute("style");
- if (val != NULL) {
+ if (val != NULL && *val) {
sp_style_merge_from_style_string(style, val);
}
@@ -632,10 +644,10 @@ sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr)
/* Text (css2 chapter 16) */
SPS_READ_PLENGTH_IF_UNSET(&style->text_indent, repr, "text-indent");
SPS_READ_PENUM_IF_UNSET(&style->text_align, repr, "text-align", enum_text_align, true);
- if (!style->text_decoration.set) {
- val = repr->attribute("text-decoration");
- if (val) {
- sp_style_read_itextdecoration(&style->text_decoration, val);
+ if (!style->text_decoration_line.set) {
+ // assume it uses either text-decoration or text-decoration-line, but not both
+ if ((val = repr->attribute("text-decoration")) || (val = repr->attribute("text-decoration-line"))) {
+ sp_style_read_itextdecoration(&style->text_decoration_line, &style->text_decoration_style, &style->text_decoration_color, val);
}
}
if (!style->line_height.set) {
@@ -1090,8 +1102,23 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val)
SPS_READ_IENUM_IF_UNSET(&style->text_align, val, enum_text_align, true);
break;
case SP_PROP_TEXT_DECORATION:
- if (!style->text_decoration.set) {
- sp_style_read_itextdecoration(&style->text_decoration, val);
+ if (!style->text_decoration_line.set) {
+ sp_style_read_itextdecoration(&style->text_decoration_line, &style->text_decoration_style, &style->text_decoration_color, val);
+ }
+ break;
+ case SP_PROP_TEXT_DECORATION_LINE:
+ if (!style->text_decoration_line.set) {
+ sp_style_read_itextdecorationLine(&style->text_decoration_line, val);
+ }
+ break;
+ case SP_PROP_TEXT_DECORATION_STYLE:
+ if (!style->text_decoration_style.set) {
+ sp_style_read_itextdecorationStyle(&style->text_decoration_style, val);
+ }
+ break;
+ case SP_PROP_TEXT_DECORATION_COLOR:
+ if (!style->text_decoration_color.set) {
+ sp_style_read_itextdecorationColor(&style->text_decoration_color, val);
}
break;
case SP_PROP_LINE_HEIGHT:
@@ -1679,11 +1706,23 @@ sp_style_merge_from_parent(SPStyle *const style, SPStyle const *const parent)
style->text_align.computed = parent->text_align.computed;
}
- if (!style->text_decoration.set || style->text_decoration.inherit) {
- style->text_decoration.underline = parent->text_decoration.underline;
- style->text_decoration.overline = parent->text_decoration.overline;
- style->text_decoration.line_through = parent->text_decoration.line_through;
- style->text_decoration.blink = parent->text_decoration.blink;
+ if (!style->text_decoration_line.set || style->text_decoration_line.inherit) {
+ style->text_decoration_line.underline = parent->text_decoration_line.underline;
+ style->text_decoration_line.overline = parent->text_decoration_line.overline;
+ style->text_decoration_line.line_through = parent->text_decoration_line.line_through;
+ style->text_decoration_line.blink = parent->text_decoration_line.blink;
+ }
+
+ if (!style->text_decoration_style.set || style->text_decoration_style.inherit) {
+ style->text_decoration_style.solid = parent->text_decoration_style.solid;
+ style->text_decoration_style.isdouble = parent->text_decoration_style.isdouble;
+ style->text_decoration_style.dotted = parent->text_decoration_style.dotted;
+ style->text_decoration_style.dashed = parent->text_decoration_style.dashed;
+ style->text_decoration_style.wavy = parent->text_decoration_style.wavy;
+ }
+
+ if (!style->text_decoration_color.set || style->text_decoration_color.inherit) {
+ sp_style_merge_ipaint(style, &style->text_decoration_color, &parent->text_decoration_color);
}
if (!style->line_height.set || style->line_height.inherit) {
@@ -2169,8 +2208,9 @@ sp_style_merge_from_dying_parent(SPStyle *const style, SPStyle const *const pare
* property (fixme). This code may need changing once we do the
* special fill/stroke inheritance mentioned by the spec.
*/
- sp_style_merge_prop_from_dying_parent<SPITextDecoration>(style->text_decoration,
- parent->text_decoration);
+ sp_style_merge_prop_from_dying_parent<SPITextDecorationLine>( style->text_decoration_line, parent->text_decoration_line);
+ sp_style_merge_prop_from_dying_parent<SPITextDecorationStyle>(style->text_decoration_style, parent->text_decoration_style);
+ sp_style_merge_paint_prop_from_dying_parent(style,style->text_decoration_color, parent->text_decoration_color);
//nyi: font-size-adjust, // <number> | none | inherit
//nyi: glyph-orientation-horizontal,
@@ -2623,7 +2663,9 @@ sp_style_write_string(SPStyle const *const style, guint const flags)
/* Text */
p += sp_style_write_ilength(p, c + BMAX - p, "text-indent", &style->text_indent, NULL, flags);
p += sp_style_write_ienum(p, c + BMAX - p, "text-align", enum_text_align, &style->text_align, NULL, flags);
- p += sp_style_write_itextdecoration(p, c + BMAX - p, "text-decoration", &style->text_decoration, NULL, flags);
+ p += sp_style_write_itextdecoration(p, c + BMAX - p, "text-decoration",
+ &style->text_decoration_line, &style->text_decoration_style, &style->text_decoration_color,
+ NULL, NULL, NULL, flags);
p += sp_style_write_ilengthornormal(p, c + BMAX - p, "line-height", &style->line_height, NULL, flags);
p += sp_style_write_ilengthornormal(p, c + BMAX - p, "letter-spacing", &style->letter_spacing, NULL, flags);
p += sp_style_write_ilengthornormal(p, c + BMAX - p, "word-spacing", &style->word_spacing, NULL, flags);
@@ -2801,7 +2843,10 @@ sp_style_write_difference(SPStyle const *const from, SPStyle const *const to)
/* Text */
p += sp_style_write_ilength(p, c + BMAX - p, "text-indent", &from->text_indent, &to->text_indent, SP_STYLE_FLAG_IFDIFF);
p += sp_style_write_ienum(p, c + BMAX - p, "text-align", enum_text_align, &from->text_align, &to->text_align, SP_STYLE_FLAG_IFDIFF);
- p += sp_style_write_itextdecoration(p, c + BMAX - p, "text-decoration", &from->text_decoration, &to->text_decoration, SP_STYLE_FLAG_IFDIFF);
+ p += sp_style_write_itextdecoration(p, c + BMAX - p, "text-decoration",
+ &from->text_decoration_line, &from->text_decoration_style, &from->text_decoration_color,
+ &to->text_decoration_line, &to->text_decoration_style, &to->text_decoration_color,
+ SP_STYLE_FLAG_IFDIFF);
p += sp_style_write_ilengthornormal(p, c + BMAX - p, "line-height", &from->line_height, &to->line_height, SP_STYLE_FLAG_IFDIFF);
p += sp_style_write_ilengthornormal(p, c + BMAX - p, "letter-spacing", &from->letter_spacing, &to->letter_spacing, SP_STYLE_FLAG_IFDIFF);
p += sp_style_write_ilengthornormal(p, c + BMAX - p, "word-spacing", &from->word_spacing, &to->word_spacing, SP_STYLE_FLAG_IFDIFF);
@@ -3018,11 +3063,20 @@ sp_style_clear(SPStyle *style)
style->text_align.set = FALSE;
style->text_align.value = style->text_align.computed = SP_CSS_TEXT_ALIGN_START;
- style->text_decoration.set = FALSE;
- style->text_decoration.underline = FALSE;
- style->text_decoration.overline = FALSE;
- style->text_decoration.line_through = FALSE;
- style->text_decoration.blink = FALSE;
+ style->text_decoration_line.set = FALSE;
+ style->text_decoration_line.underline = FALSE;
+ style->text_decoration_line.overline = FALSE;
+ style->text_decoration_line.line_through = FALSE;
+ style->text_decoration_line.blink = FALSE;
+
+ style->text_decoration_style.set = FALSE;
+ style->text_decoration_style.solid = FALSE;
+ style->text_decoration_style.isdouble = FALSE;
+ style->text_decoration_style.dotted = FALSE;
+ style->text_decoration_style.dashed = FALSE;
+ style->text_decoration_style.wavy = FALSE;
+
+ style->text_decoration_color.clear();
style->line_height.set = FALSE;
style->line_height.unit = SP_CSS_UNIT_PERCENT;
@@ -3463,54 +3517,157 @@ sp_style_read_ilengthornormal(SPILengthOrNormal *val, gchar const *str)
* Set SPITextDecoration object from string.
*/
static void
-sp_style_read_itextdecoration(SPITextDecoration *val, gchar const *str)
-{
+sp_style_read_itextdecoration(SPITextDecorationLine *line, SPITextDecorationStyle *style, SPIPaint *color, gchar const *str){
+ sp_style_read_itextdecorationLine(line, str); // scans all tokens for line types
+ sp_style_read_itextdecorationStyle(style, str); // scans all tokens for style types
+ // the color routine must be fed one token at a time - if multiple colors are found the LAST one is used
+ const gchar *hstr = str;
+ while (1) {
+ if (*str == ' ' || *str == ',' || *str == '\0'){
+ int slen = str - hstr;
+ gchar *frag = g_strndup(hstr,slen+1); // only send one piece at a time, since keywords may be intermixed
+ sp_style_read_itextdecorationColor(color, frag);
+ free(frag);
+ if(color->set)break;
+ if(*str == '\0')break;
+ hstr = str + 1;
+ }
+ str++;
+ }
+}
+
+/**
+ * Set SPITextDecorationLine object from string.
+ * returns true if there was a match, false otherwise
+ */
+static void
+sp_style_read_itextdecorationLine(SPITextDecorationLine *line, gchar const *str){
if (!strcmp(str, "inherit")) {
- val->set = TRUE;
- val->inherit = TRUE;
+ line->set = true;
+ line->inherit = true;
} else if (!strcmp(str, "none")) {
- val->set = TRUE;
- val->inherit = FALSE;
- val->underline = FALSE;
- val->overline = FALSE;
- val->line_through = FALSE;
- val->blink = FALSE;
+ line->set = true;
+ line->inherit = false;
+ line->underline = false;
+ line->overline = false;
+ line->line_through = false;
+ line->blink = false;
} else {
- bool found_underline = false;
- bool found_overline = false;
+ bool found_one = false;
+ bool hit_one = false;
+
+ // CSS 2 keywords
+ bool found_underline = false;
+ bool found_overline = false;
bool found_line_through = false;
- bool found_blink = false;
- for ( ; *str ; str++ ) {
- if (*str == ' ') continue;
- if (strneq(str, "underline", 9) && (str[9] == ' ' || str[9] == '\0')) {
- found_underline = true;
- str += 9;
- } else if (strneq(str, "overline", 8) && (str[8] == ' ' || str[8] == '\0')) {
- found_overline = true;
- str += 8;
- } else if (strneq(str, "line-through", 12) && (str[12] == ' ' || str[12] == '\0')) {
- found_line_through = true;
- str += 12;
- } else if (strneq(str, "blink", 5) && (str[5] == ' ' || str[5] == '\0')) {
- found_blink = true;
- str += 5;
- } else {
- return; // invalid value
+ bool found_blink = false;
+
+ // this method ignores inlineid keys and extra delimiters, so " ,,, blink hello" will set blink and ignore hello
+ const gchar *hstr = str;
+ while (1) {
+ if (*str == ' ' || *str == ',' || *str == '\0'){
+ int slen = str - hstr;
+ // CSS 2 keywords
+ while(1){ // not really a loop, used to avoid a goto
+ hit_one = true; // most likely we will
+ if ((slen == 9) && strneq(hstr, "underline", slen)){ found_underline = true; break; }
+ if ((slen == 8) && strneq(hstr, "overline", slen)){ found_overline = true; break; }
+ if ((slen == 12) && strneq(hstr, "line-through", slen)){ found_line_through = true; break; }
+ if ((slen == 5) && strneq(hstr, "blink", slen)){ found_blink = true; break; }
+ if ((slen == 4) && strneq(hstr, "none", slen)){ break; }
+
+ hit_one = false; // whatever this thing is, we do not recognize it
+ break;
+ }
+ found_one |= hit_one;
+ if(*str == '\0')break;
+ hstr = str + 1;
}
+ str++;
}
- if (!(found_underline || found_overline || found_line_through || found_blink)) {
- return; // invalid value: empty
+ if (found_one) {
+ line->set = true;
+ line->inherit = false;
+ line->underline = found_underline;
+ line->overline = found_overline;
+ line->line_through = found_line_through;
+ line->blink = found_blink;
+ }
+ else {
+ line->set = false;
+ line->inherit = false;
}
- val->set = TRUE;
- val->inherit = FALSE;
- val->underline = found_underline;
- val->overline = found_overline;
- val->line_through = found_line_through;
- val->blink = found_blink;
}
}
/**
+ * Set SPITextDecorationStyle object from string.
+ * returns true if there was a match, false otherwise
+*/
+static void
+sp_style_read_itextdecorationStyle(SPITextDecorationStyle *style, gchar const *str){
+ if (!strcmp(str, "inherit")) {
+ style->set = true;
+ style->inherit = true;
+ } else if (!strcmp(str, "none")) {
+ style->set = true;
+ style->inherit = false;
+ style->solid = false;
+ style->isdouble = false;
+ style->dotted = false;
+ style->dashed = false;
+ style->wavy = false;
+ } else {
+ // note, these are CSS 3 keywords
+ bool found_solid = false;
+ bool found_double = false;
+ bool found_dotted = false;
+ bool found_dashed = false;
+ bool found_wavy = false;
+ bool found_one = false;
+
+ // this method ignores inlineid keys and extra delimiters, so " ,,, style hello" will set style and ignore hello
+ // if more than one style is present, the first is used
+ const gchar *hstr = str;
+ while (1) {
+ if (*str == ' ' || *str == ',' || *str == '\0'){
+ int slen = str - hstr;
+ if ( (slen == 5) && strneq(hstr, "solid", slen)){ found_solid = true; found_one = true; break; }
+ else if ((slen == 6) && strneq(hstr, "double", slen)){ found_double = true; found_one = true; break; }
+ else if ((slen == 6) && strneq(hstr, "dotted", slen)){ found_dotted = true; found_one = true; break; }
+ else if ((slen == 6) && strneq(hstr, "dashed", slen)){ found_dashed = true; found_one = true; break; }
+ else if ((slen == 4) && strneq(hstr, "wavy", slen)){ found_wavy = true; found_one = true; break; }
+ if(*str == '\0')break; // nothing more to test
+ hstr = str + 1;
+ }
+ str++;
+ }
+ if(found_one){
+ style->set = true;
+ style->inherit = false;
+ style->solid = found_solid;
+ style->isdouble = found_double;
+ style->dotted = found_dotted;
+ style->dashed = found_dashed;
+ style->wavy = found_wavy;
+ }
+ else {
+ style->set = false;
+ style->inherit = false;
+ }
+ }
+}
+
+/**
+ * Set SPIPaint object from string.
+ */
+static void
+sp_style_read_itextdecorationColor(SPIPaint *color, gchar const *str){
+ sp_style_read_icolor(color, str, NULL,NULL);
+}
+
+
+/**
* Set SPIPaint object from string containing an integer value.
* \param document Ignored
*/
@@ -3524,11 +3681,11 @@ sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocume
paint->set = TRUE;
paint->inherit = TRUE;
} else {
+ paint->inherit = FALSE;
guint32 const rgb0 = sp_svg_read_color(str, 0xff);
if (rgb0 != 0xff) {
paint->setColor(rgb0);
paint->set = TRUE;
- paint->inherit = FALSE;
}
}
}
@@ -4066,12 +4223,39 @@ sp_style_write_ilengthornormal(gchar *p, gint const len, gchar const *const key,
*
*/
static bool
-sp_textdecoration_differ(SPITextDecoration const *const a, SPITextDecoration const *const b)
+sp_textdecorationLine_differ(SPITextDecorationLine const *const a, SPITextDecorationLine const *const b)
{
- return a->underline != b->underline
- || a->overline != b->overline
- || a->line_through != b->line_through
- || a->blink != b->blink;
+ return( (a->underline != b->underline )
+ || (a->overline != b->overline )
+ || (a->line_through != b->line_through)
+ || (a->blink != b->blink )
+ );
+}
+
+/**
+ *
+ */
+static bool
+sp_textdecorationStyle_differ(SPITextDecorationStyle const *const a, SPITextDecorationStyle const *const b)
+{
+ return( (a->solid != b->solid )
+ || (a->isdouble != b->isdouble )
+ || (a->dotted != b->dotted )
+ || (a->dashed != b->dashed )
+ || (a->wavy != b->wavy )
+ );
+}
+
+/**
+ *
+ */
+static bool
+sp_textdecorationColor_differ(SPIPaint const *const a, SPIPaint const *const b)
+{
+ bool status = (a->isPaintserver() == b->isPaintserver()) &&
+ (a->colorSet == b->colorSet) &&
+ (a->currentcolor == b->currentcolor);
+ return(status);
}
/**
@@ -4079,31 +4263,55 @@ sp_textdecoration_differ(SPITextDecoration const *const a, SPITextDecoration con
*/
static gint
sp_style_write_itextdecoration(gchar *p, gint const len, gchar const *const key,
- SPITextDecoration const *const val,
- SPITextDecoration const *const base,
+ SPITextDecorationLine const *const line,
+ SPITextDecorationStyle const *const style,
+ SPIPaint const *const color,
+ SPITextDecorationLine const *const baseLine,
+ SPITextDecorationStyle const *const baseStyle,
+ SPIPaint const *const baseColor,
guint const flags)
{
Inkscape::CSSOStringStream os;
- if ((flags & SP_STYLE_FLAG_ALWAYS)
- || ((flags & SP_STYLE_FLAG_IFSET) && val->set)
- || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set
- && (!base->set || sp_textdecoration_differ(val, base))))
- {
- if (val->inherit) {
- return g_snprintf(p, len, "%s:inherit;", key);
- } else {
- os << key << ":";
- if (val->underline || val->overline || val->line_through || val->blink) {
- if (val->underline) os << " underline";
- if (val->overline) os << " overline";
- if (val->line_through) os << " line-through";
- if (val->blink) os << " blink";
- } else
- os << "none";
- os << ";";
- return g_strlcpy(p, os.str().c_str(), len);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS)
+ || ((flags & SP_STYLE_FLAG_IFSET) && line->set)
+ || ((flags & SP_STYLE_FLAG_IFDIFF) && line->set
+ && ( !baseLine->set || sp_textdecorationLine_differ(line, baseLine)))
+ || ((flags & SP_STYLE_FLAG_IFSET) && style->set)
+ || ((flags & SP_STYLE_FLAG_IFDIFF) && style->set
+ && ( !baseStyle->set || sp_textdecorationStyle_differ(style, baseStyle)))
+ || ((flags & SP_STYLE_FLAG_IFSET) && color->set)
+ || ((flags & SP_STYLE_FLAG_IFDIFF) && color->set
+ && ( !baseColor->set || sp_textdecorationColor_differ(color, baseColor)))
+ ){
+ os << key << ":";
+ if (line->inherit || style->inherit || color->inherit) {
+ os << " inherit";
+ }
+ else if (line->underline || line->overline || line->line_through || line->blink) {
+ if (line->underline) os << " underline";
+ if (line->overline) os << " overline";
+ if (line->line_through) os << " line-through";
+ if (line->blink) os << " blink";
+
+ if ( style->solid) os << " solid";
+ else if (style->isdouble) os << " double";
+ else if (style->dotted) os << " dotted";
+ else if (style->dashed) os << " dashed";
+ else if (style->wavy) os << " wavy";
+ // color, if it is set, otherwise omit it
+ if(color->set){
+ char color_buf[8];
+ sp_svg_write_color(color_buf, sizeof(color_buf), color->value.color.toRGBA32( 0 ));
+ os << " ";
+ os << color_buf;
+ }
+ }
+ else {
+ os << "none";
}
+ os << ";";
+ return g_strlcpy(p, os.str().c_str(), len);
}
return 0;
}
diff --git a/src/style.h b/src/style.h
index 0639c7402..c992e9dd2 100644
--- a/src/style.h
+++ b/src/style.h
@@ -229,8 +229,9 @@ struct SPIBaselineShift {
float computed;
};
+// CSS 2. Changes in CSS 3, where description is for TextDecorationLine, NOT TextDecoration
/// Text decoration type internal to SPStyle.
-struct SPITextDecoration {
+struct SPITextDecorationLine {
unsigned set : 1;
unsigned inherit : 1;
unsigned underline : 1;
@@ -239,6 +240,18 @@ struct SPITextDecoration {
unsigned blink : 1; // "Conforming user agents are not required to support this value." yay!
};
+// CSS3 2.2
+/// Text decoration style type internal to SPStyle.
+struct SPITextDecorationStyle {
+ unsigned set : 1;
+ unsigned inherit : 1;
+ unsigned solid : 1;
+ unsigned isdouble : 1; // cannot use "double" as it is a reserved keyword
+ unsigned dotted : 1;
+ unsigned dashed : 1;
+ unsigned wavy : 1;
+};
+
/// Extended length type internal to SPStyle.
struct SPILengthOrNormal {
unsigned set : 1;
@@ -249,6 +262,21 @@ struct SPILengthOrNormal {
float computed;
};
+// These are used to implement text_decoration. The values are not saved to or read from SVG file
+struct SPITextDecorationData {
+ float phase_length; // length along text line,used for phase for dot/dash/wavy
+ bool tspan_line_start; // is first span on a line
+ bool tspan_line_end; // is last span on a line
+ float tspan_width; // from libnrtype, when it calculates spans
+ float ascender; // the rest from tspan's font
+ float descender;
+ float line_gap;
+ float underline_thickness;
+ float underline_position;
+ float line_through_thickness;
+ float line_through_position;
+};
+
struct SPTextStyle;
/// Stroke dash details.
@@ -289,8 +317,18 @@ struct SPStyle {
SPILength text_indent;
/** text alignment (css2 16.2) (not to be confused with text-anchor) */
SPIEnum text_align;
- /** text decoration (css2 16.3.1) */
- SPITextDecoration text_decoration;
+ /** text decoration (css2 16.3.1) is now handled as a subset of css3 2.4 */
+ // SPITextDecoration text_decoration;
+
+ /** CSS 3 2.1, 2.2, 2.3 */
+ /** Not done yet, test_decoration3 = css3 2.4*/
+ SPITextDecorationLine text_decoration_line;
+ SPIPaint text_decoration_color;
+ SPITextDecorationStyle text_decoration_style;
+
+ // used to implement text_decoration, not saved to or read from SVG file
+ SPITextDecorationData text_decoration_data;
+
// 16.3.2 is text-shadow. That's complicated.
/** Line spacing (css2 10.8.1) */
SPILengthOrNormal line_height;
diff --git a/src/text-editing.cpp b/src/text-editing.cpp
index 401f56bb2..050917e03 100644
--- a/src/text-editing.cpp
+++ b/src/text-editing.cpp
@@ -43,7 +43,7 @@
static const gchar *tref_edit_message = _("You cannot edit <b>cloned character data</b>.");
-static bool tidy_xml_tree_recursively(SPObject *root);
+static bool tidy_xml_tree_recursively(SPObject *root, bool has_text_decoration);
Inkscape::Text::Layout const * te_get_layout (SPItem const *item)
{
@@ -774,6 +774,10 @@ sp_te_delete (SPItem *item, Inkscape::Text::Layout::iterator const &start,
SPObject *common_ancestor = get_common_ancestor(item, start_item, end_item);
+ bool has_text_decoration = false;
+ gchar const *root_style = (item)->getRepr()->attribute("style");
+ if(strstr(root_style,"text-decoration"))has_text_decoration = true;
+
if (start_item == end_item) {
// the quick case where we're deleting stuff all from the same string
if (SP_IS_STRING(start_item)) { // always true (if it_start != it_end anyway)
@@ -835,7 +839,7 @@ sp_te_delete (SPItem *item, Inkscape::Text::Layout::iterator const &start,
}
}
- while (tidy_xml_tree_recursively(common_ancestor)){};
+ while (tidy_xml_tree_recursively(common_ancestor, has_text_decoration)){};
te_update_layout_now(item);
item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
layout->validateIterator(&iter_pair.first);
@@ -1554,7 +1558,7 @@ static SPObject* ascend_while_first(SPObject *item, Glib::ustring::iterator text
/** empty spans: abc<span></span>def
-> abcdef */
-static bool tidy_operator_empty_spans(SPObject **item)
+static bool tidy_operator_empty_spans(SPObject **item, bool has_text_decoration)
{
bool result = false;
if ( !(*item)->hasChildren()
@@ -1572,7 +1576,7 @@ static bool tidy_operator_empty_spans(SPObject **item)
/** inexplicable spans: abc<span style="">def</span>ghi
-> "abc""def""ghi"
the repeated strings will be merged by another operator. */
-static bool tidy_operator_inexplicable_spans(SPObject **item)
+static bool tidy_operator_inexplicable_spans(SPObject **item, bool has_text_decoration)
{
//XML Tree being directly used here while it shouldn't be.
if (*item && sp_repr_is_meta_element((*item)->getRepr())) {
@@ -1607,7 +1611,7 @@ static bool tidy_operator_inexplicable_spans(SPObject **item)
/** repeated spans: <font a>abc</font><font a>def</font>
-> <font a>abcdef</font> */
-static bool tidy_operator_repeated_spans(SPObject **item)
+static bool tidy_operator_repeated_spans(SPObject **item, bool has_text_decoration)
{
SPObject *first = *item;
SPObject *second = first->getNext();
@@ -1653,7 +1657,7 @@ static bool tidy_operator_repeated_spans(SPObject **item)
-> <font b>abc</font>
excessive nesting: <font a><size 1>abc</size></font>
-> <font a,size 1>abc</font> */
-static bool tidy_operator_excessive_nesting(SPObject **item)
+static bool tidy_operator_excessive_nesting(SPObject **item, bool has_text_decoration)
{
if (!(*item)->hasChildren()) {
return false;
@@ -1731,7 +1735,7 @@ example. You may note that this only does its work when the doubly-nested
child is the first or last. The other cases are called 'style inversion'
below, and I'm not yet convinced that the result of that operation will be
tidier in all cases. */
-static bool tidy_operator_redundant_double_nesting(SPObject **item)
+static bool tidy_operator_redundant_double_nesting(SPObject **item, bool has_text_decoration)
{
if (!(*item)->hasChildren()) return false;
if ((*item)->firstChild() == (*item)->lastChild()) return false; // this is excessive nesting, done above
@@ -1792,7 +1796,7 @@ static bool redundant_semi_nesting_processor(SPObject **item, SPObject *child, b
-> <font b>abc</font><font>def</font>
test this by applying a colour to a region, then a different colour to
a partially-overlapping region. */
-static bool tidy_operator_redundant_semi_nesting(SPObject **item)
+static bool tidy_operator_redundant_semi_nesting(SPObject **item, bool has_text_decoration)
{
if (!(*item)->hasChildren()) return false;
if ((*item)->firstChild() == (*item)->lastChild()) return false; // this is redundant nesting, done above
@@ -1819,13 +1823,21 @@ static SPString* find_last_string_child_not_equal_to(SPObject *root, SPObject *n
return NULL;
}
-/** whitespace-only spans: abc<font> </font>def
- -> abc<font></font> def
- abc<b><i>def</i> </b>ghi
- -> abc<b><i>def</i></b> ghi */
-static bool tidy_operator_styled_whitespace(SPObject **item)
+/** whitespace-only spans:
+ abc<font> </font>def
+ -> abc<font></font> def
+ abc<b><i>def</i> </b>ghi
+ -> abc<b><i>def</i></b> ghi
+
+ visible text-decoration changes on white spaces should not be subject
+ to this sort of processing. So
+ abc<text-decoration-color> </text-decoration-color>def
+ is unchanged.
+*/
+static bool tidy_operator_styled_whitespace(SPObject **item, bool has_text_decoration)
{
- if (!SP_IS_STRING(*item)) {
+ // any type of visible text decoration is OK as pure spaces, so do nothing here in that case.
+ if (!SP_IS_STRING(*item) || has_text_decoration ) {
return false;
}
Glib::ustring const &str = SP_STRING(*item)->string;
@@ -1835,6 +1847,7 @@ static bool tidy_operator_styled_whitespace(SPObject **item)
}
}
+
SPObject *test_item = *item;
SPString *next_string;
for ( ; ; ) { // find the next string
@@ -1862,7 +1875,7 @@ static bool tidy_operator_styled_whitespace(SPObject **item)
if (next_string == NULL) {
return false; // an empty paragraph
}
- next_string->string += str;
+ next_string->string = str + next_string->string;
break;
}
}
@@ -1896,9 +1909,11 @@ It may be that some of the later tidy operators that I wrote are actually
general cases of the earlier operators, and hence the special-case-only
versions can be removed. I haven't analysed my work in detail to figure
out if this is so. */
-static bool tidy_xml_tree_recursively(SPObject *root)
+static bool tidy_xml_tree_recursively(SPObject *root, bool has_text_decoration)
{
- static bool (* const tidy_operators[])(SPObject**) = {
+ gchar const *root_style = (root)->getRepr()->attribute("style");
+ if(root_style && strstr(root_style,"text-decoration"))has_text_decoration = true;
+ static bool (* const tidy_operators[])(SPObject**, bool) = {
tidy_operator_empty_spans,
tidy_operator_inexplicable_spans,
tidy_operator_repeated_spans,
@@ -1915,12 +1930,12 @@ static bool tidy_xml_tree_recursively(SPObject *root)
continue;
}
if (child->hasChildren()) {
- changes |= tidy_xml_tree_recursively(child);
+ changes |= tidy_xml_tree_recursively(child, has_text_decoration);
}
unsigned i;
for (i = 0 ; i < sizeof(tidy_operators) / sizeof(tidy_operators[0]) ; i++) {
- if (tidy_operators[i](&child)) {
+ if (tidy_operators[i](&child, has_text_decoration)) {
changes = true;
break;
}
@@ -2018,7 +2033,10 @@ void sp_te_apply_style(SPItem *text, Inkscape::Text::Layout::iterator const &sta
and neither option can be made to work, a fallback could be to reduce
everything to a single level of nesting and drop all pretence of
roundtrippability. */
- while (tidy_xml_tree_recursively(common_ancestor)){};
+ bool has_text_decoration = false;
+ gchar const *root_style = (text)->getRepr()->attribute("style");
+ if(strstr(root_style,"text-decoration"))has_text_decoration = true;
+ while (tidy_xml_tree_recursively(common_ancestor, has_text_decoration)){};
// if we only modified subobjects this won't have been automatically sent
text->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);