diff options
Diffstat (limited to 'src/display')
| -rw-r--r-- | src/display/drawing-context.h | 15 | ||||
| -rw-r--r-- | src/display/drawing-text.cpp | 304 | ||||
| -rw-r--r-- | src/display/drawing-text.h | 15 | ||||
| -rw-r--r-- | src/display/nr-style.cpp | 58 | ||||
| -rw-r--r-- | src/display/nr-style.h | 37 |
5 files changed, 409 insertions, 20 deletions
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 |
