summaryrefslogtreecommitdiffstats
path: root/src/display
diff options
context:
space:
mode:
authorMarkus Engel <markus.engel@tum.de>2013-08-30 21:35:42 +0000
committerMarkus Engel <markus.engel@tum.de>2013-08-30 21:35:42 +0000
commit8756c49b91fd4262b6770ac3b2f229209bdae96b (patch)
tree67ac89d7bc7c69e04e811324734dccafc07f33c9 /src/display
parentMerged from trunk (r12465). (diff)
parentMerge emf/wmf work (diff)
downloadinkscape-8756c49b91fd4262b6770ac3b2f229209bdae96b.tar.gz
inkscape-8756c49b91fd4262b6770ac3b2f229209bdae96b.zip
Merged from trunk (r12488).
(bzr r11608.1.120)
Diffstat (limited to 'src/display')
-rw-r--r--src/display/canvas-axonomgrid.cpp101
-rw-r--r--src/display/canvas-grid.cpp129
-rw-r--r--src/display/canvas-grid.h6
-rw-r--r--src/display/drawing-context.h15
-rw-r--r--src/display/drawing-text.cpp304
-rw-r--r--src/display/drawing-text.h15
-rw-r--r--src/display/nr-style.cpp58
-rw-r--r--src/display/nr-style.h37
8 files changed, 476 insertions, 189 deletions
diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp
index 1eadd3fd2..f7a7cb39a 100644
--- a/src/display/canvas-axonomgrid.cpp
+++ b/src/display/canvas-axonomgrid.cpp
@@ -51,8 +51,9 @@
#include "2geom/angle.h"
#include "util/mathfns.h"
#include "round.h"
-#include "helper/units.h"
+#include "util/units.h"
+using Inkscape::Util::unit_table;
enum Dim3 { X=0, Y, Z };
@@ -160,15 +161,15 @@ CanvasAxonomGrid::CanvasAxonomGrid (SPNamedView * nv, Inkscape::XML::Node * in_r
: CanvasGrid(nv, in_repr, in_doc, GRID_AXONOMETRIC)
{
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- gridunit = sp_unit_get_by_abbreviation( prefs->getString("/options/grids/axonom/units").data() );
+ gridunit = new Inkscape::Util::Unit(unit_table.getUnit(prefs->getString("/options/grids/axonom/units")));
if (!gridunit)
- gridunit = &sp_unit_get_by_id(SP_UNIT_PX);
- origin[Geom::X] = sp_units_get_pixels( prefs->getDouble("/options/grids/axonom/origin_x", 0.0), *gridunit );
- origin[Geom::Y] = sp_units_get_pixels( prefs->getDouble("/options/grids/axonom/origin_y", 0.0), *gridunit );
+ gridunit = new Inkscape::Util::Unit(unit_table.getUnit("px"));
+ origin[Geom::X] = Inkscape::Util::Quantity::convert(prefs->getDouble("/options/grids/axonom/origin_x", 0.0), *gridunit, "px");
+ origin[Geom::Y] = Inkscape::Util::Quantity::convert(prefs->getDouble("/options/grids/axonom/origin_y", 0.0), *gridunit, "px");
color = prefs->getInt("/options/grids/axonom/color", 0x0000ff20);
empcolor = prefs->getInt("/options/grids/axonom/empcolor", 0x0000ff40);
empspacing = prefs->getInt("/options/grids/axonom/empspacing", 5);
- lengthy = sp_units_get_pixels( prefs->getDouble("/options/grids/axonom/spacing_y", 1.0), *gridunit );
+ lengthy = Inkscape::Util::Quantity::convert(prefs->getDouble("/options/grids/axonom/spacing_y", 1.0), *gridunit, "px");
angle_deg[X] = prefs->getDouble("/options/grids/axonom/angle_x", 30.0);
angle_deg[Z] = prefs->getDouble("/options/grids/axonom/angle_z", 30.0);
angle_deg[Y] = 0;
@@ -188,63 +189,6 @@ CanvasAxonomGrid::~CanvasAxonomGrid ()
if (snapper) delete snapper;
}
-
-/* fixme: Collect all these length parsing methods and think common sane API */
-
-static gboolean sp_nv_read_length(gchar const *str, guint base, gdouble *val, SPUnit const **unit)
-{
- if (!str) {
- return FALSE;
- }
-
- gchar *u;
- gdouble v = g_ascii_strtod(str, &u);
- if (!u) {
- return FALSE;
- }
- while (isspace(*u)) {
- u += 1;
- }
-
- if (!*u) {
- /* No unit specified - keep default */
- *val = v;
- return TRUE;
- }
-
- if (base & SP_UNIT_DEVICE) {
- if (u[0] && u[1] && !isalnum(u[2]) && !strncmp(u, "px", 2)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_PX);
- *val = v;
- return TRUE;
- }
- }
-
- if (base & SP_UNIT_ABSOLUTE) {
- if (!strncmp(u, "pt", 2)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_PT);
- } else if (!strncmp(u, "mm", 2)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_MM);
- } else if (!strncmp(u, "cm", 2)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_CM);
- } else if (!strncmp(u, "m", 1)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_M);
- } else if (!strncmp(u, "in", 2)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_IN);
- } else if (!strncmp(u, "ft", 2)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_FT);
- } else if (!strncmp(u, "pc", 2)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_PC);
- } else {
- return FALSE;
- }
- *val = v;
- return TRUE;
- }
-
- return FALSE;
-}
-
static gboolean sp_nv_read_opacity(gchar const *str, guint32 *color)
{
if (!str) {
@@ -270,17 +214,20 @@ CanvasAxonomGrid::readRepr()
{
gchar const *value;
if ( (value = repr->attribute("originx")) ) {
- sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[Geom::X], &gridunit);
- origin[Geom::X] = sp_units_get_pixels(origin[Geom::X], *(gridunit));
+ Inkscape::Util::Quantity q = unit_table.getQuantity(value);
+ gridunit = q.unit;
+ origin[Geom::X] = unit_table.getQuantity(value).value("px");
}
if ( (value = repr->attribute("originy")) ) {
- sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[Geom::Y], &gridunit);
- origin[Geom::Y] = sp_units_get_pixels(origin[Geom::Y], *(gridunit));
+ Inkscape::Util::Quantity q = unit_table.getQuantity(value);
+ gridunit = q.unit;
+ origin[Geom::Y] = unit_table.getQuantity(value).value("px");
}
if ( (value = repr->attribute("spacingy")) ) {
- sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &lengthy, &gridunit);
- lengthy = sp_units_get_pixels(lengthy, *(gridunit));
+ Inkscape::Util::Quantity q = unit_table.getQuantity(value);
+ gridunit = q.unit;
+ lengthy = q.value("px");
if (lengthy < 0.0500) lengthy = 0.0500;
}
@@ -419,17 +366,17 @@ _wr.setUpdating (false);
attach_all (*table, widget_array, sizeof(widget_array));
// set widget values
- _rumg->setUnit (gridunit);
+ _rumg->setUnit (gridunit->abbr);
gdouble val;
val = origin[Geom::X];
- val = sp_pixels_get_units (val, *(gridunit));
+ val = Inkscape::Util::Quantity::convert(val, "px", *gridunit);
_rsu_ox->setValue (val);
val = origin[Geom::Y];
- val = sp_pixels_get_units (val, *(gridunit));
+ val = Inkscape::Util::Quantity::convert(val, "px", *gridunit);
_rsu_oy->setValue (val);
val = lengthy;
- double gridy = sp_pixels_get_units (val, *(gridunit));
+ double gridy = Inkscape::Util::Quantity::convert(val, "px", *gridunit);
_rsu_sy->setValue (gridy);
_rsu_ax->setValue(angle_deg[X]);
@@ -458,17 +405,17 @@ CanvasAxonomGrid::updateWidgets()
_rcb_enabled.setActive(snapper->getEnabled());
}
- _rumg.setUnit (gridunit);
+ _rumg.setUnit (gridunit->abbr);
gdouble val;
val = origin[Geom::X];
- val = sp_pixels_get_units (val, *(gridunit));
+ val = Inkscape::Util::Quantity::convert(val, &px, gridunit);
_rsu_ox.setValue (val);
val = origin[Geom::Y];
- val = sp_pixels_get_units (val, *(gridunit));
+ val = Inkscape::Util::Quantity::convert(val, &px, gridunit);
_rsu_oy.setValue (val);
val = lengthy;
- double gridy = sp_pixels_get_units (val, *(gridunit));
+ double gridy = Inkscape::Util::Quantity::convert(val, &px, gridunit);
_rsu_sy.setValue (gridy);
_rsu_ax.setValue(angle_deg[X]);
diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp
index ee5ad0945..ef32c113b 100644
--- a/src/display/canvas-grid.cpp
+++ b/src/display/canvas-grid.cpp
@@ -42,7 +42,7 @@
#include "display/canvas-grid.h"
#include "display/sp-canvas-group.h"
#include "document.h"
-#include "helper/units.h"
+#include "util/units.h"
#include "inkscape.h"
#include "preferences.h"
#include "sp-namedview.h"
@@ -55,6 +55,7 @@
#include "display/sp-canvas.h"
using Inkscape::DocumentUndo;
+using Inkscape::Util::unit_table;
namespace Inkscape {
@@ -398,11 +399,11 @@ void CanvasGrid::setOrigin(Geom::Point const &origin_px)
gdouble val;
val = origin_px[Geom::X];
- val = sp_pixels_get_units (val, *gridunit);
- os_x << val << sp_unit_get_abbreviation(gridunit);
+ val = Inkscape::Util::Quantity::convert(val, "px", *gridunit);
+ os_x << val << gridunit->abbr;
val = origin_px[Geom::Y];
- val = sp_pixels_get_units (val, *gridunit);
- os_y << val << sp_unit_get_abbreviation(gridunit);
+ val = Inkscape::Util::Quantity::convert(val, "px", *gridunit);
+ os_y << val << gridunit->abbr;
repr->setAttribute("originx", os_x.str().c_str());
repr->setAttribute("originy", os_y.str().c_str());
}
@@ -488,17 +489,17 @@ CanvasXYGrid::CanvasXYGrid (SPNamedView * nv, Inkscape::XML::Node * in_repr, SPD
: CanvasGrid(nv, in_repr, in_doc, GRID_RECTANGULAR)
{
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- gridunit = sp_unit_get_by_abbreviation( prefs->getString("/options/grids/xy/units").data() );
+ gridunit = new Inkscape::Util::Unit(unit_table.getUnit(prefs->getString("/options/grids/xy/units")));
if (!gridunit) {
- gridunit = &sp_unit_get_by_id(SP_UNIT_PX);
+ gridunit = new Inkscape::Util::Unit(unit_table.getUnit("px"));
}
- origin[Geom::X] = sp_units_get_pixels(prefs->getDouble("/options/grids/xy/origin_x", 0.0), *gridunit);
- origin[Geom::Y] = sp_units_get_pixels(prefs->getDouble("/options/grids/xy/origin_y", 0.0), *gridunit);
+ origin[Geom::X] = Inkscape::Util::Quantity::convert(prefs->getDouble("/options/grids/xy/origin_x", 0.0), *gridunit, "px");
+ origin[Geom::Y] = Inkscape::Util::Quantity::convert(prefs->getDouble("/options/grids/xy/origin_y", 0.0), *gridunit, "px");
color = prefs->getInt("/options/grids/xy/color", 0x0000ff20);
empcolor = prefs->getInt("/options/grids/xy/empcolor", 0x0000ff40);
empspacing = prefs->getInt("/options/grids/xy/empspacing", 5);
- spacing[Geom::X] = sp_units_get_pixels(prefs->getDouble("/options/grids/xy/spacing_x", 0.0), *gridunit);
- spacing[Geom::Y] = sp_units_get_pixels(prefs->getDouble("/options/grids/xy/spacing_y", 0.0), *gridunit);
+ spacing[Geom::X] = Inkscape::Util::Quantity::convert(prefs->getDouble("/options/grids/xy/spacing_x", 0.0), *gridunit, "px");
+ spacing[Geom::Y] = Inkscape::Util::Quantity::convert(prefs->getDouble("/options/grids/xy/spacing_y", 0.0), *gridunit, "px");
render_dotted = prefs->getBool("/options/grids/xy/dotted", false);
snapper = new CanvasXYGridSnapper(this, &namedview->snap_manager, 0);
@@ -511,64 +512,6 @@ CanvasXYGrid::~CanvasXYGrid ()
if (snapper) delete snapper;
}
-
-/* fixme: Collect all these length parsing methods and think common sane API */
-
-static gboolean
-sp_nv_read_length(gchar const *str, guint base, gdouble *val, SPUnit const **unit)
-{
- if (!str) {
- return FALSE;
- }
-
- gchar *u;
- gdouble v = g_ascii_strtod(str, &u);
- if (!u) {
- return FALSE;
- }
- while (isspace(*u)) {
- u += 1;
- }
-
- if (!*u) {
- /* No unit specified - keep default */
- *val = v;
- return TRUE;
- }
-
- if (base & SP_UNIT_DEVICE) {
- if (u[0] && u[1] && !isalnum(u[2]) && !strncmp(u, "px", 2)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_PX);
- *val = v;
- return TRUE;
- }
- }
-
- if (base & SP_UNIT_ABSOLUTE) {
- if (!strncmp(u, "pt", 2)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_PT);
- } else if (!strncmp(u, "mm", 2)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_MM);
- } else if (!strncmp(u, "cm", 2)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_CM);
- } else if (!strncmp(u, "m", 1)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_M);
- } else if (!strncmp(u, "in", 2)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_IN);
- } else if (!strncmp(u, "ft", 2)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_FT);
- } else if (!strncmp(u, "pc", 2)) {
- *unit = &sp_unit_get_by_id(SP_UNIT_PC);
- } else {
- return FALSE;
- }
- *val = v;
- return TRUE;
- }
-
- return FALSE;
-}
-
static gboolean sp_nv_read_opacity(gchar const *str, guint32 *color)
{
if (!str) {
@@ -645,28 +588,32 @@ CanvasXYGrid::readRepr()
{
gchar const *value;
if ( (value = repr->attribute("originx")) ) {
- sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[Geom::X], &gridunit);
- origin[Geom::X] = sp_units_get_pixels(origin[Geom::X], *(gridunit));
+ Inkscape::Util::Quantity q = unit_table.getQuantity(value);
+ gridunit = q.unit;
+ origin[Geom::X] = unit_table.getQuantity(value).value("px");
}
if ( (value = repr->attribute("originy")) ) {
- sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[Geom::Y], &gridunit);
- origin[Geom::Y] = sp_units_get_pixels(origin[Geom::Y], *(gridunit));
+ Inkscape::Util::Quantity q = unit_table.getQuantity(value);
+ gridunit = q.unit;
+ origin[Geom::Y] = unit_table.getQuantity(value).value("px");
}
if ( (value = repr->attribute("spacingx")) ) {
double oldVal = spacing[Geom::X];
- sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &spacing[Geom::X], &gridunit);
- validateScalar( oldVal, &spacing[Geom::X]);
- spacing[Geom::X] = sp_units_get_pixels(spacing[Geom::X], *(gridunit));
-
+ Inkscape::Util::Quantity q = unit_table.getQuantity(value);
+ gridunit = q.unit;
+ spacing[Geom::X] = q.quantity;
+ validateScalar(oldVal, &spacing[Geom::X]);
+ spacing[Geom::X] = Inkscape::Util::Quantity::convert(spacing[Geom::X], *gridunit, "px");
}
if ( (value = repr->attribute("spacingy")) ) {
double oldVal = spacing[Geom::Y];
- sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &spacing[Geom::Y], &gridunit);
- validateScalar( oldVal, &spacing[Geom::Y]);
- spacing[Geom::Y] = sp_units_get_pixels(spacing[Geom::Y], *(gridunit));
-
+ Inkscape::Util::Quantity q = unit_table.getQuantity(value);
+ gridunit = q.unit;
+ spacing[Geom::Y] = q.quantity;
+ validateScalar(oldVal, &spacing[Geom::Y]);
+ spacing[Geom::Y] = Inkscape::Util::Quantity::convert(spacing[Geom::Y], *gridunit, "px");
}
if ( (value = repr->attribute("color")) ) {
@@ -802,20 +749,20 @@ CanvasXYGrid::newSpecificWidget()
attach_all (*table, widget_array, sizeof(widget_array));
// set widget values
- _rumg->setUnit (gridunit);
+ _rumg->setUnit (gridunit->abbr);
gdouble val;
val = origin[Geom::X];
- val = sp_pixels_get_units (val, *(gridunit));
+ val = Inkscape::Util::Quantity::convert(val, "px", *gridunit);
_rsu_ox->setValue (val);
val = origin[Geom::Y];
- val = sp_pixels_get_units (val, *(gridunit));
+ val = Inkscape::Util::Quantity::convert(val, "px", *gridunit);
_rsu_oy->setValue (val);
val = spacing[Geom::X];
- double gridx = sp_pixels_get_units (val, *(gridunit));
+ double gridx = Inkscape::Util::Quantity::convert(val, "px", *gridunit);
_rsu_sx->setValue (gridx);
val = spacing[Geom::Y];
- double gridy = sp_pixels_get_units (val, *(gridunit));
+ double gridy = Inkscape::Util::Quantity::convert(val, "px", *gridunit);
_rsu_sy->setValue (gridy);
_rcp_gcol->setRgba32 (color);
@@ -851,20 +798,20 @@ CanvasXYGrid::updateWidgets()
_rcb_enabled.setActive(snapper->getEnabled());
}
- _rumg.setUnit (gridunit);
+ _rumg.setUnit (gridunit->abbr);
gdouble val;
val = origin[Geom::X];
- val = sp_pixels_get_units (val, *(gridunit));
+ val = Inkscape::Quantity::convert(val, "px", *gridunit);
_rsu_ox.setValue (val);
val = origin[Geom::Y];
- val = sp_pixels_get_units (val, *(gridunit));
+ val = Inkscape::Quantity::convert(val, "px", *gridunit);
_rsu_oy.setValue (val);
val = spacing[Geom::X];
- double gridx = sp_pixels_get_units (val, *(gridunit));
+ double gridx = Inkscape::Quantity::convert(val, "px", *gridunit);
_rsu_sx.setValue (gridx);
val = spacing[Geom::Y];
- double gridy = sp_pixels_get_units (val, *(gridunit));
+ double gridy = Inkscape::Quantity::convert(val, "px", *gridunit);
_rsu_sy.setValue (gridy);
_rcp_gcol.setRgba32 (color);
diff --git a/src/display/canvas-grid.h b/src/display/canvas-grid.h
index 09c261e0d..56ed86e94 100644
--- a/src/display/canvas-grid.h
+++ b/src/display/canvas-grid.h
@@ -28,6 +28,10 @@ namespace XML {
class Node;
}
+namespace Util {
+class Unit;
+}
+
enum GridType {
GRID_RECTANGULAR = 0,
GRID_AXONOMETRIC = 1
@@ -88,7 +92,7 @@ public:
guint32 empcolor; /**< Color for emphasis lines */
gint empspacing; /**< Spacing between emphasis lines */
- SPUnit const* gridunit;
+ Inkscape::Util::Unit const* gridunit;
Inkscape::XML::Node * repr;
SPDocument *doc;
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 1929bfed6..cd7e9575f 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 43df6f8f1..8fd736cc3 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