=== modified file 'share/icons/icons.svg'
--- share/icons/icons.svg 2013-09-20 17:04:04 +0000
+++ share/icons/icons.svg 2014-02-10 20:22:34 +0000
@@ -3819,7 +3819,6 @@
-
@@ -3851,4 +3850,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2
+3
+
+
+
+
+
+
+
+
+
+
+
+
=== modified file 'src/desktop-style.cpp'
--- src/desktop-style.cpp 2014-02-09 11:08:00 +0000
+++ src/desktop-style.cpp 2014-02-10 20:22:34 +0000
@@ -1499,6 +1499,113 @@
}
/**
+ * Write to style_res the text decorations of a list of objects.
+ */
+int
+objects_query_textdecoration (GSList *objects, SPStyle *style_res)
+{
+ if (g_slist_length(objects) == 0) {
+ /* No objects, set empty */
+ return QUERY_STYLE_NOTHING;
+ }
+
+ SPITextDecorationLine text_decoration_line;
+ SPITextDecorationStyle text_decoration_style;
+ SPITextDecorationStyle sumt_decoration_style;
+ SPIPaint text_decoration_fill;
+ SPIPaint text_decoration_stroke;
+ int n_count = 0;
+ bool multi = false;
+
+ sumt_decoration_style.set = 0;
+ sumt_decoration_style.inherit = 0;
+ sumt_decoration_style.solid = 0;
+ sumt_decoration_style.isdouble = 0;
+ sumt_decoration_style.dotted = 0;
+ sumt_decoration_style.dashed = 0;
+ sumt_decoration_style.wavy = 0;
+
+ text_decoration_line.set = 0;
+ text_decoration_line.inherit = 0;
+ text_decoration_line.underline = 0;
+ text_decoration_line.overline = 0;
+ text_decoration_line.line_through = 0;
+ text_decoration_line.blink = 0;
+
+ for (GSList const *i = objects; i != NULL; i = i->next) {
+ SPObject *obj = SP_OBJECT (i->data);
+ if (!SP_IS_ITEM(obj)) {
+ continue;
+ }
+ SPStyle *style = obj->style;
+ if (!style) {
+ continue;
+ }
+
+ if ( !style->text_decoration_line.set ) {
+ continue;
+ }
+
+ n_count++;
+
+ text_decoration_line.set |= style->text_decoration_line.set;
+ text_decoration_line.inherit |= style->text_decoration_line.inherit;
+ text_decoration_line.underline |= style->text_decoration_line.underline;
+ text_decoration_line.overline |= style->text_decoration_line.overline;
+ text_decoration_line.line_through |= style->text_decoration_line.line_through;
+ text_decoration_line.blink |= style->text_decoration_line.blink;
+
+ sumt_decoration_style.set |= style->text_decoration_style.set;
+ sumt_decoration_style.inherit |= style->text_decoration_style.inherit;
+ sumt_decoration_style.solid |= style->text_decoration_style.solid;
+ sumt_decoration_style.isdouble |= style->text_decoration_style.isdouble;
+ sumt_decoration_style.dotted |= style->text_decoration_style.dotted;
+ sumt_decoration_style.dashed |= style->text_decoration_style.dashed;
+ sumt_decoration_style.wavy |= style->text_decoration_style.wavy;
+
+ if(style->text_decoration_style.set){
+ // last tspan style that has a style set wins
+ text_decoration_style.set = style->text_decoration_style.set;
+ text_decoration_style.inherit = style->text_decoration_style.inherit;
+ text_decoration_style.solid = style->text_decoration_style.solid;
+ text_decoration_style.isdouble = style->text_decoration_style.isdouble;
+ text_decoration_style.dotted = style->text_decoration_style.dotted;
+ text_decoration_style.dashed = style->text_decoration_style.dashed;
+ text_decoration_style.wavy = style->text_decoration_style.wavy;
+ }
+ text_decoration_fill.set = style->text_decoration_fill.set;
+ text_decoration_fill.inherit = style->text_decoration_fill.inherit;
+ text_decoration_stroke.set = style->text_decoration_stroke.set;
+ text_decoration_stroke.inherit = style->text_decoration_stroke.inherit;
+ //Todo Fixme - copy the actual colors too
+ }
+ if((sumt_decoration_style.set + sumt_decoration_style.inherit + sumt_decoration_style.solid +
+ sumt_decoration_style.isdouble + sumt_decoration_style.dotted + sumt_decoration_style.dashed +
+ sumt_decoration_style.wavy)
+ > 1)multi = true;
+
+ if((text_decoration_line.set + text_decoration_line.inherit + text_decoration_line.underline +
+ text_decoration_line.overline + text_decoration_line.line_through + text_decoration_line.blink)
+ > 1)multi = true;
+
+ style_res->text_decoration_line = text_decoration_line;
+ style_res->text_decoration_style = text_decoration_style;
+ style_res->text_decoration_fill = text_decoration_fill;
+ style_res->text_decoration_stroke = text_decoration_stroke;
+
+ if (n_count == 0) {
+ return QUERY_STYLE_NOTHING;
+ } else if (n_count == 1) {
+ return QUERY_STYLE_SINGLE;
+ } else {
+ if (!multi)
+ return QUERY_STYLE_MULTIPLE_SAME;
+ else
+ return QUERY_STYLE_MULTIPLE_DIFFERENT;
+ }
+}
+
+/**
* Query the given list of objects for the given property, write
* the result to style, return appropriate flag.
*/
@@ -1537,6 +1644,8 @@
return objects_query_blend (list, style);
} else if (property == QUERY_STYLE_PROPERTY_BLUR) {
return objects_query_blur (list, style);
+ } else if (property == QUERY_STYLE_PROPERTY_TEXTDECORATION) {
+ return objects_query_textdecoration (list, style);
}
return QUERY_STYLE_NOTHING;
}
=== modified file 'src/desktop-style.h'
--- src/desktop-style.h 2013-03-14 10:28:27 +0000
+++ src/desktop-style.h 2014-02-10 20:22:34 +0000
@@ -51,7 +51,8 @@
QUERY_STYLE_PROPERTY_BASELINES, // baseline-shift
QUERY_STYLE_PROPERTY_MASTEROPACITY, // opacity
QUERY_STYLE_PROPERTY_BLEND, // blend
- QUERY_STYLE_PROPERTY_BLUR // blur
+ QUERY_STYLE_PROPERTY_BLUR, // blur
+ QUERY_STYLE_PROPERTY_TEXTDECORATION // text-decoration
};
void sp_desktop_apply_css_recursive(SPObject *o, SPCSSAttr *css, bool skip_lines);
@@ -78,6 +79,7 @@
int objects_query_strokejoin (GSList *objects, SPStyle *style_res);
int objects_query_blur (GSList *objects, SPStyle *style_res);
+int objects_query_textdecoration (GSList *objects, SPStyle *style_res);
int sp_desktop_query_style_from_list (GSList *list, SPStyle *style, int property);
int sp_desktop_query_style(SPDesktop *desktop, SPStyle *style, int property);
=== modified file 'src/display/drawing-shape.cpp'
--- src/display/drawing-shape.cpp 2014-02-08 08:44:12 +0000
+++ src/display/drawing-shape.cpp 2014-02-10 20:22:34 +0000
@@ -179,19 +179,19 @@
// update fill and stroke paints.
// this cannot be done during nr_arena_shape_update, because we need a Cairo context
// to render svg:pattern
- has_fill = _nrstyle.prepareFill(dc, _item_bbox);
- has_stroke = _nrstyle.prepareStroke(dc, _item_bbox);
+ has_fill = _nrstyle.prepareFill(dc.raw(), _item_bbox);
+ has_stroke = _nrstyle.prepareStroke(dc.raw(), _item_bbox);
has_stroke &= (_nrstyle.stroke_width != 0);
if (has_fill || has_stroke) {
// TODO: remove segments outside of bbox when no dashes present
dc.path(_curve->get_pathvector());
if (has_fill) {
- _nrstyle.applyFill(dc);
+ _nrstyle.applyFill(dc.raw());
dc.fillPreserve();
}
if (has_stroke) {
- _nrstyle.applyStroke(dc);
+ _nrstyle.applyStroke(dc.raw());
dc.strokePreserve();
}
dc.newPath(); // clear path
=== modified file 'src/display/drawing-text.cpp'
--- src/display/drawing-text.cpp 2014-02-08 08:44:12 +0000
+++ src/display/drawing-text.cpp 2014-02-10 20:33:56 +0000
@@ -84,8 +84,12 @@
scale_bigbox /= _transform->descrim();
}
+ // bounding box for draws, a little bigger because some of the text decorations can extend outwards quite a ways
Geom::Rect bigbox(Geom::Point(0.0, _asc*scale_bigbox*1.1),Geom::Point(_width*scale_bigbox, -_dsc*scale_bigbox*1.1));
Geom::Rect b = bigbox * ctx.ctm;
+ // bounding box for picking - vertical limits are maximum font ascent/descent, horizontal limits are full width of the text.
+ Geom::Rect pbigbox(Geom::Point(0.0, _asc*scale_bigbox*1.0),Geom::Point(_width*scale_bigbox, -_dsc*scale_bigbox*1.0));
+ Geom::Rect pb = pbigbox * ctx.ctm;
if (ggroup->_nrstyle.stroke.type != NRStyle::PAINT_NONE) {
// this expands the selection box for cases where the stroke is "thick"
@@ -96,10 +100,11 @@
float width = MAX(0.125, ggroup->_nrstyle.stroke_width * scale);
if ( fabs(ggroup->_nrstyle.stroke_width * scale) > 0.01 ) { // FIXME: this is always true
b.expandBy(0.5 * width);
+ pb.expandBy(0.5 * width);
}
// save bbox without miters for picking
- _pick_bbox = b.roundOutwards();
+ _pick_bbox = pb.roundOutwards();
float miterMax = width * ggroup->_nrstyle.miter_limit;
if ( miterMax > 0.01 ) {
@@ -110,14 +115,8 @@
_bbox = b.roundOutwards();
} else {
_bbox = b.roundOutwards();
- _pick_bbox = *_bbox;
+ _pick_bbox = pb.roundOutwards();
}
-/*
-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;
}
@@ -136,8 +135,9 @@
// 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;
+ // FIXME, why expand by delta? When is the next line needed?
+ // expanded.expandBy(delta);
+ if (expanded.contains(p))return this;
return NULL;
}
@@ -195,7 +195,21 @@
return DrawingGroup::_updateItem(area, ctx, flags, reset);
}
-void DrawingText::decorateStyle(DrawingContext &dc, double vextent, double xphase, Geom::Point const &p1, Geom::Point const &p2)
+void
+revrectangle(cairo_t *ct, 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 rectangle(cairo_t *ct, Geom::Rect const &r) {
+ cairo_rectangle(ct, r.left(), r.top(), r.width(), r.height());
+}
+
+
+//Fixme The next method should probably be elsewhere, as it is also called by render
+void DrawingText::decorateStyle(cairo_t *ct, double vextent, double xphase, Geom::Point const &p1, Geom::Point const &p2, double thickness)
{
double wave[16]={
0.000000, 0.382499, 0.706825, 0.923651, 1.000000, 0.923651, 0.706825, 0.382499,
@@ -213,7 +227,6 @@
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
@@ -221,26 +234,23 @@
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]);
+ Geom::Point ps = Geom::Point(step * round(p1[Geom::X]/step),p1[Geom::Y]);
+ Geom::Point pf = Geom::Point(step * round(p2[Geom::X]/step),p2[Geom::Y]);
+ Geom::Point poff = Geom::Point(0,thickness/2.0);
if(_nrstyle.text_decoration_style & TEXT_DECORATION_STYLE_ISDOUBLE){
ps -= Geom::Point(0, vextent/12.0);
pf -= Geom::Point(0, vextent/12.0);
- dc.moveTo(ps);
- dc.lineTo(pf);
+// cairo_move_to(ct, ps[Geom::X], ps[Geom::Y]);
+// cairo_line_to(ct, pf[Geom::X], pf[Geom::Y]);
+ revrectangle(ct, Geom::Rect(ps + poff, pf - poff));
ps += Geom::Point(0, vextent/6.0);
pf += Geom::Point(0, vextent/6.0);
- dc.moveTo(ps);
- dc.lineTo(pf);
+// cairo_move_to(ct, ps[Geom::X], ps[Geom::Y]);
+// cairo_line_to(ct, pf[Geom::X], pf[Geom::Y]);
+ revrectangle(ct, Geom::Rect(ps + poff, pf - poff));
}
/* 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
@@ -248,43 +258,51 @@
Huge possitive offset should keep the phase calculation from ever being negative.
*/
else if(_nrstyle.text_decoration_style & TEXT_DECORATION_STYLE_DOTTED){
+ Geom::Point pv = ps;
while(1){
+ Geom::Point pvlast = pv;
if(dots[i]>0){
- if(ps[Geom::X]> pf[Geom::X])break;
- dc.moveTo(ps);
- ps += Geom::Point(step * (double)dots[i], 0.0);
- if(ps[Geom::X]>= pf[Geom::X]){
- dc.lineTo(pf);
+ if(pv[Geom::X] > pf[Geom::X])break;
+// cairo_move_to(ct, pv[Geom::X], pv[Geom::Y]);
+ pv += Geom::Point(step * (double)dots[i], 0.0);
+ if(pv[Geom::X]>= pf[Geom::X]){
+// cairo_line_to(ct, pf[Geom::X], pf[Geom::Y]);
+ revrectangle(ct, Geom::Rect(pvlast + poff, pf - poff));
break;
}
else {
- dc.lineTo(ps);
+// cairo_line_to(ct, pv[Geom::X], pv[Geom::Y]);
+ revrectangle(ct, Geom::Rect(pvlast + poff, pv - poff));
}
- ps += Geom::Point(step * 4.0, 0.0);
+ pv += Geom::Point(step * 4.0, 0.0);
}
else {
- ps += Geom::Point(step * -(double)dots[i], 0.0);
+ pv += 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){
+ Geom::Point pv = ps;
while(1){
+ Geom::Point pvlast = pv;
if(dashes[i]>0){
- if(ps[Geom::X]> pf[Geom::X])break;
- dc.moveTo(ps);
- ps += Geom::Point(step * (double)dashes[i], 0.0);
- if(ps[Geom::X]>= pf[Geom::X]){
- dc.lineTo(pf);
+ if(pv[Geom::X] > pf[Geom::X])break;
+// cairo_move_to(ct, ps[Geom::X], ps[Geom::Y]);
+ pv += Geom::Point(step * (double)dashes[i], 0.0);
+ if(pv[Geom::X]>= pf[Geom::X]){
+// cairo_line_to(ct, pf[Geom::X], pf[Geom::Y]);
+ revrectangle(ct, Geom::Rect(pvlast + poff, pf - poff));
break;
}
else {
- dc.lineTo(ps);
+// cairo_line_to(ct, ps[Geom::X], ps[Geom::Y]);
+ revrectangle(ct, Geom::Rect(pvlast + poff, pv - poff));
}
- ps += Geom::Point(step * 8.0, 0.0);
+ pv += Geom::Point(step * 8.0, 0.0);
}
else {
- ps += Geom::Point(step * -(double)dashes[i], 0.0);
+ pv += Geom::Point(step * -(double)dashes[i], 0.0);
}
i = 0; // once in phase, it stays in phase
}
@@ -292,24 +310,35 @@
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];
- dc.moveTo(Geom::Point(x, y + amp * wave[i]));
+ double y = ps[Geom::Y] + poff[Geom::Y];
+ cairo_move_to(ct, x, y + amp * wave[i]);
while(1){
i = ((i + 1) & 15);
x += step;
- dc.lineTo(Geom::Point(x, y + amp * wave[i]));
+ cairo_line_to(ct, x, y + amp * wave[i]);
if(x >= pf[Geom::X])break;
}
+ y = ps[Geom::Y] - poff[Geom::Y];
+ cairo_line_to(ct, x, y + amp * wave[i]);
+ while(1){
+ i = ((i - 1) & 15);
+ x -= step;
+ cairo_line_to(ct, x, y + amp * wave[i]);
+ if(x <= ps[Geom::X])break;
+ }
+ cairo_close_path ( ct);
}
else { // TEXT_DECORATION_STYLE_SOLID, also default in case it was not set for some reason
- dc.moveTo(ps);
- dc.lineTo(pf);
-// dc.revrectangle(Geom::Rect(ps,pf));
+// cairo_move_to(ct, ps[Geom::X], ps[Geom::Y]);
+// cairo_line_to(ct, pf[Geom::X], pf[Geom::Y]);
+ revrectangle(ct, Geom::Rect(ps + poff, pf - poff));
}
}
-/* returns scaled line thickness */
-double DrawingText::decorateItem(DrawingContext &dc, Geom::Affine const &aff, double phase_length)
+//Fixme The next method should probably be elsewhere, as it is also called by render
+/* returns the thickness of the line
+ under is true for decorations that go under the glyphs, false for those that go over */
+void DrawingText::decorateItem(cairo_t *ct, double phase_length, bool under)
{
double tsp_width_adj = _nrstyle.tspan_width / _nrstyle.font_size;
double tsp_asc_adj = _nrstyle.ascender / _nrstyle.font_size;
@@ -318,44 +347,42 @@
double final_underline_thickness = CLAMP(_nrstyle.underline_thickness, tsp_size_adj/30.0, tsp_size_adj/10.0);
double final_line_through_thickness = CLAMP(_nrstyle.line_through_thickness, tsp_size_adj/30.0, tsp_size_adj/10.0);
- double scale = aff.descrim();
double xphase = phase_length/ _nrstyle.font_size; // used to figure out phase of patterns
- Inkscape::DrawingContext::Save save(dc);
- dc.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
double 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(dc, 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(dc, 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(dc, 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(dc, 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(dc, tsp_size_adj, xphase, p1, p2);
- }
- thickness *= scale;
- return(thickness);
+ cairo_set_tolerance(ct,0.5);
+ if(under){
+ 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, thickness);
+ }
+ 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, thickness);
+ }
+ }
+ else {
+ if(_nrstyle.text_decoration_line & TEXT_DECORATION_LINE_LINETHROUGH){
+ 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, thickness);
+ }
+ // 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){
+ 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);
+ 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);
+ }
+ }
}
unsigned DrawingText::_renderItem(DrawingContext &dc, Geom::IntRect const &/*area*/, unsigned /*flags*/, DrawingItem * /*stop_at*/)
@@ -386,47 +413,46 @@
double leftmost = DBL_MAX;
double phase_length = 0.0;
bool firsty = true;
- bool decorate = true;
+ bool decorate = false;
double starty = 0.0;
Geom::Affine aff;
using Geom::X;
using Geom::Y;
// NOTE:
- // prepareFill / prepareStroke need to be called with _ctm in effect.
+ // prepareFill / prepareStroke need to be called with _ctm in effect
+ // so that patterns will scale along with the text.
// However, we might need to apply a different ctm for glyphs.
// Therefore, only apply this ctm temporarily.
- bool has_stroke, has_fill;
+ bool has_stroke, has_fill, has_td_fill, has_td_stroke;
{
Inkscape::DrawingContext::Save save(dc);
dc.transform(_ctm);
- has_fill = _nrstyle.prepareFill( dc, _item_bbox);
- has_stroke = _nrstyle.prepareStroke(dc, _item_bbox);
+ has_fill = _nrstyle.prepareFill( dc.raw(), _item_bbox);
+ has_stroke = _nrstyle.prepareStroke(dc.raw(), _item_bbox);
+ has_td_fill = _nrstyle.prepareTextDecorationFill(dc.raw(), _item_bbox);
+ has_td_stroke = _nrstyle.prepareTextDecorationStroke(dc.raw(), _item_bbox);
}
if (has_fill || has_stroke) {
Geom::Affine rotinv;
bool invset = false;
- // accumulate the path that represents the glyphs
- for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
- DrawingGlyphs *g = dynamic_cast(&*i);
- if (!g) throw InvalidItemException();
- if (!invset) {
- rotinv = g->_ctm.withoutTranslation().inverse();
- invset = true;
- }
- Inkscape::DrawingContext::Save save(dc);
- if (g->_ctm.isSingular()) continue;
- dc.transform(g->_ctm);
- if (g->_drawable) {
- dc.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) {
+ /* Find 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. rotinv/invset will be used again later.
+ */
+ if ((_nrstyle.text_decoration_line != TEXT_DECORATION_LINE_CLEAR) && (has_td_fill || has_td_stroke)) {
+ decorate = true;
+ for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
+ DrawingGlyphs *g = dynamic_cast(&*i);
+ if (!g) throw InvalidItemException();
+ if (!invset) {
+ rotinv = g->_ctm.withoutTranslation().inverse();
+ invset = true;
+ }
Geom::Point pt = g->_ctm.translation() * rotinv;
if (pt[X] < leftmost) {
leftmost = pt[X];
@@ -446,44 +472,77 @@
}
}
- // draw the text itself
- // we need to apply this object's ctm again
- Inkscape::DrawingContext::Save save(dc);
- dc.transform(_ctm);
- if (has_fill) {
- _nrstyle.applyFill(dc);
- dc.fillPreserve();
- }
- if (has_stroke) {
- _nrstyle.applyStroke(dc);
- dc.strokePreserve();
- }
- dc.newPath(); // clear path
-
- // draw text decoration
- 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);
- }
- dc.setSource(ergba);
- dc.setTolerance(0.5);
- double thickness = decorateItem(dc, aff, phase_length);
- dc.setLineWidth(thickness);
- dc.strokePreserve();
- dc.newPath(); // clear path
- }
+ // draw text decorations that go UNDER the text
+ if (decorate) {
+ {
+ Inkscape::DrawingContext::Save save(dc);
+ dc.transform(aff); // must be leftmost affine in span
+ decorateItem(dc.raw(),phase_length, true);
+ }
+ {
+ Inkscape::DrawingContext::Save save(dc);
+ dc.transform(_ctm); // Needed so that fill pattern rotates with text
+ if (has_td_fill) {
+ _nrstyle.applyTextDecorationFill(dc.raw());
+ dc.fillPreserve();
+ }
+ if (has_td_stroke) {
+ _nrstyle.applyTextDecorationStroke(dc.raw());
+ dc.strokePreserve();
+ }
+ }
+ }
+ dc.newPath(); // clear path
+
+ // accumulate the path that represents the glyphs
+ for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
+ DrawingGlyphs *g = dynamic_cast(&*i);
+ if (!g) throw InvalidItemException();
+
+ Inkscape::DrawingContext::Save save(dc);
+ if (g->_ctm.isSingular()) continue;
+ dc.transform(g->_ctm);
+ if (g->_drawable) {
+ dc.path(*g->_font->PathVector(g->_glyph));
+ }
+ }
+
+ // Draw the glyphs.
+ {
+ Inkscape::DrawingContext::Save save(dc);
+ dc.transform(_ctm); // Needed so that fill pattern rotates with text
+ if (has_fill) {
+ _nrstyle.applyFill(dc.raw());
+ dc.fillPreserve();
+ }
+ if (has_stroke) {
+ _nrstyle.applyStroke(dc.raw());
+ dc.strokePreserve();
+ }
+ }
+ dc.newPath(); // clear path
+
+ // draw text decorations that go OVER the text
+ if (decorate) {
+ {
+ Inkscape::DrawingContext::Save save(dc);
+ dc.transform(aff); // must be leftmost affine in span
+ decorateItem(dc.raw(),phase_length, false);
+ }
+ {
+ Inkscape::DrawingContext::Save save(dc);
+ dc.transform(_ctm); // Needed so that fill pattern rotates with text
+ if (has_td_fill) {
+ _nrstyle.applyTextDecorationFill(dc.raw());
+ dc.fillPreserve();
+ }
+ if (has_td_stroke) {
+ _nrstyle.applyTextDecorationStroke(dc.raw());
+ dc.strokePreserve();
+ }
+ }
+ }
+ dc.newPath(); // clear path
}
return RENDER_OK;
}
@@ -520,9 +579,7 @@
DrawingText::_pickItem(Geom::Point const &p, double delta, unsigned flags)
{
DrawingItem *picked = DrawingGroup::_pickItem(p, delta, flags);
- if (picked) {
- return this;
- }
+ if (picked) return this;
return NULL;
}
=== modified file 'src/display/drawing-text.h'
--- src/display/drawing-text.h 2014-02-08 08:44:12 +0000
+++ src/display/drawing-text.h 2014-02-10 20:22:34 +0000
@@ -58,6 +58,9 @@
float width, float ascent, float descent, float phase_length);
void setStyle(SPStyle *style);
+ //Fixme The next two should probably be elsewhere, as they are also called by render
+ void decorateItem(cairo_t *ct, double phase_length, bool under);
+ void decorateStyle(cairo_t *ct, double vextent, double xphase, Geom::Point const &p1, Geom::Point const &p2, double thickness);
protected:
virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx,
@@ -68,8 +71,6 @@
virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags);
virtual bool _canClip();
- double decorateItem(DrawingContext &dc, Geom::Affine const &aff, double phase_length);
- void decorateStyle(DrawingContext &dc, double vextent, double xphase, Geom::Point const &p1, Geom::Point const &p2);
NRStyle _nrstyle;
friend class DrawingGlyphs;
=== modified file 'src/display/nr-style.cpp'
--- src/display/nr-style.cpp 2014-02-08 08:44:12 +0000
+++ src/display/nr-style.cpp 2014-02-10 20:22:34 +0000
@@ -54,8 +54,13 @@
, line_join(CAIRO_LINE_JOIN_MITER)
, fill_pattern(NULL)
, stroke_pattern(NULL)
+ , text_decoration_fill_pattern(NULL)
+ , text_decoration_stroke_pattern(NULL)
, text_decoration_line(TEXT_DECORATION_LINE_CLEAR)
, text_decoration_style(TEXT_DECORATION_STYLE_CLEAR)
+ , text_decoration_fill()
+ , text_decoration_stroke()
+ , text_decoration_stroke_width(0.0)
, phase_length(0.0)
, tspan_line_start(false)
, tspan_line_end(false)
@@ -74,11 +79,15 @@
{
if (fill_pattern) cairo_pattern_destroy(fill_pattern);
if (stroke_pattern) cairo_pattern_destroy(stroke_pattern);
+ if (text_decoration_fill_pattern) cairo_pattern_destroy(text_decoration_fill_pattern);
+ if (text_decoration_stroke_pattern) cairo_pattern_destroy(text_decoration_stroke_pattern);
if (dash){
delete [] dash;
}
fill.clear();
stroke.clear();
+ text_decoration_fill.clear();
+ text_decoration_stroke.clear();
}
void NRStyle::set(SPStyle *style)
@@ -175,17 +184,49 @@
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( style->text_decoration_fill.set ||
+ style->text_decoration_fill.inherit ||
+ style->text_decoration_fill.currentcolor ||
+ style->text_decoration_fill.colorSet ||
+ style->text_decoration_fill.value.href
+ ){
+ if ( style->text_decoration_fill.isPaintserver() ) {
+ text_decoration_fill.set(style->getTextDecorationFillPaintServer());
+ } else if ( style->text_decoration_fill.isColor() ) {
+ text_decoration_fill.set(style->text_decoration_fill.value.color);
+ } else if ( style->text_decoration_fill.isNone() ) {
+ text_decoration_fill.clear();
+ } else {
+ g_assert_not_reached();
+ }
+ text_decoration_fill.opacity = 1.0;
+ }
+ else {
+ text_decoration_fill.clear();
+ }
+
+ if( style->text_decoration_stroke.set ||
+ style->text_decoration_stroke.inherit ||
+ style->text_decoration_stroke.currentcolor ||
+ style->text_decoration_stroke.colorSet ||
+ style->text_decoration_stroke.value.href
+ ){
+ if ( style->text_decoration_stroke.isPaintserver() ) {
+ text_decoration_stroke.set(style->getTextDecorationStrokePaintServer());
+ } else if ( style->text_decoration_stroke.isColor() ) {
+ text_decoration_stroke.set(style->text_decoration_stroke.value.color);
+ } else if ( style->text_decoration_stroke.isNone() ) {
+ text_decoration_stroke.clear();
+ } else {
+ g_assert_not_reached();
+ }
+ text_decoration_stroke.opacity = 1.0;
+ }
+ else {
+ text_decoration_stroke.clear();
+ }
+ text_decoration_stroke.opacity = SP_SCALE24_TO_FLOAT(style->text_decoration_stroke_opacity.value);
+ text_decoration_stroke_width = style->text_decoration_stroke_width.computed;
if(text_decoration_line != TEXT_DECORATION_LINE_CLEAR){
phase_length = style->text_decoration_data.phase_length;
@@ -207,21 +248,20 @@
update();
}
-bool NRStyle::prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox)
+bool NRStyle::prepareFill(cairo_t *ct, Geom::OptRect const &paintbox)
{
// update fill pattern
if (!fill_pattern) {
switch (fill.type) {
case PAINT_SERVER: {
//fill_pattern = sp_paint_server_create_pattern(fill.server, dc.raw(), paintbox, fill.opacity);
- fill_pattern = fill.server->pattern_new(dc.raw(), paintbox, fill.opacity);
-
- } break;
+ fill_pattern = fill.server->pattern_new(ct, paintbox, fill.opacity);
+ } break;
case PAINT_COLOR: {
SPColor const &c = fill.color;
fill_pattern = cairo_pattern_create_rgba(
c.v.c[0], c.v.c[1], c.v.c[2], fill.opacity);
- } break;
+ } break;
default: break;
}
}
@@ -229,19 +269,45 @@
return true;
}
-void NRStyle::applyFill(Inkscape::DrawingContext &dc)
-{
- dc.setSource(fill_pattern);
- dc.setFillRule(fill_rule);
-}
-
-bool NRStyle::prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox)
+void NRStyle::applyFill(cairo_t *ct)
+{
+ cairo_set_source(ct, fill_pattern);
+ cairo_set_fill_rule(ct, fill_rule);
+}
+
+bool NRStyle::prepareTextDecorationFill(cairo_t *ct, Geom::OptRect const &paintbox)
+{
+ // update text decoration pattern
+ if (!text_decoration_fill_pattern) {
+ switch (text_decoration_fill.type) {
+ case PAINT_SERVER: {
+ text_decoration_fill_pattern = text_decoration_fill.server->pattern_new(ct, paintbox, text_decoration_fill.opacity);
+ } break;
+ case PAINT_COLOR: {
+ SPColor const &c = text_decoration_fill.color;
+ text_decoration_fill_pattern = cairo_pattern_create_rgba(
+ c.v.c[0], c.v.c[1], c.v.c[2], text_decoration_fill.opacity);
+ } break;
+ default: break;
+ }
+ }
+ if (!text_decoration_fill_pattern) return false;
+ return true;
+}
+
+void NRStyle::applyTextDecorationFill(cairo_t *ct)
+{
+ cairo_set_source(ct, text_decoration_fill_pattern);
+ //fill rule will not matter, no intersections
+}
+
+
+bool NRStyle::prepareStroke(cairo_t *ct, Geom::OptRect const &paintbox)
{
if (!stroke_pattern) {
switch (stroke.type) {
case PAINT_SERVER: {
- //stroke_pattern = sp_paint_server_create_pattern(stroke.server, dc.raw(), paintbox, stroke.opacity);
- stroke_pattern = stroke.server->pattern_new(dc.raw(), paintbox, stroke.opacity);
+ stroke_pattern = stroke.server->pattern_new(ct, paintbox, stroke.opacity);
} break;
case PAINT_COLOR: {
@@ -256,23 +322,57 @@
return true;
}
-void NRStyle::applyStroke(Inkscape::DrawingContext &dc)
-{
- dc.setSource(stroke_pattern);
- dc.setLineWidth(stroke_width);
- dc.setLineCap(line_cap);
- dc.setLineJoin(line_join);
- dc.setMiterLimit(miter_limit);
- cairo_set_dash(dc.raw(), dash, n_dash, dash_offset); // fixme
-}
+void NRStyle::applyStroke(cairo_t *ct)
+{
+ cairo_set_source(ct, stroke_pattern);
+ cairo_set_line_width(ct, stroke_width);
+ cairo_set_line_cap(ct, line_cap);
+ cairo_set_line_join(ct, line_join);
+ cairo_set_miter_limit(ct, miter_limit);
+ cairo_set_dash(ct, dash, n_dash, dash_offset); // fixme
+}
+
+bool NRStyle::prepareTextDecorationStroke(cairo_t *ct, Geom::OptRect const &paintbox)
+{
+ if (!text_decoration_stroke_pattern) {
+ switch (text_decoration_stroke.type) {
+ case PAINT_SERVER: {
+ text_decoration_stroke_pattern = text_decoration_stroke.server->pattern_new(ct, paintbox, text_decoration_stroke.opacity);
+ } break;
+ case PAINT_COLOR: {
+ SPColor const &c = text_decoration_stroke.color;
+ text_decoration_stroke_pattern = cairo_pattern_create_rgba(
+ c.v.c[0], c.v.c[1], c.v.c[2], text_decoration_stroke.opacity);
+ } break;
+ default: break;
+ }
+ }
+ if (!text_decoration_stroke_pattern) return false;
+ return true;
+}
+
+void NRStyle::applyTextDecorationStroke(cairo_t *ct)
+{
+ cairo_set_source(ct, text_decoration_stroke_pattern);
+ cairo_set_line_width(ct, text_decoration_stroke_width);
+ cairo_set_line_cap(ct, CAIRO_LINE_CAP_BUTT);
+ cairo_set_line_join(ct, CAIRO_LINE_JOIN_MITER);
+ cairo_set_miter_limit(ct, 10.0);
+ cairo_set_dash(ct, 0, 0, 0.0); // fixme no dashes.
+}
+
void NRStyle::update()
{
// force pattern update
if (fill_pattern) cairo_pattern_destroy(fill_pattern);
if (stroke_pattern) cairo_pattern_destroy(stroke_pattern);
+ if (text_decoration_fill_pattern) cairo_pattern_destroy(text_decoration_fill_pattern);
+ if (text_decoration_stroke_pattern) cairo_pattern_destroy(text_decoration_stroke_pattern);
fill_pattern = NULL;
stroke_pattern = NULL;
+ text_decoration_fill_pattern = NULL;
+ text_decoration_stroke_pattern = NULL;
}
/*
=== modified file 'src/display/nr-style.h'
--- src/display/nr-style.h 2014-02-08 08:44:12 +0000
+++ src/display/nr-style.h 2014-02-10 20:22:34 +0000
@@ -28,10 +28,14 @@
~NRStyle();
void set(SPStyle *);
- bool prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox);
- bool prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox);
- void applyFill(Inkscape::DrawingContext &dc);
- void applyStroke(Inkscape::DrawingContext &dc);
+ bool prepareFill(cairo_t *ct, Geom::OptRect const &paintbox);
+ bool prepareStroke(cairo_t *ct, Geom::OptRect const &paintbox);
+ bool prepareTextDecorationFill(cairo_t *ct, Geom::OptRect const &paintbox);
+ bool prepareTextDecorationStroke(cairo_t *ct, Geom::OptRect const &paintbox);
+ void applyFill(cairo_t *ct);
+ void applyStroke(cairo_t *ct);
+ void applyTextDecorationFill(cairo_t *ct);
+ void applyTextDecorationStroke(cairo_t *ct);
void update();
enum PaintType {
@@ -67,6 +71,8 @@
cairo_pattern_t *fill_pattern;
cairo_pattern_t *stroke_pattern;
+ cairo_pattern_t *text_decoration_fill_pattern;
+ cairo_pattern_t *text_decoration_stroke_pattern;
#define TEXT_DECORATION_LINE_CLEAR 0x00
#define TEXT_DECORATION_LINE_SET 0x01
@@ -87,8 +93,9 @@
int text_decoration_line;
int text_decoration_style;
- Paint text_decoration_color;
- bool text_decoration_useColor; // if false, use whatever the glyph color was
+ Paint text_decoration_fill;
+ Paint text_decoration_stroke;
+ float text_decoration_stroke_width;
// These are the same as in style.h
float phase_length;
bool tspan_line_start;
=== modified file 'src/libnrtype/Layout-TNG-Output.cpp'
--- src/libnrtype/Layout-TNG-Output.cpp 2013-10-06 20:59:49 +0000
+++ src/libnrtype/Layout-TNG-Output.cpp 2014-02-10 20:22:34 +0000
@@ -241,6 +241,7 @@
sp_print_fill(ctx, temp_pv, ctm, text_source->style, pbox, dbox, bbox);
if (!text_source->style->stroke.isNone())
sp_print_stroke(ctx, temp_pv, ctm, text_source->style, pbox, dbox, bbox);
+ //ToDo - currently no text decorations are enabled for text->path printing
}
}
}
@@ -456,7 +457,8 @@
}
} while (glyph_index < _glyphs.size()
&& _path_fitted == NULL
- && (font_matrix * glyph_matrix.inverse()).isIdentity()
+// Fixme. This breaks at every glyph for some reason. Commented out seems to work. What is it for?
+// && (font_matrix * glyph_matrix.inverse()).isIdentity()
&& _characters[_glyphs[glyph_index].in_character].in_span == this_span_index);
// remove vertical flip
=== modified file 'src/sp-item.cpp'
--- src/sp-item.cpp 2013-12-10 12:40:42 +0000
+++ src/sp-item.cpp 2014-02-10 20:22:34 +0000
@@ -1124,6 +1124,8 @@
sp_pattern_transform_multiply(pattern, postmul, set);
}
}
+
+ // Fixme: text_decoration_fill/stroke adjustments are linked to the fill/stroke adjustments, above?
}
void SPItem::adjust_gradient( Geom::Affine const &postmul, bool set )
@@ -1154,6 +1156,8 @@
sp_gradient_transform_multiply( gradient, postmul, set );
}
}
+
+ // Fixme: text_decoration_fill/stroke gradients are linked to the fill/stroke adjustments, above?
}
void SPItem::adjust_stroke( gdouble ex )
=== modified file 'src/style.cpp'
--- src/style.cpp 2013-12-10 12:40:42 +0000
+++ src/style.cpp 2014-02-10 20:22:34 +0000
@@ -447,8 +447,8 @@
}
/**
- * Emit style modified signal on style's object if server is style's fill
- * or stroke paint server.
+ * Emit style modified signal on style's object if server is style's fill,
+ * stroke, text_decoration_fill, or text_decoration_stroke paint server.
*/
static void
sp_style_paint_server_ref_modified(SPObject *obj, guint flags, SPStyle *style)
@@ -456,8 +456,10 @@
(void)flags; // TODO
SPPaintServer *server = static_cast(obj);
- if ((style->fill.isPaintserver())
- && style->getFillPaintServer() == server)
+ if ((style->fill.isPaintserver() && (style->getFillPaintServer() == server)) ||
+ (style->stroke.isPaintserver() && (style->getStrokePaintServer() == server)) ||
+ (style->text_decoration_fill.isPaintserver() && (style->getTextDecorationFillPaintServer() == server)) ||
+ (style->text_decoration_stroke.isPaintserver() && (style->getTextDecorationStrokePaintServer() == server)))
{
if (style->object) {
/** \todo
@@ -470,13 +472,6 @@
*/
style->object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
}
- } else if ((style->stroke.isPaintserver())
- && style->getStrokePaintServer() == server)
- {
- if (style->object) {
- /// \todo fixme:
- style->object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
- }
} else if (server) {
g_assert_not_reached();
}
@@ -517,6 +512,40 @@
}
/**
+ * Gets called when the paintserver is (re)attached to the style
+ */
+static void
+sp_style_text_decoration_fill_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style)
+{
+ if (old_ref) {
+ style->text_decoration_fill_ps_modified_connection.disconnect();
+ }
+ if (SP_IS_PAINT_SERVER(ref)) {
+ style->text_decoration_fill_ps_modified_connection =
+ ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_paint_server_ref_modified), style));
+ }
+
+ sp_style_paint_server_ref_modified(ref, 0, style);
+}
+
+/**
+ * Gets called when the paintserver is (re)attached to the style
+ */
+static void
+sp_style_text_decoration_stroke_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style)
+{
+ if (old_ref) {
+ style->text_decoration_stroke_ps_modified_connection.disconnect();
+ }
+ if (SP_IS_PAINT_SERVER(ref)) {
+ style->text_decoration_stroke_ps_modified_connection =
+ ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_paint_server_ref_modified), style));
+ }
+
+ sp_style_paint_server_ref_modified(ref, 0, style);
+}
+
+/**
* Returns a new SPStyle object with settings as per sp_style_clear().
*/
SPStyle *
@@ -538,6 +567,8 @@
new (&style->filter_modified_connection) sigc::connection();
new (&style->fill_ps_modified_connection) sigc::connection();
new (&style->stroke_ps_modified_connection) sigc::connection();
+ new (&style->text_decoration_fill_ps_modified_connection) sigc::connection();
+ new (&style->text_decoration_stroke_ps_modified_connection) sigc::connection();
return style;
}
@@ -605,6 +636,16 @@
delete style->stroke.value.href;
style->stroke.value.href = NULL;
}
+ if (style->text_decoration_fill.value.href) {
+ style->text_decoration_fill_ps_modified_connection.disconnect();
+ delete style->text_decoration_fill.value.href;
+ style->text_decoration_fill.value.href = NULL;
+ }
+ if (style->text_decoration_stroke.value.href) {
+ style->text_decoration_stroke_ps_modified_connection.disconnect();
+ delete style->text_decoration_stroke.value.href;
+ style->text_decoration_stroke.value.href = NULL;
+ }
if (style->filter.href) {
style->filter_modified_connection.disconnect();
delete style->filter.href;
@@ -614,9 +655,13 @@
style->filter_modified_connection.~connection();
style->fill_ps_modified_connection.~connection();
style->stroke_ps_modified_connection.~connection();
+ style->text_decoration_fill_ps_modified_connection.~connection();
+ style->text_decoration_stroke_ps_modified_connection.~connection();
style->fill.clear();
style->stroke.clear();
+ style->text_decoration_fill.clear();
+ style->text_decoration_stroke.clear();
sp_style_filter_clear(style);
g_free(style->stroke_dash.dash);
@@ -684,10 +729,11 @@
/* 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);
+ //At present SVG2/CSS3 only supports a single color, so equate it to fill.
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);
+ sp_style_read_itextdecoration(&style->text_decoration_line, &style->text_decoration_style, &style->text_decoration_fill, val);
}
}
if (!style->line_height.set) {
@@ -1142,8 +1188,9 @@
SPS_READ_IENUM_IF_UNSET(&style->text_align, val, enum_text_align, true);
break;
case SP_PROP_TEXT_DECORATION:
+ //At present SVG2/CSS3 only supports a single color, so equate it to fill.
if (!style->text_decoration_line.set) {
- sp_style_read_itextdecoration(&style->text_decoration_line, &style->text_decoration_style, &style->text_decoration_color, val);
+ sp_style_read_itextdecoration(&style->text_decoration_line, &style->text_decoration_style, &style->text_decoration_fill, val);
}
break;
case SP_PROP_TEXT_DECORATION_LINE:
@@ -1157,8 +1204,9 @@
}
break;
case SP_PROP_TEXT_DECORATION_COLOR:
- if (!style->text_decoration_color.set) {
- sp_style_read_itextdecorationColor(&style->text_decoration_color, val);
+ //At present SVG2/CSS3 only supports a single color, so equate it to fill.
+ if (!style->text_decoration_fill.set) {
+ sp_style_read_itextdecorationColor(&style->text_decoration_fill, val);
}
break;
case SP_PROP_LINE_HEIGHT:
@@ -1753,25 +1801,6 @@
style->text_align.computed = parent->text_align.computed;
}
- 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) {
style->line_height.value = parent->line_height.value;
style->line_height.computed = parent->line_height.computed;
@@ -1903,6 +1932,74 @@
}
}
+ /* text_decoration_fill/stroke MUST follow fill/stroke being set, as in some cases the color comes from one of those */
+ 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;
+ style->text_decoration_line.noneSet = parent->text_decoration_line.noneSet;
+ style->text_decoration_line.active = parent->text_decoration_line.active;
+ }
+
+ 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;
+ }
+
+ /* worry about decoration color only if one must be drawn */
+ if(style->text_decoration_line.set && !style->text_decoration_line.inherit) {
+ /* Text decoration colors can come from many places...
+ Fill may be set explicitly with a color (CSS3).
+ It may be set implicitly (CSS2, when the text decoration is specified).
+ It may be inherited from a parent.
+ */
+ if(!style->text_decoration_fill.set) {
+ sp_style_merge_ipaint(style, &style->text_decoration_fill, &style->fill);
+ style->text_decoration_fill_opacity = style->fill_opacity;
+ }
+ else {
+ // already set explicitly (CSS 3)
+ }
+
+ // no way to set text_decoration_stroke directly, it always comes from stroke
+ if (!style->text_decoration_stroke.set) {
+ sp_style_merge_ipaint(style, &style->text_decoration_stroke, &style->stroke);
+ style->text_decoration_stroke_opacity = style->stroke_opacity;
+ style->text_decoration_stroke_width = style->stroke_width;
+ }
+ }
+ else if(style->text_decoration_line.active || style->text_decoration_line.inherit) {
+ if (!style->text_decoration_fill.set || style->text_decoration_fill.inherit) {
+ sp_style_merge_ipaint(style, &style->text_decoration_fill, &parent->text_decoration_fill);
+ }
+ if (!style->text_decoration_stroke.set || style->text_decoration_stroke.inherit) {
+ sp_style_merge_ipaint(style, &style->text_decoration_stroke, &parent->text_decoration_stroke);
+ }
+ if (!style->text_decoration_fill_opacity.set || style->text_decoration_fill_opacity.inherit) {
+ style->text_decoration_fill_opacity.value = parent->text_decoration_fill_opacity.value;
+ }
+ if (!style->text_decoration_stroke_opacity.set || style->text_decoration_stroke_opacity.inherit) {
+ style->text_decoration_stroke_opacity.value = parent->text_decoration_stroke_opacity.value;
+ }
+ if (!style->text_decoration_stroke_width.set || style->text_decoration_stroke_width.inherit) {
+ style->text_decoration_stroke_width.computed = parent->text_decoration_stroke_width.computed;
+ } else {
+ /* Update computed value for any change in font inherited from parent. */
+ double const em = style->font_size.computed;
+ if (style->text_decoration_stroke_width.unit == SP_CSS_UNIT_EM) {
+ style->text_decoration_stroke_width.computed = style->text_decoration_stroke_width.value * em;
+ }
+ else if (style->text_decoration_stroke_width.unit == SP_CSS_UNIT_EX) {
+ double const ex = em * 0.5; // fixme: Get x height from libnrtype or pango
+ style->text_decoration_stroke_width.computed = style->text_decoration_stroke_width.value * ex;
+ }
+ }
+ }
+
/* Markers - Free the old value and make copy of the new */
for (unsigned i = SP_MARKER_LOC; i < SP_MARKER_LOC_QTY; i++) {
if (!style->marker[i].set || style->marker[i].inherit) {
@@ -2258,8 +2355,9 @@
* special fill/stroke inheritance mentioned by the spec.
*/
sp_style_merge_prop_from_dying_parent( style->text_decoration_line, parent->text_decoration_line);
- sp_style_merge_prop_from_dying_parent(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);
+ sp_style_merge_prop_from_dying_parent(style->text_decoration_style, parent->text_decoration_style); //CSS3
+ sp_style_merge_prop_from_dying_parent(style->text_decoration_fill_opacity, parent->text_decoration_fill_opacity);
+ sp_style_merge_prop_from_dying_parent(style->text_decoration_stroke_opacity, parent->text_decoration_stroke_opacity);
//nyi: font-size-adjust, // | none | inherit
//nyi: glyph-orientation-horizontal,
@@ -2282,6 +2380,7 @@
SPILength SPStyle::*const lfields[] = {
&SPStyle::stroke_width,
+ &SPStyle::text_decoration_stroke_width,
&SPStyle::text_indent
};
for (unsigned i = 0; i < G_N_ELEMENTS(lfields); ++i) {
@@ -2453,7 +2552,9 @@
SPIPaint SPStyle::*const fields[] = {
&SPStyle::color,
&SPStyle::fill,
- &SPStyle::stroke
+ &SPStyle::stroke,
+ &SPStyle::text_decoration_fill,
+ &SPStyle::text_decoration_stroke
};
for (unsigned i = 0; i < G_N_ELEMENTS(fields); ++i) {
SPIPaint SPStyle::*const fld = fields[i];
@@ -2710,8 +2811,9 @@
/* 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);
+ // SVG2/CSS3 only supports "color", use whatever is in fill
p += sp_style_write_itextdecoration(p, c + BMAX - p, "text-decoration",
- &style->text_decoration_line, &style->text_decoration_style, &style->text_decoration_color,
+ &style->text_decoration_line, &style->text_decoration_style, &style->text_decoration_fill,
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);
@@ -2892,9 +2994,10 @@
/* 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);
+ // SVG2/CSS3 only supports "color", use whatever is in fill
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,
+ &from->text_decoration_line, &from->text_decoration_style, &from->text_decoration_fill,
+ &to->text_decoration_line, &to->text_decoration_style, &to->text_decoration_fill,
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);
@@ -3048,11 +3151,25 @@
delete style->fill.value.href;
style->fill.value.href = NULL;
}
+
style->stroke_ps_modified_connection.disconnect();
if (style->stroke.value.href) {
delete style->stroke.value.href;
style->stroke.value.href = NULL;
}
+
+ style->text_decoration_fill_ps_modified_connection.disconnect();
+ if (style->text_decoration_fill.value.href) {
+ delete style->text_decoration_fill.value.href;
+ style->text_decoration_fill.value.href = NULL;
+ }
+
+ style->text_decoration_stroke_ps_modified_connection.disconnect();
+ if (style->text_decoration_stroke.value.href) {
+ delete style->text_decoration_stroke.value.href;
+ style->text_decoration_stroke.value.href = NULL;
+ }
+
style->filter_modified_connection.disconnect();
if (style->filter.href) {
delete style->filter.href;
@@ -3087,6 +3204,12 @@
style->stroke.value.href = new SPPaintServerReference(document);
style->stroke.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), style));
+
+ style->text_decoration_fill.value.href = new SPPaintServerReference(document);
+ style->text_decoration_fill.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_text_decoration_fill_paint_server_ref_changed), style));
+
+ style->text_decoration_stroke.value.href = new SPPaintServerReference(document);
+ style->text_decoration_stroke.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_text_decoration_stroke_paint_server_ref_changed), style));
}
style->text = text;
@@ -3133,6 +3256,8 @@
style->text_decoration_line.overline = FALSE;
style->text_decoration_line.line_through = FALSE;
style->text_decoration_line.blink = FALSE;
+ style->text_decoration_line.noneSet = FALSE;
+ style->text_decoration_line.active = FALSE;
style->text_decoration_style.set = FALSE;
style->text_decoration_style.inherit = FALSE;
@@ -3142,7 +3267,20 @@
style->text_decoration_style.dashed = FALSE;
style->text_decoration_style.wavy = FALSE;
- style->text_decoration_color.clear();
+ style->text_decoration_stroke.clear();
+ style->text_decoration_stroke_opacity.set = FALSE;
+ style->text_decoration_stroke_opacity.inherit = FALSE;
+ style->text_decoration_stroke_opacity.value = SP_SCALE24_MAX;
+
+ style->text_decoration_fill.clear();
+ style->text_decoration_fill_opacity.set = FALSE;
+ style->text_decoration_fill_opacity.inherit = FALSE;
+ style->text_decoration_fill_opacity.value = SP_SCALE24_MAX;
+
+ style->text_decoration_stroke_width.set = FALSE;
+ style->text_decoration_stroke_width.inherit = FALSE;
+ style->text_decoration_stroke_width.unit = SP_CSS_UNIT_NONE;
+ style->text_decoration_stroke_width.value = style->stroke_width.computed = 1.0;
style->line_height.set = FALSE;
style->line_height.inherit = FALSE;
@@ -3673,16 +3811,21 @@
*/
static void
sp_style_read_itextdecorationLine(SPITextDecorationLine *line, gchar const *str){
+ line->set = false;
+ line->inherit = false;
+ line->underline = false;
+ line->overline = false;
+ line->line_through = false;
+ line->blink = false;
+ line->noneSet = false;
+ line->active = false;
if (!strcmp(str, "inherit")) {
line->set = true;
line->inherit = true;
+ // active will be set from parent
} else if (!strcmp(str, "none")) {
line->set = true;
- line->inherit = false;
- line->underline = false;
- line->overline = false;
- line->line_through = false;
- line->blink = false;
+ line->noneSet = true;
} else {
bool found_one = false;
bool hit_one = false;
@@ -3692,8 +3835,9 @@
bool found_overline = false;
bool found_line_through = false;
bool found_blink = false;
+ bool found_none = false;
- // this method ignores inlineid keys and extra delimiters, so " ,,, blink hello" will set blink and ignore hello
+ // this method ignores inlined keys and extra delimiters, so " ,,, blink hello" will set blink and ignore hello
const gchar *hstr = str;
while (1) {
if (*str == ' ' || *str == ',' || *str == '\0'){
@@ -3705,7 +3849,7 @@
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; }
+ if ((slen == 4) && strneq(hstr, "none", slen)){ found_none = true; break; }
hit_one = false; // whatever this thing is, we do not recognize it
break;
@@ -3718,15 +3862,15 @@
}
if (found_one) {
line->set = true;
- line->inherit = false;
+ line->noneSet = found_none;
line->underline = found_underline;
line->overline = found_overline;
line->line_through = found_line_through;
line->blink = found_blink;
+ line->active = found_underline | found_overline | found_line_through | found_blink;
}
else {
- line->set = false;
- line->inherit = false;
+ // all fields stay false
}
}
}
@@ -4360,6 +4504,8 @@
|| (a->overline != b->overline )
|| (a->line_through != b->line_through)
|| (a->blink != b->blink )
+ || (a->noneSet != b->noneSet )
+ || (a->active != b->active )
);
}
@@ -4417,26 +4563,33 @@
){
os << key << ":";
if (line->inherit || style->inherit || color->inherit) {
- os << " inherit";
+ os << "inherit";
+ }
+ else if (line->noneSet) {
+ os << "none";
}
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;
- }
+ if (line->underline) { os << "underline "; }
+ if (line->overline) { os << "overline "; }
+ if (line->line_through) { os << "line-through "; }
+ if (line->blink) { os << "blink "; }
+
+ // CSS 3
+ if(style->isdouble || style->dotted || style->dashed || style->wavy || color->set) {
+ // color must be written if style is, and vice versa
+ 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 "; }
+
+ if (color->inherit) { os << "inherit"; }
+ else {
+ char color_buf[8];
+ sp_svg_write_color(color_buf, sizeof(color_buf), color->value.color.toRGBA32( 0 ));
+ os << color_buf;
+ }
+ }
}
else {
os << "none";
@@ -4875,6 +5028,9 @@
if (style->text_rendering.set) {
repr->setAttribute("text-rendering", NULL);
}
+ if (style->text_decoration_line.set) {
+ repr->setAttribute("text_decoration", NULL); // not necessary to shut off style and color separately.
+ }
}
/**
=== modified file 'src/style.h'
--- src/style.h 2013-12-10 12:40:42 +0000
+++ src/style.h 2014-02-10 20:22:34 +0000
@@ -133,6 +133,8 @@
#define SP_STYLE_FILL_SERVER(s) ((const_cast (s))->getFillPaintServer())
#define SP_STYLE_STROKE_SERVER(s) ((const_cast (s))->getStrokePaintServer())
+#define SP_STYLE_TEXT_DECORATION_FILL_SERVER(s) ((const_cast (s))->getTextDecorationFillPaintServer())
+#define SP_STYLE_TEXT_DECORATION_STROKE_SERVER(s) ((const_cast (s))->getTextDecorationStrokePaintServer())
/// Paint type internal to SPStyle.
struct SPIPaint {
@@ -226,6 +228,8 @@
unsigned overline : 1;
unsigned line_through : 1;
unsigned blink : 1; // "Conforming user agents are not required to support this value." yay!
+ unsigned noneSet : 1;
+ unsigned active : 1; // set whenever a decoration will be drawn, whether set or inherited
};
// CSS3 2.2
@@ -311,7 +315,11 @@
/** CSS 3 2.1, 2.2, 2.3 */
/** Not done yet, test_decoration3 = css3 2.4*/
SPITextDecorationLine text_decoration_line;
- SPIPaint text_decoration_color;
+ SPIPaint text_decoration_fill;
+ SPIPaint text_decoration_stroke;
+ SPIScale24 text_decoration_fill_opacity;
+ SPIScale24 text_decoration_stroke_opacity;
+ SPILength text_decoration_stroke_width;
SPITextDecorationStyle text_decoration_style;
// used to implement text_decoration, not saved to or read from SVG file
@@ -433,6 +441,8 @@
sigc::connection filter_modified_connection;
sigc::connection fill_ps_modified_connection;
sigc::connection stroke_ps_modified_connection;
+ sigc::connection text_decoration_fill_ps_modified_connection;
+ sigc::connection text_decoration_stroke_ps_modified_connection;
SPObject *getFilter() { return (filter.href) ? filter.href->getObject() : NULL; }
SPObject const *getFilter() const { return (filter.href) ? filter.href->getObject() : NULL; }
@@ -445,6 +455,14 @@
SPPaintServer *getStrokePaintServer() { return (stroke.value.href) ? stroke.value.href->getObject() : NULL; }
SPPaintServer const *getStrokePaintServer() const { return (stroke.value.href) ? stroke.value.href->getObject() : NULL; }
gchar const *getStrokeURI() const { return (stroke.value.href) ? stroke.value.href->getURI()->toString() : NULL; }
+
+ SPPaintServer *getTextDecorationFillServer() { return (text_decoration_fill.value.href) ? text_decoration_fill.value.href->getObject() : NULL; }
+ SPPaintServer *getTextDecorationFillPaintServer() const { return (text_decoration_fill.value.href) ? text_decoration_fill.value.href->getObject() : NULL; }
+ gchar const *getTextDecorationFillURI() const { return (text_decoration_fill.value.href) ? text_decoration_fill.value.href->getURI()->toString() : NULL; }
+
+ SPPaintServer *getTextDecorationStrokeServer() { return (text_decoration_stroke.value.href) ? text_decoration_stroke.value.href->getObject() : NULL; }
+ SPPaintServer *getTextDecorationStrokePaintServer() const { return (text_decoration_stroke.value.href) ? text_decoration_stroke.value.href->getObject() : NULL; }
+ gchar const *getTextDecorationStrokeURI() const { return (text_decoration_stroke.value.href) ? text_decoration_stroke.value.href->getURI()->toString() : NULL; }
};
SPStyle *sp_style_new(SPDocument *document);
=== modified file 'src/text-editing.cpp'
--- src/text-editing.cpp 2013-10-28 09:31:07 +0000
+++ src/text-editing.cpp 2014-02-10 20:22:34 +0000
@@ -776,7 +776,7 @@
bool has_text_decoration = false;
gchar const *root_style = (item)->getRepr()->attribute("style");
- if(strstr(root_style,"text-decoration"))has_text_decoration = true;
+ if(root_style && 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
@@ -2035,7 +2035,7 @@
roundtrippability. */
bool has_text_decoration = false;
gchar const *root_style = (text)->getRepr()->attribute("style");
- if(strstr(root_style,"text-decoration"))has_text_decoration = true;
+ if(root_style && 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
=== modified file 'src/ui/dialog/text-edit.cpp'
--- src/ui/dialog/text-edit.cpp 2014-01-02 17:34:58 +0000
+++ src/ui/dialog/text-edit.cpp 2014-02-10 20:22:34 +0000
@@ -68,6 +68,7 @@
TextEdit::TextEdit()
: UI::Widget::Panel("", "/dialogs/textandfont", SP_VERB_DIALOG_TEXT),
font_label(_("_Font"), true),
+ decorate_label(_("_Decorate"), true),
layout_frame(),
text_label(_("_Text"), true),
setasdefault_button(_("Set as _default")),
@@ -93,11 +94,95 @@
fsel = SP_FONT_SELECTOR(fontsel);
fontsel_hbox.pack_start(*Gtk::manage(Glib::wrap(fontsel)), true, true);
+ Gtk::HBox *hb;
+// Gtk::HBox *f = new Gtk::HBox(false, 0);
+// f->show();
+// add(*f);
+#if WITH_GTKMM_3_0
+ table = new Gtk::Grid();
+ table->set_border_width(4);
+ table->set_row_spacing(4);
+#else
+ table = new Gtk::Table(3, 6, false);
+ table->set_border_width(4);
+ table->set_row_spacings(4);
+#endif
+ table->show();
+// f->add(*table);
+ decorate_hbox.add(*table);
+ gint i = 0;
+
+ /* Action type */
+ // TRANSLATORS: The text decoration action type specifies type of text decoration line.
+ // It can be "none", "set" (add a text-decoration field to the style), "unset".
+ spw_label(table, _("Text Decoration Action:"), 0, i, NULL);
+ hb = spw_hbox(table, 3, 1, i);
+// none is the absence of all the others.
+ hbRadioButton(hb, &text_decoration_line_none, _("Decorate none"), INKSCAPE_ICON("paint-none" ), NULL);
+ hbRadioButton(hb, &text_decoration_line_act, _("Decorate set"), INKSCAPE_ICON("text-decorate-line-underline" ), &text_decoration_line_none);
+ hbRadioButton(hb, &text_decoration_line_inherit, _("unset Decorate - (make it undefined so it can be inherited)"), INKSCAPE_ICON("paint-unknown" ), &text_decoration_line_none);
+ i++;
+
+ /* Line type */
+ // TRANSLATORS: The text decoration line type specifies the type of text decoration line.
+ // It can be "none", "underline", "overline", "strikethrough", or "blink".
+ spw_label(table, _("Text Decoration Line:"), 0, i, NULL);
+ hb = spw_hbox(table, 3, 1, i);
+
+ /* Text Decoration Line buttons */
+
+ hbToggleButton(hb, &text_decoration_line_under, _("Decorate with underline"), INKSCAPE_ICON("text-decorate-line-underline" ));
+ hbToggleButton(hb, &text_decoration_line_over, _("Decorate with overline"), INKSCAPE_ICON("text-decorate-line-overline" ));
+ hbToggleButton(hb, &text_decoration_line_through, _("Decorate with strikethrough"), INKSCAPE_ICON("text-decorate-line-strikethrough"));
+ hbToggleButton(hb, &text_decoration_line_blink, _("Decorate with blink"), INKSCAPE_ICON("text-decorate-line-blink" ));
+ i++;
+
+ /* Style type */
+ // TRANSLATORS: The text decoration style type specifies the type of text decoration style.
+ // It can be "solid", "dotted", "dashed", "double", or "wavy".
+ spw_label(table, _("Text Decoration Style:"), 0, i, NULL);
+ hb = spw_hbox(table, 3, 1, i);
+
+ /* Text Decoration Style buttons */
+ hbRadioButton(hb, &text_decoration_style_solid, _("Decorate with solid"), INKSCAPE_ICON("text-decorate-style-solid" ), NULL);
+ hbRadioButton(hb, &text_decoration_style_dotted, _("Decorate with dotted"), INKSCAPE_ICON("text-decorate-style-dotted"), &text_decoration_style_solid);
+ hbRadioButton(hb, &text_decoration_style_dashed, _("Decorate with dashed"), INKSCAPE_ICON("text-decorate-style-dashed"), &text_decoration_style_solid);
+ hbRadioButton(hb, &text_decoration_style_double, _("Decorate with double"), INKSCAPE_ICON("text-decorate-style-double"), &text_decoration_style_solid);
+ hbRadioButton(hb, &text_decoration_style_wavy, _("Decorate with wavy"), INKSCAPE_ICON("text-decorate-style-wavy" ), &text_decoration_style_solid);
+ i++;
+
+ /* Text Decoration Color */
+ // TRANSLATORS: The text decoration color type specifies the color of the text decoration.
+ // It can be "Text" or "Custom"
+ spw_label(table, _("Text Decoration Color:"), 0, i, NULL);
+ hb = spw_hbox(table, 3, 1, i);
+
+ /* Text Decoration Color buttons */
+ hbRadioButton(hb, &text_decoration_color_text, _("Decorate Color from Text"), INKSCAPE_ICON("text-decorate-color-text" ), NULL);
+ hbRadioButton(hb, &text_decoration_color_custom, _("Decorate Custom Color"), INKSCAPE_ICON("text-decorate-color-custom"), &text_decoration_color_text);
+ i++;
+ // FIXME, add a color selector here, for now the custom one always sets 0xFF00FF
+
+ /* Comment on CSS 2,3. SVG 1.1 only supports CSS 2, future SVG will probably support CSS 3.
+ Mixing CSS 3 and CSS 2 text-decoration syntax can produce some odd results. For instance, if
+ on outer tspan has "overline wavy" set an inner tspan of just "underline" will also be wavy. Other than
+ throwing a warning there is not much one can do to avoid this sort of issue, because CSS 2 has no
+ way to say "not wavy" and CSS 2 syntax is still compatible with CSS 3 - there is no way to tell if CSS 2
+ or CSS 3 was intended. */
+ /* Text Decoration CSS level */
+ // TRANSLATORS: The text decoration CSS level supported by this menu for text decoration.
+ // It can be "2" or "3"
+ spw_label(table, _("CSS Level:"), 0, i, NULL);
+ hb = spw_hbox(table, 3, 1, i);
+
+ /* Text Decoration Color buttons */
+ hbRadioButton(hb, &text_decoration_css_2, _("CSS 2"), INKSCAPE_ICON("CSS-2" ), NULL);
+ hbRadioButton(hb, &text_decoration_css_3, _("CSS 3"), INKSCAPE_ICON("CSS-3"), &text_decoration_css_2);
+ i++;
+
/* Align buttons */
- styleButton(&align_left, _("Align left"), INKSCAPE_ICON("format-justify-left"), NULL);
- styleButton(&align_center, _("Align center"), INKSCAPE_ICON("format-justify-center"), &align_left);
- styleButton(&align_right, _("Align right"), INKSCAPE_ICON("format-justify-right"), &align_left);
- styleButton(&align_justify, _("Justify (only flowed text)"), INKSCAPE_ICON("format-justify-fill"), &align_left);
+ hbRadioButton(&layout_hbox, &align_left, _("Align left"), INKSCAPE_ICON("format-justify-left"), NULL);
+ hbRadioButton(&layout_hbox, &align_center, _("Align center"), INKSCAPE_ICON("format-justify-center"), &align_left);
#if WITH_GTKMM_3_0
align_sep.set_orientation(Gtk::ORIENTATION_VERTICAL);
@@ -106,8 +191,8 @@
layout_hbox.pack_start(align_sep, false, false, 10);
/* Direction buttons */
- styleButton(&text_horizontal, _("Horizontal text"), INKSCAPE_ICON("format-text-direction-horizontal"), NULL);
- styleButton(&text_vertical, _("Vertical text"), INKSCAPE_ICON("format-text-direction-vertical"), &text_horizontal);
+ hbRadioButton(&layout_hbox, &text_horizontal, _("Horizontal text"), INKSCAPE_ICON("format-text-direction-horizontal"), NULL);
+ hbRadioButton(&layout_hbox, &text_vertical, _("Vertical text"), INKSCAPE_ICON("format-text-direction-vertical"), &text_horizontal);
#if WITH_GTKMM_3_0
text_sep.set_orientation(Gtk::ORIENTATION_VERTICAL);
@@ -133,6 +218,7 @@
layout_frame.set_padding(4,4,4,4);
layout_frame.add(layout_hbox);
+
// Text start Offset
{
startOffset = gtk_combo_box_text_new_with_entry ();
@@ -147,16 +233,14 @@
gtk_widget_set_tooltip_text(startOffset, _("Text path offset"));
#if WITH_GTKMM_3_0
- Gtk::Separator *sep = Gtk::manage(new Gtk::Separator());
- sep->set_orientation(Gtk::ORIENTATION_VERTICAL);
-#else
- Gtk::VSeparator *sep = Gtk::manage(new Gtk::VSeparator);
+ path_offset_sep.set_orientation(Gtk::ORIENTATION_VERTICAL);
#endif
- layout_hbox.pack_start(*sep, false, false, 10);
+ layout_hbox.pack_start(path_offset_sep, false, false, 10);
layout_hbox.pack_start(*Gtk::manage(Glib::wrap(startOffset)), false, false);
}
+
/* Font preview */
preview_label.set_ellipsize(Pango::ELLIPSIZE_END);
preview_label.set_justify(Gtk::JUSTIFY_CENTER);
@@ -193,9 +277,10 @@
gtk_text_view_set_editable (GTK_TEXT_VIEW (text_view), TRUE);
scroller.add(*Gtk::manage(Glib::wrap(text_view)));
text_vbox.pack_start(scroller, true, true, 0);
-
+
notebook.append_page(font_vbox, font_label);
notebook.append_page(text_vbox, text_label);
+ notebook.append_page(decorate_hbox, decorate_label);
/* Buttons */
setasdefault_button.set_use_underline(true);
@@ -233,7 +318,7 @@
deskTrack.disconnect();
}
-void TextEdit::styleButton(Gtk::RadioButton *button, gchar const *tooltip, gchar const *icon_name, Gtk::RadioButton *group_button )
+void TextEdit::hbRadioButton(Gtk::HBox *hb, Gtk::RadioButton *button, gchar const *tooltip, gchar const *icon_name, Gtk::RadioButton *group_button )
{
GtkWidget *icon = sp_icon_new( Inkscape::ICON_SIZE_SMALL_TOOLBAR, icon_name );
if (!GTK_IS_IMAGE(icon)) {
@@ -251,7 +336,23 @@
button->set_mode(false);
button->signal_clicked().connect(sigc::mem_fun(*this, &TextEdit::onToggle));
- layout_hbox.pack_start(*button, false, false);
+ hb->pack_start(*button, false, false);
+}
+
+void TextEdit::hbToggleButton(Gtk::HBox *hb, Gtk::ToggleButton *button, gchar const *tooltip, gchar const *icon_name )
+{
+ GtkWidget *icon = sp_icon_new( Inkscape::ICON_SIZE_SMALL_TOOLBAR, icon_name );
+ if (!GTK_IS_IMAGE(icon)) {
+ icon = gtk_image_new_from_icon_name ( icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR );
+ }
+
+ button->add(*Gtk::manage(Glib::wrap(icon)));
+ button->set_tooltip_text(tooltip);
+ button->set_relief(Gtk::RELIEF_NONE);
+ button->set_mode(false);
+ button->signal_clicked().connect(sigc::mem_fun(*this, &TextEdit::onToggle));
+
+ hb->pack_start(*button, false, false);
}
void TextEdit::onSelectionModified(guint flags )
@@ -332,11 +433,12 @@
int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
+ int result_text_decorations = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_TEXTDECORATION);
// If querying returned nothing, read the style from the text tool prefs (default style for new texts)
// (Ok to not get a font specification - must just rely on the family and style in that case)
if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING
- || result_numbers == QUERY_STYLE_NOTHING) {
+ || result_numbers == QUERY_STYLE_NOTHING || result_text_decorations == QUERY_STYLE_NOTHING) {
sp_style_read_from_prefs(query, "/tools/text");
}
@@ -369,6 +471,37 @@
align_right.set_active();
}
+ text_decoration_line_under.set_active(false);
+ text_decoration_line_over.set_active(false);
+ text_decoration_line_through.set_active(false);
+ text_decoration_line_blink.set_active(false);
+ if (query->text_decoration_line.set) {
+ text_decoration_line_act.set_active();
+ if (query->text_decoration_line.underline ) text_decoration_line_under.set_active();
+ if (query->text_decoration_line.overline ) text_decoration_line_over.set_active();
+ if (query->text_decoration_line.line_through) text_decoration_line_through.set_active();
+ if (query->text_decoration_line.blink ) text_decoration_line_blink.set_active();
+ }
+ else if (query->text_decoration_line.inherit) {
+ text_decoration_line_inherit.set_active();
+ }
+ else {
+ text_decoration_line_none.set_active();
+ }
+ text_decoration_style_solid.set_active(false);
+ text_decoration_style_dotted.set_active(false);
+ text_decoration_style_dashed.set_active(false);
+ text_decoration_style_double.set_active(false);
+ text_decoration_style_wavy.set_active(false);
+ if (query->text_decoration_style.solid) { text_decoration_style_solid.set_active(); }
+ else if (query->text_decoration_style.dotted) { text_decoration_style_dotted.set_active(); }
+ else if (query->text_decoration_style.dashed) { text_decoration_style_dashed.set_active(); }
+ else if (query->text_decoration_style.isdouble) { text_decoration_style_double.set_active(); }
+ else if (query->text_decoration_style.wavy) { text_decoration_style_wavy.set_active(); }
+ else { text_decoration_style_solid.set_active(); }
+ /* Because of the odd way some of the text_decoration information is inherited there is no
+ simple way to determine if CSS 3 or CSS 2 is in use all of the time. So do not even try.*/
+
if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
text_horizontal.set_active();
} else {
@@ -513,6 +646,74 @@
} else {
sp_repr_css_set_property (css, "writing-mode", "tb");
}
+
+ /* Text decoration is a little tricky, as it may be specified using CSS2's
+ "text-decoration", or it may be specified using CSS3's variant of the same (with
+ line style and color parameters) or CSS3's separate text-decoration-color,
+ text-decoration-line, and text-decoration-style. Always employ CSS2 style if
+ that is sufficient (color and style are defaults)
+ */
+
+ gchar td_color_string[16] = "";
+ gchar td_style_string[16] = "";
+ bool use_css3 = false;
+ if (text_decoration_css_3.get_active()) { //FixMe Setting CSS2 should gray out CSS3 options in the dialog
+ use_css3 = true;
+ }
+
+ if(use_css3){
+ if(text_decoration_color_custom.get_active()) {
+ g_snprintf(td_color_string, sizeof(td_color_string), "%s", "#FF00FF"); //FIXME, should really get a custom color from somewhere
+ }
+ else {
+ g_snprintf(td_color_string, sizeof(td_color_string), "%s", "currentColor");
+ }
+
+ // one of these will always be set
+ if( text_decoration_style_solid.get_active()) {
+ g_snprintf(td_style_string, sizeof(td_style_string), "%s", "solid");
+ }
+ else if (text_decoration_style_double.get_active()) {
+ g_snprintf(td_style_string, sizeof(td_style_string), "%s", "double");
+ }
+ else if (text_decoration_style_dashed.get_active()) {
+ g_snprintf(td_style_string, sizeof(td_style_string), "%s", "dashed");
+ }
+ else if (text_decoration_style_dotted.get_active()) {
+ g_snprintf(td_style_string, sizeof(td_style_string), "%s", "dotted");
+ }
+ else { // must be wavy
+ g_snprintf(td_style_string, sizeof(td_style_string), "%s", "wavy");
+ }
+ }
+
+
+ if(text_decoration_line_none.get_active()) { // this overrides bits and erases any text-decoration that might be there
+ sp_repr_css_set_property(css, "text-decoration", "none");
+ }
+ else if(text_decoration_line_inherit.get_active()) { // this overrides bits and sets inherit (write nothing, same as explicit inherit)
+ sp_repr_css_unset_property(css, "text-decoration");
+ }
+ else { // a decoration will be shown if a line type was specified, text_decoration_line_inherit.get_active() is true
+ // from 0 to all 4 of these might be set
+ gchar td_line_string[64] = "";
+ g_snprintf(td_line_string, sizeof(td_line_string), "%s%s%s%s",
+ (text_decoration_line_under.get_active() ? "underline " : ""),
+ (text_decoration_line_over.get_active() ? "overline " : ""),
+ (text_decoration_line_through.get_active() ? "line-through " : ""),
+ (text_decoration_line_blink.get_active() ? "blink " : "")
+ );
+ gchar td_full_string[96] = "";
+ if(td_line_string[0]){ // there is a text decoration
+ if (use_css3) {
+ g_snprintf(td_full_string, sizeof(td_full_string), "%s %s %s", td_line_string, td_style_string, td_color_string);
+ }
+ else {
+ g_snprintf(td_full_string, sizeof(td_full_string), "%s", td_line_string);
+ }
+ }
+ sp_repr_css_set_property(css, "text-decoration", td_full_string);
+ }
// Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
// sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
=== modified file 'src/ui/dialog/text-edit.h'
--- src/ui/dialog/text-edit.h 2013-06-07 03:18:19 +0000
+++ src/ui/dialog/text-edit.h 2014-02-10 20:22:34 +0000
@@ -33,9 +33,15 @@
#include
#include
#include
+#if WITH_GTKMM_3_0
+#include
+#else
+#include
+#endif
#include "ui/widget/panel.h"
#include "ui/widget/frame.h"
#include "ui/dialog/desktop-tracker.h"
+#include "widgets/spw-utilities.h"
class SPItem;
struct SPFontSelector;
@@ -146,16 +152,27 @@
SPCSSAttr *fillTextStyle ();
/**
- * Helper function to style radio buttons with icons, tooltips.
+ * Helper function to set radio buttons with icons, tooltips.
*
- * styleButton is used when creating the dialog.
+ * hbRadioButton is used when creating the Decorate dialog.
*
* @param button pointer to the button which is created
* @param tooltip pointer to its tooltip string
* @param iconname string identifying the icon to be shown
* @param group_button group to which the radio button belongs to
*/
- void styleButton(Gtk::RadioButton *button, gchar const *tooltip, gchar const *iconname, Gtk::RadioButton *group_button );
+ void hbRadioButton(Gtk::HBox *hb, Gtk::RadioButton *button, gchar const *tooltip, gchar const *icon_name, Gtk::RadioButton *group_button );
+
+ /**
+ * Helper function to set toggle buttons with icons, tooltips.
+ *
+ * hbToggleButton is used when creating the Decorate dialog.
+ *
+ * @param button pointer to the button which is created
+ * @param tooltip pointer to its tooltip string
+ * @param iconname string identifying the icon to be shown
+ */
+ void hbToggleButton(Gtk::HBox *hb, Gtk::ToggleButton *button, gchar const *tooltip, gchar const *icon_name );
/**
* Can be invoked for setting the desktop. Currently not used.
@@ -178,11 +195,21 @@
*/
Gtk::Notebook notebook;
+#if WITH_GTKMM_3_0
+ Gtk::Grid *table;
+#else
+ Gtk::Table *table;
+#endif
+
Gtk::VBox font_vbox;
Gtk::Label font_label;
Gtk::HBox fontsel_hbox;
SPFontSelector *fsel;
+ Gtk::VBox decorate_vbox;
+ Gtk::Label decorate_label;
+ Gtk::HBox decorate_hbox;
+
Gtk::Alignment layout_frame;
Gtk::HBox layout_hbox;
Gtk::RadioButton align_left;
@@ -196,6 +223,34 @@
Gtk::VSeparator align_sep;
#endif
+#if WITH_GTKMM_3_0
+ Gtk::Separator path_offset_sep;
+#else
+ Gtk::VSeparator path_offset_sep;
+#endif
+
+ Gtk::RadioButton text_decoration_line_none;
+ Gtk::RadioButton text_decoration_line_act;
+ Gtk::RadioButton text_decoration_line_inherit;
+
+ Gtk::ToggleButton text_decoration_line_under;
+ Gtk::ToggleButton text_decoration_line_over;
+ Gtk::ToggleButton text_decoration_line_through;
+ Gtk::ToggleButton text_decoration_line_blink;
+
+ Gtk::RadioButton text_decoration_style_solid;
+ Gtk::RadioButton text_decoration_style_dotted;
+ Gtk::RadioButton text_decoration_style_dashed;
+ Gtk::RadioButton text_decoration_style_double;
+ Gtk::RadioButton text_decoration_style_wavy;
+
+ Gtk::RadioButton text_decoration_color_text;
+ Gtk::RadioButton text_decoration_color_custom;
+
+ Gtk::RadioButton text_decoration_css_2;
+ Gtk::RadioButton text_decoration_css_3;
+
+
Gtk::RadioButton text_vertical;
Gtk::RadioButton text_horizontal;