diff options
| author | su_v <suv-sf@users.sourceforge.net> | 2015-12-07 10:00:35 +0000 |
|---|---|---|
| committer | ~suv <suv-sf@users.sourceforge.net> | 2015-12-07 10:00:35 +0000 |
| commit | 457c41675d69c7463c3590ad9b8e43bcae20e611 (patch) | |
| tree | 43d1f8d4e350a3070944bbd7bb4ad265c1b6ed02 /src | |
| parent | merge from trunk (r14473) (diff) | |
| parent | Remove leftover assert. (diff) | |
| download | inkscape-457c41675d69c7463c3590ad9b8e43bcae20e611.tar.gz inkscape-457c41675d69c7463c3590ad9b8e43bcae20e611.zip | |
merge from trunk (r14506)
(bzr r14425.1.4)
Diffstat (limited to 'src')
89 files changed, 1427 insertions, 4649 deletions
diff --git a/src/2geom/curve.cpp b/src/2geom/curve.cpp index b45228514..24c8be9f8 100644 --- a/src/2geom/curve.cpp +++ b/src/2geom/curve.cpp @@ -108,7 +108,11 @@ std::vector<CurveIntersection> Curve::intersectSelf(Coord eps) const // Monotonic segments cannot have self-intersections. // Thus, we can split the curve at roots and intersect the portions. std::vector<Coord> splits; +#if __cplusplus <= 199711L std::auto_ptr<Curve> deriv(derivative()); +#else + std::unique_ptr<Curve> deriv(derivative()); +#endif splits = deriv->roots(0, X); if (splits.empty()) { return result; diff --git a/src/2geom/elliptical-arc.cpp b/src/2geom/elliptical-arc.cpp index 25a78b4ad..77d3d788d 100644 --- a/src/2geom/elliptical-arc.cpp +++ b/src/2geom/elliptical-arc.cpp @@ -354,7 +354,11 @@ EllipticalArc::pointAndDerivatives(Coord t, unsigned int n) const std::vector<Point> result; result.reserve(nn); double angle = angleAt(t); +#if __cplusplus <= 199711L std::auto_ptr<EllipticalArc> ea( static_cast<EllipticalArc*>(duplicate()) ); +#else + std::unique_ptr<EllipticalArc> ea( static_cast<EllipticalArc*>(duplicate()) ); +#endif ea->_ellipse.setCenter(0, 0); unsigned int m = std::min(nn, 4u); for ( unsigned int i = 0; i < m; ++i ) diff --git a/src/Makefile.am b/src/Makefile.am index 04e33c471..27d4fb844 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -259,9 +259,7 @@ check-local: # the XFAIL_TESTS build target should be removed. # See the following Launchpad bugs: # -# LP #1202271 <cxxtest: preferences-test.h fails> # LP #1208013 <cxxtest: curve-test.h fails> -# LP #1208002 <cxxtest: svg-length-test.h fails> # LP #1208005 <cxxtest: svg-path-geom-test.h fails> # LP #1207502 <cxxtest: svg-affine-test.h fails> diff --git a/src/attributes.cpp b/src/attributes.cpp index a237f8861..d373dbc68 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -41,6 +41,7 @@ static SPStyleProp const props[] = { {SP_ATTR_TRANSFORM_CENTER_Y, "inkscape:transform-center-y"}, {SP_ATTR_INKSCAPE_PATH_EFFECT, "inkscape:path-effect"}, {SP_ATTR_INKSCAPE_HIGHLIGHT_COLOR, "inkscape:highlight-color"}, + {SP_ATTR_INKSCAPE_SPRAY_ORIGIN, "inkscape:spray-origin"}, /* SPAnchor */ {SP_ATTR_XLINK_HREF, "xlink:href"}, {SP_ATTR_XLINK_TYPE, "xlink:type"}, diff --git a/src/attributes.h b/src/attributes.h index 42ec99d8f..7d22858b8 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -41,6 +41,7 @@ enum SPAttributeEnum { SP_ATTR_TRANSFORM_CENTER_Y, SP_ATTR_INKSCAPE_PATH_EFFECT, SP_ATTR_INKSCAPE_HIGHLIGHT_COLOR, + SP_ATTR_INKSCAPE_SPRAY_ORIGIN, /* SPAnchor */ SP_ATTR_XLINK_HREF, SP_ATTR_XLINK_TYPE, diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp index e66da90fb..67c0687b7 100644 --- a/src/desktop-style.cpp +++ b/src/desktop-style.cpp @@ -500,7 +500,7 @@ objects_query_fillstroke (const std::vector<SPItem*> &objects, SPStyle *style_re SPIPaint *paint_res = isfill? &style_res->fill : &style_res->stroke; bool paintImpossible = true; - paint_res->set = TRUE; + paint_res->set = true; SVGICCColor* iccColor = 0; @@ -1144,7 +1144,7 @@ objects_query_fontstyle (const std::vector<SPItem*> &objects, SPStyle *style_res different = true; // different styles } - set = TRUE; + set = true; style_res->font_weight.value = style_res->font_weight.computed = style->font_weight.computed; style_res->font_style.value = style_res->font_style.computed = style->font_style.computed; style_res->font_stretch.value = style_res->font_stretch.computed = style->font_stretch.computed; @@ -1256,6 +1256,56 @@ objects_query_fontvariants (const std::vector<SPItem*> &objects, SPStyle *style_ } +/** + * Write to style_res the average writing modes style of objects. + */ +int +objects_query_writing_modes (const std::vector<SPItem*> &objects, SPStyle *style_res) +{ + bool different = false; + bool set = false; + + int texts = 0; + + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; + + if (!isTextualItem(obj)) { + continue; + } + + SPStyle *style = obj->style; + if (!style) { + continue; + } + + texts ++; + + if (set && + ( ( style_res->writing_mode.computed != style->writing_mode.computed ) || + ( style_res->text_orientation.computed != style->text_orientation.computed ) ) ) { + different = true; // different styles + } + + set = true; + style_res->writing_mode.computed = style->writing_mode.computed; + style_res->text_orientation.computed = style->text_orientation.computed; + } + + if (texts == 0 || !set) + return QUERY_STYLE_NOTHING; + + if (texts > 1) { + if (different) { + return QUERY_STYLE_MULTIPLE_DIFFERENT; + } else { + return QUERY_STYLE_MULTIPLE_SAME; + } + } else { + return QUERY_STYLE_SINGLE; + } +} + int objects_query_fontfeaturesettings (const std::vector<SPItem*> &objects, SPStyle *style_res) { @@ -1293,7 +1343,7 @@ objects_query_fontfeaturesettings (const std::vector<SPItem*> &objects, SPStyle style_res->font_feature_settings.value = NULL; } - style_res->font_feature_settings.set = TRUE; + style_res->font_feature_settings.set = true; style_res->font_feature_settings.value = g_strdup(style->font_feature_settings.value); } @@ -1449,7 +1499,7 @@ objects_query_fontfamily (const std::vector<SPItem*> &objects, SPStyle *style_re style_res->font_family.value = NULL; } - style_res->font_family.set = TRUE; + style_res->font_family.set = true; style_res->font_family.value = g_strdup(style->font_family.value); } @@ -1508,7 +1558,7 @@ objects_query_fontspecification (const std::vector<SPItem*> &objects, SPStyle *s style_res->font_specification.value = NULL; } - style_res->font_specification.set = TRUE; + style_res->font_specification.set = true; style_res->font_specification.value = g_strdup(style->font_specification.value); } } @@ -1728,6 +1778,8 @@ sp_desktop_query_style_from_list (const std::vector<SPItem*> &list, SPStyle *sty return objects_query_fontfeaturesettings (list, style); } else if (property == QUERY_STYLE_PROPERTY_FONTNUMBERS) { return objects_query_fontnumbers (list, style); + } else if (property == QUERY_STYLE_PROPERTY_WRITINGMODES) { + return objects_query_writing_modes (list, style); } else if (property == QUERY_STYLE_PROPERTY_BASELINES) { return objects_query_baselines (list, style); diff --git a/src/desktop-style.h b/src/desktop-style.h index 95c434844..b9199b615 100644 --- a/src/desktop-style.h +++ b/src/desktop-style.h @@ -51,6 +51,7 @@ enum { // which property was queried (add when you need more) QUERY_STYLE_PROPERTY_FONTFEATURESETTINGS, // font feature settings (OpenType features) QUERY_STYLE_PROPERTY_FONTNUMBERS, // size, spacings QUERY_STYLE_PROPERTY_BASELINES, // baseline-shift + QUERY_STYLE_PROPERTY_WRITINGMODES, // writing-mode, text-orientation QUERY_STYLE_PROPERTY_MASTEROPACITY, // opacity QUERY_STYLE_PROPERTY_BLEND, // blend QUERY_STYLE_PROPERTY_BLUR // blur diff --git a/src/display/nr-style.cpp b/src/display/nr-style.cpp index 1740785e2..8b82a1dff 100644 --- a/src/display/nr-style.cpp +++ b/src/display/nr-style.cpp @@ -68,7 +68,6 @@ NRStyle::NRStyle() , tspan_width(0) , ascender(0) , descender(0) - , line_gap(0) , underline_thickness(0) , underline_position(0) , line_through_thickness(0) @@ -330,7 +329,6 @@ void NRStyle::set(SPStyle *style, SPStyle *context_style) 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; diff --git a/src/display/nr-style.h b/src/display/nr-style.h index 5f78795d3..6c652311a 100644 --- a/src/display/nr-style.h +++ b/src/display/nr-style.h @@ -115,7 +115,6 @@ struct NRStyle { float tspan_width; float ascender; float descender; - float line_gap; float underline_thickness; float underline_position; float line_through_thickness; diff --git a/src/document.cpp b/src/document.cpp index 0e49f23e2..23d99d78c 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -1337,20 +1337,25 @@ SPItem *SPDocument::getItemFromListAtPointBottom(unsigned int dkey, SPGroup *gro /** Turn the SVG DOM into a flat list of nodes that can be searched from top-down. The list can be persisted, which improves "find at multiple points" speed. +Returns true if upto is reached. */ -static void build_flat_item_list(std::deque<SPItem*> *nodes, unsigned int dkey, SPGroup *group, gboolean into_groups, bool take_insensitive = false, SPItem *upto = NULL) +static bool build_flat_item_list(std::deque<SPItem*> *nodes, unsigned int dkey, SPGroup *group, gboolean into_groups, bool take_insensitive = false, SPItem *upto = NULL) { + bool found_upto = false; for ( SPObject *o = group->firstChild() ; o ; o = o->getNext() ) { if (!SP_IS_ITEM(o)) { continue; } if (upto && SP_ITEM(o) == upto) { + found_upto = true; break; } if (SP_IS_GROUP(o) && (SP_GROUP(o)->effectiveLayerMode(dkey) == SPGroup::LAYER || into_groups)) { - build_flat_item_list(nodes, dkey, SP_GROUP(o), into_groups, take_insensitive, upto); + found_upto = build_flat_item_list(nodes, dkey, SP_GROUP(o), into_groups, take_insensitive, upto); + if (found_upto) + break; } else { SPItem *child = SP_ITEM(o); @@ -1359,6 +1364,7 @@ static void build_flat_item_list(std::deque<SPItem*> *nodes, unsigned int dkey, } } } + return found_upto; } /** diff --git a/src/extension/internal/gdkpixbuf-input.cpp b/src/extension/internal/gdkpixbuf-input.cpp index f99c9050d..c15e28854 100644 --- a/src/extension/internal/gdkpixbuf-input.cpp +++ b/src/extension/internal/gdkpixbuf-input.cpp @@ -86,8 +86,13 @@ GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri) ir = new ImageResolution(uri); } if (ir && ir->ok()) { - xscale = 960.0 / round(10.*ir->x() + .5); // round-off to 0.1 dpi - yscale = 960.0 / round(10.*ir->y() + .5); + xscale = 960.0 / round(10.*ir->x()); // round-off to 0.1 dpi + yscale = 960.0 / round(10.*ir->y()); + // prevent crash on image with too small dpi (bug 1479193) + if (ir->x() <= .05) + xscale = 960.0; + if (ir->y() <= .05) + yscale = 960.0; } else { xscale = 96.0 / defaultxdpi; yscale = 96.0 / defaultxdpi; diff --git a/src/libnrtype/FontInstance.cpp b/src/libnrtype/FontInstance.cpp index a5572c517..7a16fc0c3 100644 --- a/src/libnrtype/FontInstance.cpp +++ b/src/libnrtype/FontInstance.cpp @@ -21,6 +21,7 @@ #include FT_BBOX_H #include FT_TRUETYPE_TAGS_H #include FT_TRUETYPE_TABLES_H +#include FT_GLYPH_H #include <pango/pangoft2.h> #include <2geom/pathvector.h> #include <2geom/path-sink.h> @@ -183,6 +184,20 @@ font_instance::font_instance(void) : theFace(0) { //printf("font instance born\n"); + _ascent = _ascent_max = 0.8; + _descent = _descent_max = 0.2; + _xheight = 0.5; + + // Default baseline values, alphabetic is reference + _baselines[ SP_CSS_BASELINE_AUTO ] = 0.0; + _baselines[ SP_CSS_BASELINE_ALPHABETIC ] = 0.0; + _baselines[ SP_CSS_BASELINE_IDEOGRAPHIC ] = -_descent; + _baselines[ SP_CSS_BASELINE_HANGING ] = 0.8 * _ascent; + _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] = 0.8 * _xheight; + _baselines[ SP_CSS_BASELINE_CENTRAL ] = 0.5 - _descent; + _baselines[ SP_CSS_BASELINE_MIDDLE ] = 0.5 * _xheight; + _baselines[ SP_CSS_BASELINE_TEXT_BEFORE_EDGE ] = _ascent; + _baselines[ SP_CSS_BASELINE_TEXT_AFTER_EDGE ] = -_descent; } font_instance::~font_instance(void) @@ -259,6 +274,7 @@ void font_instance::InitTheFace() FT_Select_Charmap(theFace,ft_encoding_unicode) && FT_Select_Charmap(theFace,ft_encoding_symbol); } #endif + FindFontMetrics(); } } @@ -374,10 +390,10 @@ void font_instance::LoadGlyph(int glyph_id) GLYPHMETRICS metrics; DWORD bufferSize=GetGlyphOutline (parent->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity); double scale=1.0/parent->fontSize; - n_g.h_advance=metrics.gmCellIncX*scale; - n_g.v_advance=otm.otmTextMetrics.tmHeight*scale; - n_g.h_width=metrics.gmBlackBoxX*scale; - n_g.v_width=metrics.gmBlackBoxY*scale; + n_g.h_advance = metrics.gmCellIncX * scale; + n_g.v_advance = otm.otmTextMetrics.tmHeight * scale; + n_g.h_width = metrics.gmBlackBoxX * scale; + n_g.v_width = metrics.gmBlackBoxY * scale; if ( bufferSize == GDI_ERROR) { // shit happened } else if ( bufferSize == 0) { @@ -459,7 +475,13 @@ void font_instance::LoadGlyph(int glyph_id) n_g.v_advance=((double)theFace->glyph->metrics.vertAdvance)/((double)theFace->units_per_EM); n_g.v_width=((double)theFace->glyph->metrics.height)/((double)theFace->units_per_EM); } else { - n_g.v_width=n_g.v_advance=((double)theFace->height)/((double)theFace->units_per_EM); + // CSS3 Writing modes dictates that if vertical font metrics are missing we must + // synthisize them. No method is specified. The SVG 1.1 spec suggests using the em + // height (which is not theFace->height as that includes leading). The em height + // is ascender + descender (descender positive). Note: The "Requirements for + // Japanese Text Layout" W3C document says that Japanese kanji should be "set + // solid" which implies that vertical (and horizontal) advance should be 1em. + n_g.v_width=n_g.v_advance= 1.0; } if ( theFace->glyph->format == ft_glyph_format_outline ) { FT_Outline_Funcs ft2_outline_funcs = { @@ -501,7 +523,7 @@ void font_instance::LoadGlyph(int glyph_id) } } -bool font_instance::FontMetrics(double &ascent,double &descent,double &leading) +bool font_instance::FontMetrics(double &ascent,double &descent,double &xheight) { if ( pFont == NULL ) { return false; @@ -510,32 +532,17 @@ bool font_instance::FontMetrics(double &ascent,double &descent,double &leading) if ( theFace == NULL ) { return false; } -#ifdef USE_PANGO_WIN32 - OUTLINETEXTMETRIC otm; - if ( !GetOutlineTextMetrics(parent->hScreenDC,sizeof(otm),&otm) ) { - return false; - } - double scale=1.0/parent->fontSize; - ascent=fabs(otm.otmAscent*scale); - descent=fabs(otm.otmDescent*scale); - leading=fabs(otm.otmLineGap*scale); - //otmSubscriptSize, otmSubscriptOffset, otmSuperscriptSize, otmSuperscriptOffset, -#else - if ( theFace->units_per_EM == 0 ) { - return false; // bitmap font - } - ascent=fabs(((double)theFace->ascender)/((double)theFace->units_per_EM)); - descent=fabs(((double)theFace->descender)/((double)theFace->units_per_EM)); - leading=fabs(((double)theFace->height)/((double)theFace->units_per_EM)); - leading-=ascent+descent; -#endif + + ascent = _ascent; + descent = _descent; + xheight = _xheight; + return true; } -bool font_instance::FontDecoration( - double &underline_position, double &underline_thickness, - double &linethrough_position, double &linethrough_thickness -){ +bool font_instance::FontDecoration( double &underline_position, double &underline_thickness, + double &linethrough_position, double &linethrough_thickness) +{ if ( pFont == NULL ) { return false; } @@ -662,6 +669,179 @@ double font_instance::Advance(int glyph_id,bool vertical) return 0; } +// Internal function to find baselines +void font_instance::FindFontMetrics() { + + // CSS2 recommends using the OS/2 values sTypoAscender and sTypoDescender for the Typographic + // ascender and descender values: + // http://www.w3.org/TR/CSS2/visudet.html#sTypoAscender + // On Windows, the typographic ascender and descender are taken from the otmMacAscent and + // otmMacDescent values: + // http://microsoft.public.win32.programmer.gdi.narkive.com/LV6k4BDh/msdn-documentation-outlinetextmetrics-clarification + // The otmAscent and otmDescent values are the maxiumum ascent and maxiumum descent of all the + // glyphs in a font. + if ( theFace ) { + +#ifdef USE_PANGO_WIN32 + + if ( GetOutlineTextMetrics(parent->hScreenDC,sizeof(otm),&otm) ) { + double scale=1.0/parent->fontSize; + _ascent = fabs(otm.otmMacAscent * scale); + _descent = fabs(otm.otmMacDescent * scale); + _xheight = fabs(otm.otmXHeight * scale); + _ascent_max = fabs(otm.otmAscent * scale); + _descent_max = fabs(otm.otmDescent * scale); + + // In CSS em size is ascent + descent... which should be 1. If not, + // adjust so it is. + double em = _ascent + _descent; + if( em > 0 ) { + _ascent /= em; + _descent /= em; + } + + // May not be necessary but if OS/2 table missing or not version 2 or higher, + // xheight might be zero. + if( _xheight == 0.0 ) { + _xheight = 0.5; + } + + // Baselines defined relative to alphabetic. + _baselines[ SP_CSS_BASELINE_IDEOGRAPHIC ] = -_descent; // Recommendation + _baselines[ SP_CSS_BASELINE_HANGING ] = 0.8 * _ascent; // Guess + _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] = 0.8 * _xheight; // Guess + _baselines[ SP_CSS_BASELINE_CENTRAL ] = 0.5 - _descent; // Definition + _baselines[ SP_CSS_BASELINE_MIDDLE ] = 0.5 * _xheight; // Definition + _baselines[ SP_CSS_BASELINE_TEXT_BEFORE_EDGE ] = _ascent; // Definition + _baselines[ SP_CSS_BASELINE_TEXT_AFTER_EDGE ] = -_descent; // Definition + + + MAT2 identity = {{0,1},{0,0},{0,0},{0,1}}; + GLYPHMETRICS metrics; + int retval; + + // Better math baseline: + // Try center of minus sign + retval = GetGlyphOutline (parent->hScreenDC, 0x2212, GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity); + // If no minus sign, try hyphen + if( retval <= 0 ) + retval = GetGlyphOutline (parent->hScreenDC, '-', GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity); + + if( retval > 0 ) { + double math = (metrics.gmptGlyphOrigin.y + 0.5 * metrics.gmBlackBoxY) * scale; + _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] = math; + } + + // Find hanging baseline... assume it is at top of 'म'. + retval = GetGlyphOutline (parent->hScreenDC, 0x092E, GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity); + if( retval > 0 ) { + double hanging = metrics.gmptGlyphOrigin.y * scale; + _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] = hanging; + } + } + +#else + + if ( theFace->units_per_EM != 0 ) { // If zero then it's a bitmap font. + + TT_OS2* os2 = (TT_OS2*)FT_Get_Sfnt_Table( theFace, ft_sfnt_os2 ); + if( os2 ) { + _ascent = fabs(((double)os2->sTypoAscender) / ((double)theFace->units_per_EM)); + _descent = fabs(((double)os2->sTypoDescender)/ ((double)theFace->units_per_EM)); + } else { + _ascent = fabs(((double)theFace->ascender) / ((double)theFace->units_per_EM)); + _descent = fabs(((double)theFace->descender) / ((double)theFace->units_per_EM)); + } + _ascent_max = fabs(((double)theFace->ascender) / ((double)theFace->units_per_EM)); + _descent_max = fabs(((double)theFace->descender) / ((double)theFace->units_per_EM)); + + // In CSS em size is ascent + descent... which should be 1. If not, + // adjust so it is. + double em = _ascent + _descent; + if( em > 0 ) { + _ascent /= em; + _descent /= em; + } + + // x-height + if( os2 && os2->version >= 0x0002 && os2->version != 0xffffu ) { + // Only os/2 version 2 and above have sxHeight, 0xffff marks "old Mac fonts" without table + _xheight = fabs(((double)os2->sxHeight) / ((double)theFace->units_per_EM)); + } else { + // Measure 'x' height in font. Recommended option by XSL standard if no sxHeight. + FT_UInt index = FT_Get_Char_Index( theFace, 'x' ); + if( index != 0 ) { + FT_Load_Glyph( theFace, index, FT_LOAD_NO_SCALE ); + _xheight = (fabs)(((double)theFace->glyph->metrics.height/(double)theFace->units_per_EM)); + } else { + // No 'x' in font! + _xheight = 0.5; + } + } + + // Baselines defined relative to alphabetic. + _baselines[ SP_CSS_BASELINE_IDEOGRAPHIC ] = -_descent; // Recommendation + _baselines[ SP_CSS_BASELINE_HANGING ] = 0.8 * _ascent; // Guess + _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] = 0.8 * _xheight; // Guess + _baselines[ SP_CSS_BASELINE_CENTRAL ] = 0.5 - _descent; // Definition + _baselines[ SP_CSS_BASELINE_MIDDLE ] = 0.5 * _xheight; // Definition + _baselines[ SP_CSS_BASELINE_TEXT_BEFORE_EDGE ] = _ascent; // Definition + _baselines[ SP_CSS_BASELINE_TEXT_AFTER_EDGE ] = -_descent; // Definition + + // Better math baseline: + // Try center of minus sign + FT_UInt index = FT_Get_Char_Index( theFace, 0x2212 ); //'−' + // If no minus sign, try hyphen + if( index == 0 ) + index = FT_Get_Char_Index( theFace, '-' ); + + if( index != 0 ) { + FT_Load_Glyph( theFace, index, FT_LOAD_NO_SCALE ); + FT_Glyph aglyph; + FT_Get_Glyph( theFace->glyph, &aglyph ); + FT_BBox acbox; + FT_Glyph_Get_CBox( aglyph, FT_GLYPH_BBOX_UNSCALED, &acbox ); + double math = (acbox.yMin + acbox.yMax)/2.0/(double)theFace->units_per_EM; + _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] = math; + // std::cout << "Math baseline: - bbox: y_min: " << acbox.yMin + // << " y_max: " << acbox.yMax + // << " math: " << math << std::endl; + } + + // Find hanging baseline... assume it is at top of 'म'. + index = FT_Get_Char_Index( theFace, 0x092E ); // 'म' + if( index != 0 ) { + FT_Load_Glyph( theFace, index, FT_LOAD_NO_SCALE ); + FT_Glyph aglyph; + FT_Get_Glyph( theFace->glyph, &aglyph ); + FT_BBox acbox; + FT_Glyph_Get_CBox( aglyph, FT_GLYPH_BBOX_UNSCALED, &acbox ); + double hanging = (double)acbox.yMax/(double)theFace->units_per_EM; + _baselines[ SP_CSS_BASELINE_HANGING ] = hanging; + // std::cout << "Hanging baseline: प: " << hanging << std::endl; + } + } +#endif + // const gchar *family = pango_font_description_get_family(descr); + // std::cout << "Font: " << (family?family:"null") << std::endl; + // std::cout << " ascent: " << _ascent << std::endl; + // std::cout << " descent: " << _descent << std::endl; + // std::cout << " x-height: " << _xheight << std::endl; + // std::cout << " max ascent: " << _ascent_max << std::endl; + // std::cout << " max descent: " << _descent_max << std::endl; + // std::cout << " Baselines:" << std::endl; + // std::cout << " alphabetic: " << _baselines[ SP_CSS_BASELINE_ALPHABETIC ] << std::endl; + // std::cout << " ideographic: " << _baselines[ SP_CSS_BASELINE_IDEOGRAPHIC ] << std::endl; + // std::cout << " hanging: " << _baselines[ SP_CSS_BASELINE_HANGING ] << std::endl; + // std::cout << " math: " << _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] << std::endl; + // std::cout << " central: " << _baselines[ SP_CSS_BASELINE_CENTRAL ] << std::endl; + // std::cout << " middle: " << _baselines[ SP_CSS_BASELINE_MIDDLE ] << std::endl; + // std::cout << " text_before: " << _baselines[ SP_CSS_BASELINE_TEXT_BEFORE_EDGE ] << std::endl; + // std::cout << " text_after: " << _baselines[ SP_CSS_BASELINE_TEXT_AFTER_EDGE ] << std::endl; + } +} + + /* Local Variables: mode:c++ diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index c4b0a5bee..1e8fb0fb1 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -23,33 +23,6 @@ namespace Text { #define TRACE(_args) IFTRACE(g_print _args) -// ******* enum conversion tables - -// These enums are probably from the SVG 1.0 era where one could interpret 'writing-mode' as setting direction. -// SVG 1.1 makes it clear that 'direction' should be used. 'direction' has only two values 'ltr' and 'rtl'. -// The first two values for the 'writing-mode' enum just happen to match the first two value of 'direction' so the -// existing code worked when 'writing-mode' was changed to 'direction'. -// static Layout::EnumConversionItem const enum_convert_spstyle_direction_to_pango_direction[] = { -// {SP_CSS_WRITING_MODE_LR_TB, PANGO_DIRECTION_LTR}, -// {SP_CSS_WRITING_MODE_RL_TB, PANGO_DIRECTION_RTL}, -// {SP_CSS_WRITING_MODE_TB_LR, PANGO_DIRECTION_LTR}}; // this is correct - -// static Layout::EnumConversionItem const enum_convert_spstyle_direction_to_my_direction[] = { -// {SP_CSS_WRITING_MODE_LR_TB, Layout::LEFT_TO_RIGHT}, -// {SP_CSS_WRITING_MODE_RL_TB, Layout::RIGHT_TO_LEFT}, -// {SP_CSS_WRITING_MODE_TB_LR, Layout::LEFT_TO_RIGHT}}; // this is correct - -// Proper 'direction' enums -static Layout::EnumConversionItem const enum_convert_spstyle_direction_to_pango_direction[] = { - {SP_CSS_DIRECTION_LTR, PANGO_DIRECTION_LTR}, - {SP_CSS_DIRECTION_RTL, PANGO_DIRECTION_RTL}}; - -static Layout::EnumConversionItem const enum_convert_spstyle_direction_to_my_direction[] = { - {SP_CSS_DIRECTION_LTR, Layout::LEFT_TO_RIGHT}, - {SP_CSS_DIRECTION_RTL, Layout::RIGHT_TO_LEFT}}; - - - /** \brief private to Layout. Does the real work of text flowing. This class does a standard greedy paragraph wrapping algorithm. @@ -140,9 +113,10 @@ class Layout::Calculator unsigned input_index; /// index into Layout::_input_stream Glib::ustring::const_iterator input_stream_first_character; double font_size; - LineHeight line_height; /// This is not the CSS line-height attribute! + FontMetrics line_height; /// This is not the CSS line-height attribute! double line_height_multiplier; /// calculated from the font-height css property double baseline_shift; /// calculated from the baseline-shift css property + SPCSSTextOrientation text_orientation; unsigned text_bytes; unsigned char_index_in_para; /// the index of the first character in this span in the paragraph, for looking up char_attributes SVGLength x, y, dx, dy, rotate; // these are reoriented copies of the <tspan> attributes. We change span when we encounter one. @@ -220,9 +194,8 @@ class Layout::Calculator void _buildPangoItemizationForPara(ParagraphInfo *para) const; - static void _computeFontLineHeight(font_instance *font, double font_size, - SPStyle const *style, LineHeight *line_height, - double *line_height_multiplier); + // Returns line_height_multiplier + static double _computeFontLineHeight( SPStyle const *style ); unsigned _buildSpansForPara(ParagraphInfo *para) const; @@ -270,7 +243,7 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ bool _goToNextWrapShape(); bool _findChunksForLine(ParagraphInfo const ¶, UnbrokenSpanPosition *start_span_pos, - std::vector<ChunkInfo> *chunk_info, LineHeight *line_height); + std::vector<ChunkInfo> *chunk_info, FontMetrics *line_box_height); static inline PangoLogAttr const &_charAttributes(ParagraphInfo const ¶, UnbrokenSpanPosition const &span_pos) @@ -282,7 +255,7 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ UnbrokenSpanPosition const &start_span_pos, ScanlineMaker::ScanRun const &scan_run, std::vector<ChunkInfo> *chunk_info, - LineHeight *line_height) const; + FontMetrics *line_height) const; /** computes the width of a single UnbrokenSpan (pointed to by span->start.iter_span) and outputs its vital statistics into the other fields of \a span. @@ -297,11 +270,15 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ or will be unaltered if there is none. */ bool _measureUnbrokenSpan(ParagraphInfo const ¶, BrokenSpan *span, BrokenSpan *last_break_span, BrokenSpan *last_emergency_break_span, double maximum_width) const { + TRACE((" start _measureUnbrokenSpan %g\n", maximum_width)); span->setZero(); if (span->start.iter_span->dx._set && span->start.char_byte == 0){ - if(para.direction == RIGHT_TO_LEFT){ span->width -= span->start.iter_span->dx.computed; } - else { span->width += span->start.iter_span->dx.computed; } + if(para.direction == RIGHT_TO_LEFT){ + span->width -= span->start.iter_span->dx.computed; + } else { + span->width += span->start.iter_span->dx.computed; + } } if (span->start.iter_span->pango_item_index == -1) { @@ -320,7 +297,7 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ if (control_code->code == ARBITRARY_GAP) { if (span->width + control_code->width > maximum_width) return false; - TRACE(("fitted control code, width = %f\n", control_code->width)); + TRACE((" fitted control code, width = %f\n", control_code->width)); span->width += control_code->width; span->end.increment(); } @@ -359,12 +336,14 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ PangoLogAttr const &char_attributes = _charAttributes(para, span->end); if (char_attributes.is_mandatory_break && span->end != span->start) { + TRACE((" is_mandatory_break ************\n")); *last_emergency_break_span = *last_break_span = *span; - TRACE(("span %ld end of para; width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count)); + TRACE((" span %ld end of para; width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count)); return false; } if (char_attributes.is_line_break) { + TRACE((" is_line_break ************\n")); // a suitable position to break at, record where we are *last_emergency_break_span = *last_break_span = *span; if (soft_hyphen_in_word) { @@ -382,10 +361,22 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ double char_width = 0.0; while (span->end_glyph_index < (unsigned)span->end.iter_span->glyph_string->num_glyphs && span->end.iter_span->glyph_string->log_clusters[span->end_glyph_index] <= (int)span->end.char_byte) { - if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) - char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, true); - else + if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) { + // Vertical text + + if( text_source->style->text_orientation.computed == SP_CSS_TEXT_ORIENTATION_SIDEWAYS || + (text_source->style->text_orientation.computed == SP_CSS_TEXT_ORIENTATION_MIXED && + para.pango_items[span->end.iter_span->pango_item_index].item->analysis.gravity == 0) ) { + // Sideways orientation + char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, false); + } else { + // Upright orientation + char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, true); + } + } else { + // Horizontal text char_width += font_size_multiplier * span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].geometry.width; + } span->end_glyph_index++; } if (char_attributes.is_cursor_position) @@ -417,13 +408,14 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ span->word_spacing = text_source->style->word_spacing.computed; if (test_width > maximum_width && !char_attributes.is_white) { // whitespaces don't matter, we can put as many as we want at eol - TRACE(("span %ld exceeded scanrun; width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count)); + TRACE((" span %ld exceeded scanrun; width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count)); return false; } } while (span->end.char_byte != 0); // while we haven't wrapped to the next span - TRACE(("fitted span %ld width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count)); + TRACE((" fitted span %ld width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count)); + TRACE((" end _measureUnbrokenSpan %g\n", maximum_width)); return true; } @@ -474,19 +466,33 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ are ready to output the final result to #_flow. This method takes its input parameters and does that. */ - void _outputLine(ParagraphInfo const ¶, LineHeight const &line_height, std::vector<ChunkInfo> const &chunk_info) + void _outputLine(ParagraphInfo const ¶, FontMetrics const &line_height, std::vector<ChunkInfo> const &chunk_info) { - TRACE(("Start _outputLine\n")); + TRACE((" Start _outputLine: ascent %f, descent %f, top of box %f\n", line_height.ascent, line_height.descent, _scanline_maker->yCoordinate() )); if (chunk_info.empty()) { - TRACE(("line too short to fit anything on it, go to next\n")); + TRACE((" line too short to fit anything on it, go to next\n")); return; } // we've finished fiddling about with ascents and descents: create the output - TRACE(("found line fit; creating output\n")); + TRACE((" found line fit; creating output\n")); Layout::Line new_line; new_line.in_paragraph = _flow._paragraphs.size() - 1; - new_line.baseline_y = _scanline_maker->yCoordinate() + line_height.ascent; + new_line.baseline_y = _scanline_maker->yCoordinate(); + + // The y coordinate is at the beginning edge of the line box (top for horizontal text, left + // edge for vertical lr text, right edge for vertical rl text. We align, by default to the + // alphabetic baseline for horizontal text and the central baseline for vertical text. + if( _block_progression == RIGHT_TO_LEFT ) { + // Vertical text, use em box center as baseline + new_line.baseline_y -= 0.5 * line_height.emSize(); + } else if ( _block_progression == LEFT_TO_RIGHT ) { + // Vertical text, use em box center as baseline + new_line.baseline_y += 0.5 * line_height.emSize(); + } else { + new_line.baseline_y += line_height.getTypoAscent(); + } + new_line.in_shape = _current_shape_index; _flow._lines.push_back(new_line); @@ -496,7 +502,7 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ // add the chunk to the list Layout::Chunk new_chunk; new_chunk.in_line = _flow._lines.size() - 1; - TRACE((" New chunk: in_line: %d\n", new_chunk.in_line)); + TRACE((" New chunk: in_line: %d\n", new_chunk.in_line)); new_chunk.left_x = _getChunkLeftWithAlignment(para, it_chunk, &add_to_each_whitespace); // we may also have y move orders to deal with here (dx, dy and rotate are done per span) @@ -527,17 +533,27 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ // If <tspan> "y" attribute is set, use it (initial "y" attributes in // <tspans> other than the first have already been stripped for <tspans> // marked with role="line", see sp-text.cpp: SPText::_buildLayoutInput). + // NOTE: for vertical text, "y" is the user-space "x" value. if( it_chunk->broken_spans.front().start.iter_span->y._set ) { // Use set "y" attribute new_line.baseline_y = it_chunk->broken_spans.front().start.iter_span->y.computed; - // Save baseline _flow._lines.back().baseline_y = new_line.baseline_y; - // Save new <tspan> y coordinate - _scanline_maker->setNewYCoordinate(new_line.baseline_y - line_height.ascent); - + double top_of_line_box = new_line.baseline_y; + if( _block_progression == RIGHT_TO_LEFT ) { + // Vertical text, use em box center as baseline + top_of_line_box += 0.5 * line_height.emSize(); + } else if (_block_progression == LEFT_TO_RIGHT ) { + // Vertical text, use em box center as baseline + top_of_line_box -= 0.5 * line_height.emSize(); + } else { + top_of_line_box -= line_height.getTypoAscent(); + } + TRACE((" y attribute set, next line top_of_line_box: %f\n", top_of_line_box )); + // Set the initial y coordinate of the next line (see above). + _scanline_maker->setNewYCoordinate(top_of_line_box); } // Reset relative y_offset ("dy" attribute is relative but should be reset at @@ -556,21 +572,21 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ } _flow._chunks.push_back(new_chunk); - double x; + double current_x; double direction_sign; Direction previous_direction = para.direction; double counter_directional_width_remaining = 0.0; float glyph_rotate = 0.0; if (para.direction == LEFT_TO_RIGHT) { direction_sign = +1.0; - x = 0.0; + current_x = 0.0; } else { direction_sign = -1.0; if (para.alignment == FULL && !_flow._input_wrap_shapes.empty()){ - x = it_chunk->scanrun_width; + current_x = it_chunk->scanrun_width; } else { - x = it_chunk->text_width; + current_x = it_chunk->text_width; } } @@ -583,7 +599,7 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ if (it_span->start.char_byte == 0) { // Start of an unbroken span, we might have dx, dy or rotate still to process // (x and y are done per chunk) - if (unbroken_span.dx._set) x += unbroken_span.dx.computed; + if (unbroken_span.dx._set) current_x += unbroken_span.dx.computed; if (unbroken_span.dy._set) _y_offset += unbroken_span.dy.computed; if (unbroken_span.rotate._set) glyph_rotate = unbroken_span.rotate.computed * (M_PI/180); } @@ -601,6 +617,7 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ new_span.in_input_stream_item = unbroken_span.input_index; new_span.baseline_shift = 0.0; new_span.block_progression = _block_progression; + new_span.text_orientation = unbroken_span.text_orientation; if ((_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) && (new_span.font = para.pango_items[unbroken_span.pango_item_index].font)) { new_span.font->Ref(); @@ -609,12 +626,12 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ new_span.input_stream_first_character = Glib::ustring::const_iterator(unbroken_span.input_stream_first_character.base() + it_span->start.char_byte); } else { // a control code new_span.font = NULL; - new_span.font_size = new_span.line_height.ascent + new_span.line_height.descent; + new_span.font_size = new_span.line_height.emSize(); new_span.direction = para.direction; } if (new_span.direction == para.direction) { - x -= counter_directional_width_remaining; + current_x -= counter_directional_width_remaining; counter_directional_width_remaining = 0.0; } else if (new_span.direction != previous_direction) { // measure width of spans we need to switch round @@ -630,10 +647,10 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ } counter_directional_width_remaining += direction_sign * (it_following_span->width + it_following_span->whitespace_count * add_to_each_whitespace); } - x += counter_directional_width_remaining; + current_x += counter_directional_width_remaining; counter_directional_width_remaining = 0.0; // we want to go increasingly negative } - new_span.x_start = x; + new_span.x_start = current_x; if (_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) { // the span is set up, push the glyphs and chars @@ -673,10 +690,12 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ } // create the Layout::Glyph + PangoGlyphInfo *unbroken_span_glyph_info = &unbroken_span.glyph_string->glyphs[glyph_index]; Layout::Glyph new_glyph; - new_glyph.glyph = unbroken_span.glyph_string->glyphs[glyph_index].glyph; + new_glyph.glyph = unbroken_span_glyph_info->glyph; new_glyph.in_character = _flow._characters.size(); new_glyph.rotation = glyph_rotate; + new_glyph.orientation = ORIENTATION_UPRIGHT; // Only effects vertical text // We may have scaled font size to fit textLength; now, if // @lengthAdjust=spacingAndGlyphs, this scaling must be only horizontal, @@ -686,30 +705,68 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ else new_glyph.vertical_scale = 1.0; - /* put something like this back in when we do glyph-rotation-horizontal/vertical - if (new_span.block_progression == LEFT_TO_RIGHT || new_span.block_progression == RIGHT_TO_LEFT) { - new_glyph.x += new_span.line_height.ascent; - new_glyph.y -= unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * font_size_multiplier * 0.5; - new_glyph.width = new_span.line_height.ascent + new_span.line_height.descent; - } else */ + // Position glyph -------------------- + new_glyph.x = current_x + unbroken_span_glyph_info->geometry.x_offset * font_size_multiplier; + new_glyph.y =_y_offset; + + // y-coordinate is flipped between vertical and horizontal text... delta_y is common offset but applied with opposite sign + double delta_y = unbroken_span_glyph_info->geometry.y_offset * font_size_multiplier + unbroken_span.baseline_shift; + SPCSSBaseline dominant_baseline = _flow._blockBaseline(); if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) { - new_glyph.x = x + unbroken_span.glyph_string->glyphs[glyph_index].geometry.x_offset * font_size_multiplier + new_span.line_height.ascent; - new_glyph.y = _y_offset - - unbroken_span.baseline_shift + - (unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset - - unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * 0.5) * font_size_multiplier; - new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[glyph_index].glyph, true); + // Vertical text + + // Default dominant baseline is determined by overall block (i.e. <text>) 'text-orientation' value. + if( _flow._blockTextOrientation() != SP_CSS_TEXT_ORIENTATION_SIDEWAYS ) { + if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_CENTRAL; + } else { + if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_ALPHABETIC; + } + + new_glyph.y += delta_y; + + // TODO: Should also check 'glyph_orientation_vertical' if 'text-orientation' is unset... + if( new_span.text_orientation == SP_CSS_TEXT_ORIENTATION_SIDEWAYS || + (new_span.text_orientation == SP_CSS_TEXT_ORIENTATION_MIXED && + para.pango_items[unbroken_span.pango_item_index].item->analysis.gravity == 0) ) { + + // Sideways orientation (Latin characters, CJK punctuation), 90deg rotation done at output stage. zzzzzzz + new_glyph.orientation = ORIENTATION_SIDEWAYS; + + new_glyph.y -= new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ]; + new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, false); + + } else { + // Upright orientation + + new_glyph.x += new_span.line_height.ascent; + + // Glyph reference point is center (shift: left edge to center glyph) + new_glyph.y -= unbroken_span_glyph_info->geometry.width * 0.5 * font_size_multiplier; + new_glyph.y -= new_span.font_size * (para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ] - + para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ SP_CSS_BASELINE_CENTRAL ] ); + + new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, true); + if( new_glyph.width == 0 ) { + new_glyph.width = unbroken_span_glyph_info->geometry.width * font_size_multiplier; + } + + } } else { - new_glyph.x = x + unbroken_span.glyph_string->glyphs[glyph_index].geometry.x_offset * font_size_multiplier; - new_glyph.y = _y_offset - - unbroken_span.baseline_shift + - unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset * font_size_multiplier; - new_glyph.width = unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * font_size_multiplier; + // Horizontal text + + if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_ALPHABETIC; + + new_glyph.y -= delta_y; + new_glyph.y += new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ]; + + new_glyph.width = unbroken_span_glyph_info->geometry.width * font_size_multiplier; if ((new_glyph.width == 0) && (para.pango_items[unbroken_span.pango_item_index].font)) - new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[glyph_index].glyph, false); + new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, false); // for some reason pango returns zero width for invalid glyph characters (those empty boxes), so go to freetype for the info } + + if (new_span.direction == RIGHT_TO_LEFT) { // pango wanted to give us glyphs in visual order but we refused, so we need to work // out where the cluster start is ourselves @@ -718,13 +775,15 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ if (unbroken_span.glyph_string->glyphs[rtl_index].attr.is_cluster_start && rtl_index != glyph_index) break; if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) + // Vertical text cluster_width += new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[rtl_index].glyph, true); else + // Horizontal text cluster_width += font_size_multiplier * unbroken_span.glyph_string->glyphs[rtl_index].geometry.width; } new_glyph.x -= cluster_width; } - _flow._glyphs.push_back(new_glyph); + _flow._glyphs.push_back(new_glyph); // create the Layout::Character(s) double advance_width = new_glyph.width; @@ -781,15 +840,15 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ advance_width *= direction_sign; if (new_span.direction != para.direction) { counter_directional_width_remaining -= advance_width; - x -= advance_width; + current_x -= advance_width; x_in_span_last -= advance_width; } else { - x += advance_width; + current_x += advance_width; x_in_span_last += advance_width; } } } else if (_flow._input_stream[unbroken_span.input_index]->Type() == CONTROL_CODE) { - x += static_cast<InputStreamControlCode const *>(_flow._input_stream[unbroken_span.input_index])->width; + current_x += static_cast<InputStreamControlCode const *>(_flow._input_stream[unbroken_span.input_index])->width; } new_span.x_end = new_span.x_start + x_in_span_last; @@ -798,7 +857,7 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ } // end adding spans to the list, on to the next chunk... } - TRACE(("output done\n")); + TRACE((" End _outputLine\n")); } /* *********************************************************************************************************/ @@ -978,6 +1037,7 @@ void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) con #if PANGO_VERSION_CHECK(1,37,1) TRACE((" ... compiled for font features\n")); #endif + Glib::ustring para_text; PangoAttrList *attributes_list; unsigned input_index; @@ -999,10 +1059,10 @@ void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) con } else if (_flow._input_stream[input_index]->Type() == TEXT_SOURCE) { Layout::InputStreamTextSource *text_source = static_cast<Layout::InputStreamTextSource *>(_flow._input_stream[input_index]); - // create the font_instance - font_instance *font = text_source->styleGetFontInstance(); - if (font == NULL) - continue; // bad news: we'll have to ignore all this text because we know of no font to render it + // create the font_instance + font_instance *font = text_source->styleGetFontInstance(); + if (font == NULL) + continue; // bad news: we'll have to ignore all this text because we know of no font to render it PangoAttribute *attribute_font_description = pango_attr_font_desc_new(font->descr); attribute_font_description->start_index = para_text.bytes(); @@ -1028,25 +1088,22 @@ void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) con TRACE(("whole para: \"%s\"\n", para_text.data())); TRACE(("%d input sources used\n", input_index - para->first_input_index)); - // do the pango_itemize() GList *pango_items_glist = NULL; + para->direction = LEFT_TO_RIGHT; // CSS default if (_flow._input_stream[para->first_input_index]->Type() == TEXT_SOURCE) { Layout::InputStreamTextSource const *text_source = static_cast<Layout::InputStreamTextSource *>(_flow._input_stream[para->first_input_index]); - if (text_source->style->direction.set) { - PangoDirection pango_direction = (PangoDirection)_enum_converter(text_source->style->direction.computed, enum_convert_spstyle_direction_to_pango_direction, sizeof(enum_convert_spstyle_direction_to_pango_direction)/sizeof(enum_convert_spstyle_direction_to_pango_direction[0])); - pango_items_glist = pango_itemize_with_base_dir(_pango_context, pango_direction, para_text.data(), 0, para_text.bytes(), attributes_list, NULL); - para->direction = (Layout::Direction)_enum_converter(text_source->style->direction.computed, enum_convert_spstyle_direction_to_my_direction, sizeof(enum_convert_spstyle_direction_to_my_direction)/sizeof(enum_convert_spstyle_direction_to_my_direction[0])); - } + + para->direction = (text_source->style->direction.computed == SP_CSS_DIRECTION_LTR) ? LEFT_TO_RIGHT : RIGHT_TO_LEFT; + PangoDirection pango_direction = (text_source->style->direction.computed == SP_CSS_DIRECTION_LTR) ? PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL; + pango_items_glist = pango_itemize_with_base_dir(_pango_context, pango_direction, para_text.data(), 0, para_text.bytes(), attributes_list, NULL); } - if (pango_items_glist == NULL) { // no direction specified, guess it - pango_items_glist = pango_itemize(_pango_context, para_text.data(), 0, para_text.bytes(), attributes_list, NULL); - // I think according to the css spec this is wrong and we're never allowed to guess the directionality - // of a paragraph. Need to talk to an rtl speaker. - if (pango_items_glist == NULL || pango_items_glist->data == NULL) para->direction = LEFT_TO_RIGHT; - else para->direction = (((PangoItem*)pango_items_glist->data)->analysis.level & 1) ? RIGHT_TO_LEFT : LEFT_TO_RIGHT; + if( pango_items_glist == NULL ) { + // Type wasn't TEXT_SOURCE or direction was not set. + pango_items_glist = pango_itemize(_pango_context, para_text.data(), 0, para_text.bytes(), attributes_list, NULL); } + pango_attr_list_unref(attributes_list); // convert the GList to our vector<> and make the font_instance for each PangoItem at the same time @@ -1070,44 +1127,32 @@ void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) con } /** - * Gets the ascent, descent and leading for a font and the alteration that has to be performed - * according to the value specified by the line-height css property. The result of multiplying - * \a line_height by \a line_height_multiplier is the inline box height as specified in css2 - * section 10.8. + * Finds the value of line_height_multiplier given the 'line-height' property. The result of + * multiplying \a l by \a line_height_multiplier is the inline box height as specified in css2 + * section 10.8. http://www.w3.org/TR/CSS2/visudet.html#line-height + * + * The 'computed' value of 'line-height' does not have a consistent meaning. We need to find the + * 'used' value and divide that by the font size. */ -void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font_size, - SPStyle const *style, LineHeight *line_height, - double *line_height_multiplier) +double Layout::Calculator::_computeFontLineHeight( SPStyle const *style ) { - if (font == NULL) { - line_height->setZero(); - *line_height_multiplier = 1.0; - } - else { - font->FontMetrics(line_height->ascent, line_height->descent, line_height->leading); - } - *line_height *= font_size; - // yet another borked SPStyle member that we're going to have to fix ourselves + // We shouldn't need to climb the element tree... for ( ; ; ) { if (style->line_height.set && !style->line_height.inherit) { if (style->line_height.normal) break; switch (style->line_height.unit) { case SP_CSS_UNIT_NONE: - *line_height_multiplier = style->line_height.computed * font_size / line_height->total(); - return; + return style->line_height.computed; case SP_CSS_UNIT_EX: - *line_height_multiplier = style->line_height.value * 0.5 * font_size / line_height->total(); + return style->line_height.value * 0.5; // 0.5 is an approximation of the x-height. Fixme. - return; case SP_CSS_UNIT_EM: case SP_CSS_UNIT_PERCENT: - *line_height_multiplier = style->line_height.value * font_size / line_height->total(); - return; + return style->line_height.value; default: // absolute values - *line_height_multiplier = style->line_height.computed / line_height->total(); - return; + return style->line_height.computed / style->font_size.computed; } break; } @@ -1115,7 +1160,7 @@ void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font style = style->object->parent->style; if (style == NULL) break; } - *line_height_multiplier = LINE_HEIGHT_NORMAL * font_size / line_height->total(); + return (LINE_HEIGHT_NORMAL); } bool compareGlyphWidth(const PangoGlyphInfo &a, const PangoGlyphInfo &b) @@ -1155,7 +1200,6 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const new_span.input_index = input_index; new_span.line_height.ascent = control_code->ascent * _flow.getTextLengthMultiplierDue(); new_span.line_height.descent = control_code->descent * _flow.getTextLengthMultiplierDue(); - new_span.line_height.leading = 0.0; new_span.text_bytes = 0; new_span.char_index_in_para = char_index_in_para; para->unbroken_spans.push_back(new_span); @@ -1194,11 +1238,13 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const new_span.dy._set = false; new_span.rotate._set = false; if (_block_progression == TOP_TO_BOTTOM || _block_progression == BOTTOM_TO_TOP) { + // Horizontal text if (text_source->x.size() > char_index_in_source) new_span.x = text_source->x[char_index_in_source]; if (text_source->y.size() > char_index_in_source) new_span.y = text_source->y[char_index_in_source]; if (text_source->dx.size() > char_index_in_source) new_span.dx = text_source->dx[char_index_in_source].computed * _flow.getTextLengthMultiplierDue(); if (text_source->dy.size() > char_index_in_source) new_span.dy = text_source->dy[char_index_in_source].computed * _flow.getTextLengthMultiplierDue(); } else { + // Vertical text if (text_source->x.size() > char_index_in_source) new_span.y = text_source->x[char_index_in_source]; if (text_source->y.size() > char_index_in_source) new_span.x = text_source->y[char_index_in_source]; if (text_source->dx.size() > char_index_in_source) new_span.dy = text_source->dx[char_index_in_source].computed * _flow.getTextLengthMultiplierDue(); @@ -1231,7 +1277,7 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const } // now we know the length, do some final calculations and add the UnbrokenSpan to the list - new_span.font_size = text_source->styleComputeFontSize() * _flow.getTextLengthMultiplierDue(); + new_span.font_size = text_source->style->font_size.computed * _flow.getTextLengthMultiplierDue(); if (new_span.text_bytes) { new_span.glyph_string = pango_glyph_string_new(); /* Some assertions intended to help diagnose bug #1277746. */ @@ -1240,6 +1286,7 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const g_assert( span_start_byte_in_source + new_span.text_bytes <= text_source->text->bytes() ); g_assert( memchr(text_source->text->data() + span_start_byte_in_source, '\0', static_cast<size_t>(new_span.text_bytes)) == NULL ); + /* Notes as of 4/29/13. Pango_shape is not generating English language ligatures, but it is generating them for Hebrew (and probably other similar languages). In the case observed 3 unicode characters (a base and 2 Mark, nonspacings) are merged into two glyphs (the base + first Mn, the 2nd Mn). All of these map @@ -1247,6 +1294,8 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const characters and glyphs. A big chunk of the conditional code which immediately follows this call is there to clean up the resulting mess. */ + + // Convert characters to glyphs pango_shape(text_source->text->data() + span_start_byte_in_source, new_span.text_bytes, ¶->pango_items[pango_item_index].item->analysis, @@ -1324,11 +1373,14 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const /* glyphs[].x_offset values may be out of order within any log_clusters, apparently harmless */ } new_span.pango_item_index = pango_item_index; - _computeFontLineHeight(para->pango_items[pango_item_index].font, new_span.font_size, text_source->style, &new_span.line_height, &new_span.line_height_multiplier); + new_span.line_height_multiplier = _computeFontLineHeight( text_source->style ); + new_span.line_height.set( para->pango_items[pango_item_index].font ); + new_span.line_height *= new_span.font_size; // At some point we may want to calculate baseline_shift here (to take advantage // of otm features like superscript baseline), but for now we use style baseline_shift. new_span.baseline_shift = text_source->style->baseline_shift.computed; + new_span.text_orientation = (SPCSSTextOrientation)text_source->style->text_orientation.computed; // TODO: metrics for vertical text TRACE(("add text span %lu \"%s\"\n", para->unbroken_spans.size(), text_source->text->raw().substr(span_start_byte_in_source, new_span.text_bytes).c_str())); @@ -1338,11 +1390,13 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const new_span.pango_item_index = -1; font_instance *font = text_source->styleGetFontInstance(); if (font) { - _computeFontLineHeight(font, new_span.font_size, text_source->style, &new_span.line_height, &new_span.line_height_multiplier); + new_span.line_height_multiplier = _computeFontLineHeight( text_source->style ); + new_span.line_height.set( font ); + new_span.line_height *= new_span.font_size; font->Unref(); } else { - new_span.line_height.setZero(); - new_span.line_height_multiplier = 1.0; + new_span.line_height *= 0.0; // Set all to zero + new_span.line_height_multiplier = LINE_HEIGHT_NORMAL; } TRACE(("add style init span %lu\n", para->unbroken_spans.size())); } @@ -1392,60 +1446,63 @@ bool Layout::Calculator::_goToNextWrapShape() * bits of information that will prove useful when we come to output the * line to #_flow. Returns with \a start_span_pos set to the end of the * text that was fitted, \a chunk_info completely filled out and - * \a line_height set to the largest line box on the line. The return + * \a line_box_height set with the largest ascent and the largest + * descent (individually per CSS) on the line. The return * value is false only if we've run out of shapes to wrap inside (and * hence couldn't create any chunks). */ bool Layout::Calculator::_findChunksForLine(ParagraphInfo const ¶, UnbrokenSpanPosition *start_span_pos, std::vector<ChunkInfo> *chunk_info, - LineHeight *line_height) + FontMetrics *line_box_height) { - // init the initial line_height + TRACE((" begin _findChunksForLine: chunks: %lu\n", chunk_info->size())); + // init the initial line_box_height if (start_span_pos->iter_span == para.unbroken_spans.end()) { if (_flow._spans.empty()) { // empty first para: create a font for the sole purpose of measuring it InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream.front()); font_instance *font = text_source->styleGetFontInstance(); if (font) { - double font_size = text_source->styleComputeFontSize(); - double multiplier; - _computeFontLineHeight(font, font_size, text_source->style, line_height, &multiplier); + double multiplier = _computeFontLineHeight(text_source->style); + line_box_height->set( font ); + *line_box_height *= text_source->style->font_size.computed; font->Unref(); - *line_height *= multiplier; - _scanline_maker->setNewYCoordinate(_scanline_maker->yCoordinate() - line_height->ascent); + line_box_height->computeEffective( multiplier ); + TRACE((" initial next line top_of_line_box: %f\n", _scanline_maker->yCoordinate() - line_box_height->ascent )); + _scanline_maker->setNewYCoordinate(_scanline_maker->yCoordinate() - line_box_height->ascent); } } // else empty subsequent para: keep the old line height } else { if (_flow._input_wrap_shapes.empty()) { - // if we're not wrapping set the line_height big and negative so we can use negative line height - line_height->ascent = -1.0e10; - line_height->descent = -1.0e10; - line_height->leading = -1.0e10; + // if we're not wrapping set the line_box_height big and negative so we can use negative line height + line_box_height->ascent = -1.0e10; + line_box_height->descent = -1.0e10; } else - line_height->setZero(); + line_box_height->setZero(); } + TRACE((" initial line_box_height: %f\n", line_box_height->emSize() )); UnbrokenSpanPosition span_pos; for( ; ; ) { std::vector<ScanlineMaker::ScanRun> scan_runs; - scan_runs = _scanline_maker->makeScanline(*line_height); // Only one line with "InfiniteScanlineMaker + scan_runs = _scanline_maker->makeScanline(*line_box_height); // Only one line with "InfiniteScanlineMaker while (scan_runs.empty()) { // Only used by ShapeScanlineMaker if (!_goToNextWrapShape()) return false; // no more shapes to wrap in to - scan_runs = _scanline_maker->makeScanline(*line_height); + scan_runs = _scanline_maker->makeScanline(*line_box_height); } - TRACE(("finding line fit y=%f, %lu scan runs\n", scan_runs.front().y, scan_runs.size())); + TRACE((" finding line fit y=%f, %lu scan runs\n", scan_runs.front().y, scan_runs.size())); chunk_info->clear(); chunk_info->reserve(scan_runs.size()); if (para.direction == RIGHT_TO_LEFT) std::reverse(scan_runs.begin(), scan_runs.end()); unsigned scan_run_index; span_pos = *start_span_pos; for (scan_run_index = 0 ; scan_run_index < scan_runs.size() ; scan_run_index++) { - if (!_buildChunksInScanRun(para, span_pos, scan_runs[scan_run_index], chunk_info, line_height)) + if (!_buildChunksInScanRun(para, span_pos, scan_runs[scan_run_index], chunk_info, line_box_height)) break; if (!chunk_info->empty() && !chunk_info->back().broken_spans.empty()) span_pos = chunk_info->back().broken_spans.back().end; @@ -1453,6 +1510,8 @@ bool Layout::Calculator::_findChunksForLine(ParagraphInfo const ¶, if (scan_run_index == scan_runs.size()) break; // ie when buildChunksInScanRun() succeeded } *start_span_pos = span_pos; + TRACE((" final line_box_height: %f\n", line_box_height->emSize() )); + TRACE((" end _findChunksForLine: chunks: %lu\n", chunk_info->size())); return true; } @@ -1474,8 +1533,9 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const ¶, UnbrokenSpanPosition const &start_span_pos, ScanlineMaker::ScanRun const &scan_run, std::vector<ChunkInfo> *chunk_info, - LineHeight *line_height) const + FontMetrics *line_height) const { + TRACE((" begin _buildChunksInScanRun: chunks: %lu\n", chunk_info->size())); ChunkInfo new_chunk; new_chunk.text_width = 0.0; new_chunk.whitespace_count = 0; @@ -1489,7 +1549,7 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const ¶, last_span_at_emergency_break.start = start_span_pos; last_span_at_emergency_break.setZero(); - TRACE(("trying chunk from %f to %g\n", scan_run.x_start, scan_run.x_end)); + TRACE((" trying chunk from %f to %g\n", scan_run.x_start, scan_run.x_end)); BrokenSpan new_span; new_span.end = start_span_pos; while (new_span.end.iter_span != para.unbroken_spans.end()) { // this loops once for each UnbrokenSpan @@ -1511,16 +1571,17 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const ¶, } // see if this span is too tall to fit on the current line - LineHeight total_height = new_span.start.iter_span->line_height; - total_height *= new_span.start.iter_span->line_height_multiplier; + FontMetrics new_span_height = new_span.start.iter_span->line_height; + new_span_height.computeEffective( new_span.start.iter_span->line_height_multiplier ); /* floating point 80-bit/64-bit rounding problems require epsilon. See discussion http://inkscape.gristle.org/2005-03-16.txt around 22:00 */ - if ( total_height.ascent > line_height->ascent + FLT_EPSILON - || total_height.descent > line_height->descent + FLT_EPSILON - || total_height.leading > line_height->leading + FLT_EPSILON) { - line_height->max(total_height); - if (!_scanline_maker->canExtendCurrentScanline(*line_height)) + if ( new_span_height.ascent > line_height->ascent + FLT_EPSILON || + new_span_height.descent > line_height->descent + FLT_EPSILON) { + // Take larger of each of the two ascents and two descents per CSS + line_height->max(new_span_height); + if (!_scanline_maker->canExtendCurrentScanline(*line_height)) { return false; + } } bool span_fitted = _measureUnbrokenSpan(para, &new_span, &last_span_at_break, &last_span_at_emergency_break, new_chunk.scanrun_width - new_chunk.text_width); @@ -1537,10 +1598,10 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const ¶, } } - TRACE(("chunk complete, used %f width (%d whitespaces, %lu brokenspans)\n", new_chunk.text_width, new_chunk.whitespace_count, new_chunk.broken_spans.size())); + TRACE((" chunk complete, used %f width (%d whitespaces, %lu brokenspans)\n", new_chunk.text_width, new_chunk.whitespace_count, new_chunk.broken_spans.size())); chunk_info->push_back(new_chunk); - if (scan_run.width() >= 4.0 * line_height->total() && last_span_at_break.end == start_span_pos) { + if (scan_run.width() >= 4.0 * line_height->emSize() && last_span_at_break.end == start_span_pos) { /* **non-SVG spec bit**: See bug #1191102 If the user types a very long line with no spaces, the way the spec is written at the moment means that when the length of the text @@ -1575,12 +1636,25 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const ¶, chunk_info->back().text_width += last_span_at_break.width; chunk_info->back().whitespace_count += last_span_at_break.whitespace_count; } - TRACE(("correction: fitted span %lu width = %f\n", last_span_at_break.start.iter_span - para.unbroken_spans.begin(), last_span_at_break.width)); + TRACE((" correction: fitted span %lu width = %f\n", last_span_at_break.start.iter_span - para.unbroken_spans.begin(), last_span_at_break.width)); + } + } + + // Recalculate line_box_height after backing out chunks + line_height->setZero(); + for (std::vector<ChunkInfo>::const_iterator it_chunk = chunk_info->begin() ; it_chunk != chunk_info->end() ; it_chunk++) { + for (std::vector<BrokenSpan>::const_iterator it_span = it_chunk->broken_spans.begin() ; it_span != it_chunk->broken_spans.end() ; it_span++) { + TRACE((" brokenspan line_height: %f\n", it_span->start.iter_span->line_height.emSize() )); + FontMetrics span_height = it_span->start.iter_span->line_height; + span_height.computeEffective( it_span->start.iter_span->line_height_multiplier ); + line_height->max( span_height ); } } + TRACE((" line_box_height: %f\n", line_height->emSize())); if (!chunk_info->empty() && !chunk_info->back().broken_spans.empty() && chunk_info->back().broken_spans.back().ends_with_whitespace) { // for justification we need to discard space occupied by the single whitespace at the end of the chunk + TRACE((" backing out whitespace\n")); chunk_info->back().broken_spans.back().ends_with_whitespace = false; chunk_info->back().broken_spans.back().width -= chunk_info->back().broken_spans.back().each_whitespace_width; chunk_info->back().broken_spans.back().whitespace_count--; @@ -1592,9 +1666,10 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const ¶, // for justification we need to discard line-spacing and word-spacing at end of the chunk chunk_info->back().broken_spans.back().width -= chunk_info->back().broken_spans.back().letter_spacing; chunk_info->back().text_width -= chunk_info->back().broken_spans.back().letter_spacing; - TRACE(("width after subtracting last letter_spacing: %f\n", chunk_info->back().broken_spans.back().width)); + TRACE((" width after subtracting last letter_spacing: %f\n", chunk_info->back().broken_spans.back().width)); } + TRACE((" end _buildChunksInScanRun: chunks: %lu\n", chunk_info->size())); return true; } @@ -1614,19 +1689,32 @@ bool Layout::Calculator::calculate() g_warning("flow text is not of type TEXT_SOURCE. Abort."); return false; } - TRACE(("begin calculateFlow()\n")); + TRACE(("begin calculate()\n")); _flow._clearOutputObjects(); _pango_context = (font_factory::Default())->fontContext; + _font_factory_size_multiplier = (font_factory::Default())->fontSize; + // Reset gravity hint in case it was changed via previous use of 'text-orientation' + // (scripts take their natural gravity given base gravity). + pango_context_set_gravity_hint(_pango_context, PANGO_GRAVITY_HINT_NATURAL); + _block_progression = _flow._blockProgression(); + if( _block_progression == RIGHT_TO_LEFT || _block_progression == LEFT_TO_RIGHT ) { + // Vertical text, CJK + pango_context_set_base_gravity(_pango_context, PANGO_GRAVITY_EAST); + } else { + // Horizontal text + pango_context_set_base_gravity(_pango_context, PANGO_GRAVITY_AUTO); + } + _y_offset = 0.0; _createFirstScanlineMaker(); ParagraphInfo para; - LineHeight line_height; // needs to be maintained across paragraphs to be able to deal with blank paras + FontMetrics line_box_height; // needs to be maintained across paragraphs to be able to deal with blank paras for(para.first_input_index = 0 ; para.first_input_index < _flow._input_stream.size() ; ) { // jump to the next wrap shape if this is a SHAPE_BREAK control code if (_flow._input_stream[para.first_input_index]->Type() == CONTROL_CODE) { @@ -1640,7 +1728,10 @@ bool Layout::Calculator::calculate() if (_scanline_maker == NULL) break; // we're trying to flow past the last wrap shape - _buildPangoItemizationForPara(¶); + // Break things up into little pango units with unique direction, gravity, etc. + _buildPangoItemizationForPara(¶); + + // Do shaping (convert characters to glyphs) unsigned para_end_input_index = _buildSpansForPara(¶); if (_flow._input_stream[para.first_input_index]->Type() == TEXT_SOURCE) @@ -1663,11 +1754,13 @@ bool Layout::Calculator::calculate() do { // for each line in the paragraph TRACE(("begin line\n")); std::vector<ChunkInfo> line_chunk_info; - if (!_findChunksForLine(para, &span_pos, &line_chunk_info, &line_height)) + if (!_findChunksForLine(para, &span_pos, &line_chunk_info, &line_box_height)) break; // out of shapes to wrap in to - _outputLine(para, line_height, line_chunk_info); + _outputLine(para, line_box_height, line_chunk_info); + _scanline_maker->setLineHeight( line_box_height ); _scanline_maker->completeLine(); // Increments y by line height + TRACE(("end line\n")); } while (span_pos.iter_span != para.unbroken_spans.end()); TRACE(("para %lu end\n\n", _flow._paragraphs.size() - 1)); @@ -1679,8 +1772,8 @@ bool Layout::Calculator::calculate() Layout::Span new_span; if (_flow._spans.empty()) { new_span.font = NULL; - new_span.font_size = line_height.ascent + line_height.descent; - new_span.line_height = line_height; + new_span.font_size = line_box_height.emSize(); + new_span.line_height = line_box_height; new_span.x_end = 0.0; } else { new_span = _flow._spans.back(); @@ -1755,19 +1848,16 @@ void Layout::_calculateCursorShapeForEmpty() InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream.front()); font_instance *font = text_source->styleGetFontInstance(); - double font_size = text_source->styleComputeFontSize(); + double font_size = text_source->style->font_size.computed; double caret_slope_run = 0.0, caret_slope_rise = 1.0; - LineHeight line_height; + FontMetrics line_height; if (font) { const_cast<font_instance*>(font)->FontSlope(caret_slope_run, caret_slope_rise); - font->FontMetrics(line_height.ascent, line_height.descent, line_height.leading); + font->FontMetrics(line_height.ascent, line_height.descent, line_height.xheight); line_height *= font_size; font->Unref(); - } else { - line_height.ascent = font_size * 0.85; // random guesses - line_height.descent = font_size * 0.15; - line_height.leading = 0.0; } + double caret_slope = atan2(caret_slope_run, caret_slope_rise); _empty_cursor_shape.height = font_size / cos(caret_slope); _empty_cursor_shape.rotation = caret_slope; @@ -1780,16 +1870,20 @@ void Layout::_calculateCursorShapeForEmpty() ShapeScanlineMaker scanline_maker(_input_wrap_shapes.front().shape, block_progression); std::vector<ScanlineMaker::ScanRun> scan_runs = scanline_maker.makeScanline(line_height); if (!scan_runs.empty()) { - if (block_progression == LEFT_TO_RIGHT || block_progression == RIGHT_TO_LEFT) + if (block_progression == LEFT_TO_RIGHT || block_progression == RIGHT_TO_LEFT) { + // Vertical text _empty_cursor_shape.position = Geom::Point(scan_runs.front().y + font_size, scan_runs.front().x_start); - else + } else { + // Horizontal text _empty_cursor_shape.position = Geom::Point(scan_runs.front().x_start, scan_runs.front().y + font_size); + } } } } bool Layout::calculateFlow() { + TRACE(("begin calculateFlow()\n")); Layout::Calculator calc = Calculator(this); bool result = calc.calculate(); if (textLengthIncrement != 0) { diff --git a/src/libnrtype/Layout-TNG-Input.cpp b/src/libnrtype/Layout-TNG-Input.cpp index 77480cebe..6f5d4e5f8 100644 --- a/src/libnrtype/Layout-TNG-Input.cpp +++ b/src/libnrtype/Layout-TNG-Input.cpp @@ -126,52 +126,6 @@ int Layout::_enum_converter(int input, EnumConversionItem const *conversion_tabl return conversion_table[0].output; } -// ***** the style format interface -// this doesn't include all accesses to SPStyle, only the ones that are non-trivial - -static const float medium_font_size = 12.0; // more of a default if all else fails than anything else -float Layout::InputStreamTextSource::styleComputeFontSize() const -{ - return style->font_size.computed; - - // in case the computed value's not good enough, here's some manual code held in reserve: - SPStyle const *this_style = style; - float inherit_multiplier = 1.0; - - for ( ; ; ) { - if (this_style->font_size.set && !this_style->font_size.inherit) { - switch (this_style->font_size.type) { - case SP_FONT_SIZE_LITERAL: { - switch(this_style->font_size.literal) { // these multipliers are straight out of the CSS spec - case SP_CSS_FONT_SIZE_XX_SMALL: return medium_font_size * inherit_multiplier * (3.0/5.0); - case SP_CSS_FONT_SIZE_X_SMALL: return medium_font_size * inherit_multiplier * (3.0/4.0); - case SP_CSS_FONT_SIZE_SMALL: return medium_font_size * inherit_multiplier * (8.0/9.0); - default: - case SP_CSS_FONT_SIZE_MEDIUM: return medium_font_size * inherit_multiplier; - case SP_CSS_FONT_SIZE_LARGE: return medium_font_size * inherit_multiplier * (6.0/5.0); - case SP_CSS_FONT_SIZE_X_LARGE: return medium_font_size * inherit_multiplier * (3.0/2.0); - case SP_CSS_FONT_SIZE_XX_LARGE: return medium_font_size * inherit_multiplier * 2.0; - case SP_CSS_FONT_SIZE_SMALLER: inherit_multiplier *= 0.84; break; //not exactly according to spec - case SP_CSS_FONT_SIZE_LARGER: inherit_multiplier *= 1.26; break; //not exactly according to spec - } - break; - } - case SP_FONT_SIZE_PERCENTAGE: { // 'em' units should be in here, but aren't. Fix in style.cpp. - inherit_multiplier *= this_style->font_size.value; - break; - } - case SP_FONT_SIZE_LENGTH: { - return this_style->font_size.value * inherit_multiplier; - } - } - } - if (this_style->object == NULL || this_style->object->parent == NULL) break; - this_style = this_style->object->parent->style; - if (this_style == NULL) break; - } - return medium_font_size * inherit_multiplier; -} - Layout::Direction Layout::InputStreamTextSource::styleGetBlockProgression() const { switch( style->writing_mode.computed ) { @@ -192,6 +146,16 @@ Layout::Direction Layout::InputStreamTextSource::styleGetBlockProgression() cons return TOP_TO_BOTTOM; } +SPCSSTextOrientation Layout::InputStreamTextSource::styleGetTextOrientation() const +{ + return ((SPCSSTextOrientation)style->text_orientation.computed); +} + +SPCSSBaseline Layout::InputStreamTextSource::styleGetDominantBaseline() const +{ + return ((SPCSSBaseline)style->dominant_baseline.computed); +} + static Layout::Alignment text_anchor_to_alignment(unsigned anchor, Layout::Direction para_direction) { switch (anchor) { diff --git a/src/libnrtype/Layout-TNG-OutIter.cpp b/src/libnrtype/Layout-TNG-OutIter.cpp index 137fe0a0f..8c29b7dbc 100644 --- a/src/libnrtype/Layout-TNG-OutIter.cpp +++ b/src/libnrtype/Layout-TNG-OutIter.cpp @@ -120,7 +120,8 @@ Layout::iterator Layout::getNearestCursorPositionTo(double x, double y) const double best_y_range = DBL_MAX; double best_x_range = DBL_MAX; for (chunk_index = 0 ; chunk_index < _chunks.size() ; chunk_index++) { - LineHeight line_height = {0.0, 0.0, 0.0}; + FontMetrics line_height; + line_height *= 0.0; // Set all metrics to zero. double chunk_width = 0.0; for ( ; span_index < _spans.size() && _spans[span_index].in_chunk == chunk_index ; span_index++) { line_height.max(_spans[span_index].line_height); @@ -340,8 +341,8 @@ Geom::Rect Layout::characterBoundingBox(iterator const &it, double *rotation) co double baseline_y = _characters[char_index].line(this).baseline_y + _characters[char_index].span(this).baseline_shift; if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) { - double span_height = _spans[_characters[char_index].in_span].line_height.ascent + _spans[_characters[char_index].in_span].line_height.descent; - top_left[Geom::Y] = top_left[Geom::X]; + double span_height = _spans[_characters[char_index].in_span].line_height.emSize(); + top_left[Geom::Y] = top_left[Geom::X]; top_left[Geom::X] = baseline_y - span_height * 0.5; bottom_right[Geom::Y] = bottom_right[Geom::X]; bottom_right[Geom::X] = baseline_y + span_height * 0.5; @@ -404,7 +405,7 @@ std::vector<Geom::Point> Layout::createSelectionShape(iterator const &it_start, double vertical_scale = _glyphs.back().vertical_scale; if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) { - double span_height = vertical_scale * (_spans[span_index].line_height.ascent + _spans[span_index].line_height.descent); + double span_height = vertical_scale * _spans[span_index].line_height.emSize(); top_left[Geom::Y] = top_left[Geom::X]; top_left[Geom::X] = baseline_y - span_height * 0.5; bottom_right[Geom::Y] = bottom_right[Geom::X]; @@ -510,17 +511,19 @@ void Layout::queryCursorShape(iterator const &it, Geom::Point &position, double double vertical_scale = _glyphs.empty() ? 1.0 : _glyphs.back().vertical_scale; if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) { - height = vertical_scale * span->line_height.ascent + span->line_height.descent; + // Vertical text + height = vertical_scale * span->line_height.emSize(); rotation += M_PI / 2; std::swap(position[Geom::X], position[Geom::Y]); position[Geom::X] -= vertical_scale * sin(rotation) * height * 0.5; position[Geom::Y] += vertical_scale * cos(rotation) * height * 0.5; } else { + // Horizontal text double caret_slope_run = 0.0, caret_slope_rise = 1.0; if (span->font) const_cast<font_instance*>(span->font)->FontSlope(caret_slope_run, caret_slope_rise); double caret_slope = atan2(caret_slope_run, caret_slope_rise); - height = vertical_scale * (span->line_height.ascent + span->line_height.descent) / cos(caret_slope); + height = vertical_scale * (span->line_height.emSize()) / cos(caret_slope); rotation += caret_slope; position[Geom::X] -= sin(rotation) * vertical_scale * span->line_height.descent; position[Geom::Y] += cos(rotation) * vertical_scale * span->line_height.descent; diff --git a/src/libnrtype/Layout-TNG-Output.cpp b/src/libnrtype/Layout-TNG-Output.cpp index 6e3faf33b..526319f35 100644 --- a/src/libnrtype/Layout-TNG-Output.cpp +++ b/src/libnrtype/Layout-TNG-Output.cpp @@ -93,26 +93,55 @@ void Layout::_clearOutputObjects() _path_fitted = NULL; } -void Layout::LineHeight::max(LineHeight const &other) +void Layout::FontMetrics::set(font_instance *font) { - if (other.ascent > ascent) ascent = other.ascent; - if (other.descent > descent) descent = other.descent; - if (other.leading > leading) leading = other.leading; + if( font != NULL ) { + ascent = font->GetTypoAscent(); + descent = font->GetTypoDescent(); + xheight = font->GetXHeight(); + ascent_max = font->GetMaxAscent(); + descent_max = font->GetMaxDescent(); + } +} + +void Layout::FontMetrics::max(FontMetrics const &other) +{ + if (other.ascent > ascent ) ascent = other.ascent; + if (other.descent > descent ) descent = other.descent; + if( other.xheight > xheight ) xheight = other.xheight; + if( other.ascent_max > ascent_max ) ascent_max = other.ascent_max; + if( other.descent_max > descent_max ) descent_max = other.descent_max; +} + +void Layout::FontMetrics::computeEffective( const double &line_height_multiplier ) { + double half_leading = 0.5 * (line_height_multiplier - 1.0) * emSize(); + ascent += half_leading; + descent += half_leading; } void Layout::_getGlyphTransformMatrix(int glyph_index, Geom::Affine *matrix) const { Span const &span = _glyphs[glyph_index].span(this); - double sin_rotation = sin(_glyphs[glyph_index].rotation); - double cos_rotation = cos(_glyphs[glyph_index].rotation); + double rotation = _glyphs[glyph_index].rotation; + if ( (span.block_progression == LEFT_TO_RIGHT || span.block_progression == RIGHT_TO_LEFT) && + _glyphs[glyph_index].orientation == ORIENTATION_SIDEWAYS ) { + // Vertical sideways text + rotation += M_PI/2.0; + } + double sin_rotation = sin(rotation); + double cos_rotation = cos(rotation); (*matrix)[0] = span.font_size * cos_rotation; (*matrix)[1] = span.font_size * sin_rotation; (*matrix)[2] = span.font_size * sin_rotation; (*matrix)[3] = -span.font_size * cos_rotation * (_glyphs[glyph_index].vertical_scale); // unscale vertically so the specified text height is preserved if lengthAdjust=spacingAndGlyphs if (span.block_progression == LEFT_TO_RIGHT || span.block_progression == RIGHT_TO_LEFT) { + // Vertical text + // This effectively swaps x for y which changes handedness of coordinate system. This is a bit strange + // and not what one would expect but the compute code already reverses y so OK. (*matrix)[4] = _lines[_chunks[span.in_chunk].in_line].baseline_y + _glyphs[glyph_index].y; (*matrix)[5] = _chunks[span.in_chunk].left_x + _glyphs[glyph_index].x; } else { + // Horizontal text (*matrix)[4] = _chunks[span.in_chunk].left_x + _glyphs[glyph_index].x; (*matrix)[5] = _lines[_chunks[span.in_chunk].in_line].baseline_y + _glyphs[glyph_index].y; } @@ -127,9 +156,9 @@ void Layout::show(DrawingGroup *in_arena, Geom::OptRect const &paintbox) const InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[_spans[span_index].in_input_stream_item]); text_source->style->text_decoration_data.tspan_width = _spans[span_index].width(); - text_source->style->text_decoration_data.ascender = _spans[span_index].line_height.getAscent(); - text_source->style->text_decoration_data.descender = _spans[span_index].line_height.getDescent(); - text_source->style->text_decoration_data.line_gap = _spans[span_index].line_height.getLeading(); + text_source->style->text_decoration_data.ascender = _spans[span_index].line_height.getTypoAscent(); + text_source->style->text_decoration_data.descender = _spans[span_index].line_height.getTypoDescent(); + if(!span_index || (_chunks[_spans[span_index].in_chunk].in_line != _chunks[_spans[span_index-1].in_chunk].in_line)){ text_source->style->text_decoration_data.tspan_line_start = true; @@ -170,11 +199,14 @@ void Layout::show(DrawingGroup *in_arena, Geom::OptRect const &paintbox) const first_line_glyph = false; phase0 = glyph_matrix.translation()[Geom::X]; } - // save the starting coordinates for the line - these are needed for figuring out dot/dash/wave phase + // Save the starting coordinates for the line - these are needed for figuring out + // dot/dash/wave phase. + // Use maximum ascent and descent to ensure glpyhs that extend outside the embox + // are fully drawn. (void) nr_text->addComponent(_spans[span_index].font, _glyphs[glyph_index].glyph, glyph_matrix, _glyphs[glyph_index].width, - _spans[span_index].line_height.getAscent(), - _spans[span_index].line_height.getDescent(), + _spans[span_index].line_height.getMaxAscent(), + _spans[span_index].line_height.getMaxDescent(), glyph_matrix.translation()[Geom::X] - phase0 ); } @@ -580,7 +612,7 @@ Glib::ustring Layout::dumpAsText() const result += Glib::ustring::compose(" font '%1' %2 %3 %4\n", sp_font_description_get_family(_spans[span_index].font->descr), _spans[span_index].font_size, style_to_text(pango_font_description_get_style(_spans[span_index].font->descr)), weight_to_text(pango_font_description_get_weight(_spans[span_index].font->descr))); } result += Glib::ustring::compose(" x_start = %1, x_end = %2\n", _spans[span_index].x_start, _spans[span_index].x_end) - + Glib::ustring::compose(" line height: ascent %1, descent %2 leading %3\n", _spans[span_index].line_height.ascent, _spans[span_index].line_height.descent, _spans[span_index].line_height.leading) + + Glib::ustring::compose(" line height: ascent %1, descent %2\n", _spans[span_index].line_height.ascent, _spans[span_index].line_height.descent) + Glib::ustring::compose(" direction %1, block-progression %2\n", direction_to_text(_spans[span_index].direction), direction_to_text(_spans[span_index].block_progression)) + " ** characters:\n"; Glib::ustring::const_iterator iter_char = _spans[span_index].input_stream_first_character; diff --git a/src/libnrtype/Layout-TNG-Scanline-Maker.h b/src/libnrtype/Layout-TNG-Scanline-Maker.h index d513d7cc1..7c90ea8cc 100644 --- a/src/libnrtype/Layout-TNG-Scanline-Maker.h +++ b/src/libnrtype/Layout-TNG-Scanline-Maker.h @@ -47,7 +47,7 @@ public: between calls if the new height too big to fit in the space remaining in this shape. Returns an empty vector if there is no space left in the current shape. */ - virtual std::vector<ScanRun> makeScanline(Layout::LineHeight const &line_height) =0; + virtual std::vector<ScanRun> makeScanline(Layout::FontMetrics const &line_height) =0; /** Indicates that the caller has successfully filled the current line and hence that the next call to makeScanline() should return lines on @@ -71,7 +71,12 @@ public: The metrics given here are considered to be the ones that are being used now, and hence is the line advance height used by completeLine(). */ - virtual bool canExtendCurrentScanline(Layout::LineHeight const &line_height) =0; + virtual bool canExtendCurrentScanline(Layout::FontMetrics const &line_height) =0; + + /** Sets current line block height. Call before completeLine() to correct for + actually used line height (in case some chunks with larger font-size rolled back). + */ + virtual void setLineHeight(Layout::FontMetrics const &line_height) =0; }; /** \brief private to Layout. Generates infinite scanlines for when you don't want wrapping @@ -90,7 +95,7 @@ public: virtual ~InfiniteScanlineMaker(); /** Returns a single infinite run at the current location */ - virtual std::vector<ScanRun> makeScanline(Layout::LineHeight const &line_height); + virtual std::vector<ScanRun> makeScanline(Layout::FontMetrics const &line_height); /** Increments the current y by the current line height */ virtual void completeLine(); @@ -102,11 +107,16 @@ public: virtual void setNewYCoordinate(double new_y); /** Always true, but has to save the new height */ - virtual bool canExtendCurrentScanline(Layout::LineHeight const &line_height); + virtual bool canExtendCurrentScanline(Layout::FontMetrics const &line_height); + + /** Sets current line block height. Call before completeLine() to correct for + actually used line height (in case some chunks with larger font-size rolled back). + */ + virtual void setLineHeight(Layout::FontMetrics const &line_height); private: double _x, _y; - Layout::LineHeight _current_line_height; + Layout::FontMetrics _current_line_height; bool _negative_block_progression; /// if true, indicates that completeLine() should decrement rather than increment, ie block-progression is either rl or bt }; @@ -122,7 +132,7 @@ public: ShapeScanlineMaker(Shape const *shape, Layout::Direction block_progression); virtual ~ShapeScanlineMaker(); - virtual std::vector<ScanRun> makeScanline(Layout::LineHeight const &line_height); + virtual std::vector<ScanRun> makeScanline(Layout::FontMetrics const &line_height); virtual void completeLine(); @@ -131,7 +141,13 @@ public: virtual void setNewYCoordinate(double new_y); /** never true */ - virtual bool canExtendCurrentScanline(Layout::LineHeight const &line_height); + virtual bool canExtendCurrentScanline(Layout::FontMetrics const &line_height); + + /** Sets current line block height. Call before completeLine() to correct for + actually used line height (in case some chunks with larger font-size rolled back). + */ + virtual void setLineHeight(Layout::FontMetrics const &line_height); + private: /** To generate scanlines for top-to-bottom text it is easiest if we simply rotate the given shape by a multiple of 90 degrees. This stores diff --git a/src/libnrtype/Layout-TNG-Scanline-Makers.cpp b/src/libnrtype/Layout-TNG-Scanline-Makers.cpp index 7144f3876..dcc973a24 100644 --- a/src/libnrtype/Layout-TNG-Scanline-Makers.cpp +++ b/src/libnrtype/Layout-TNG-Scanline-Makers.cpp @@ -19,9 +19,7 @@ namespace Text { Layout::InfiniteScanlineMaker::InfiniteScanlineMaker(double initial_x, double initial_y, Layout::Direction block_progression) { - _current_line_height.ascent = 0.0; - _current_line_height.descent = 0.0; - _current_line_height.leading = 0.0; + _current_line_height.setZero(); switch (block_progression) { case LEFT_TO_RIGHT: case RIGHT_TO_LEFT: @@ -41,7 +39,7 @@ Layout::InfiniteScanlineMaker::~InfiniteScanlineMaker() { } -std::vector<Layout::ScanlineMaker::ScanRun> Layout::InfiniteScanlineMaker::makeScanline(Layout::LineHeight const &line_height) +std::vector<Layout::ScanlineMaker::ScanRun> Layout::InfiniteScanlineMaker::makeScanline(Layout::FontMetrics const &line_height) { std::vector<ScanRun> runs(1); runs[0].x_start = _x; @@ -54,12 +52,10 @@ std::vector<Layout::ScanlineMaker::ScanRun> Layout::InfiniteScanlineMaker::makeS void Layout::InfiniteScanlineMaker::completeLine() { if (_negative_block_progression) - _y -= _current_line_height.total(); + _y -= _current_line_height.emSize(); else - _y += _current_line_height.total(); - _current_line_height.ascent = 0.0; - _current_line_height.descent = 0.0; - _current_line_height.leading = 0.0; + _y += _current_line_height.emSize(); + _current_line_height.setZero(); } void Layout::InfiniteScanlineMaker::setNewYCoordinate(double new_y) @@ -67,12 +63,17 @@ void Layout::InfiniteScanlineMaker::setNewYCoordinate(double new_y) _y = new_y; } -bool Layout::InfiniteScanlineMaker::canExtendCurrentScanline(Layout::LineHeight const &line_height) +bool Layout::InfiniteScanlineMaker::canExtendCurrentScanline(Layout::FontMetrics const &line_height) { _current_line_height = line_height; return true; } +void Layout::InfiniteScanlineMaker::setLineHeight(Layout::FontMetrics const &line_height) +{ + _current_line_height = line_height; +} + // *********************** real shapes version Layout::ShapeScanlineMaker::ShapeScanlineMaker(Shape const *shape, Layout::Direction block_progression) @@ -111,29 +112,29 @@ Layout::ShapeScanlineMaker::~ShapeScanlineMaker() delete _rotated_shape; } -std::vector<Layout::ScanlineMaker::ScanRun> Layout::ShapeScanlineMaker::makeScanline(Layout::LineHeight const &line_height) +std::vector<Layout::ScanlineMaker::ScanRun> Layout::ShapeScanlineMaker::makeScanline(Layout::FontMetrics const &line_height) { - FloatLigne line_rasterization; - FloatLigne line_decent_length_runs; - float line_text_height = (float)(line_height.ascent + line_height.descent); - if (_y > _bounding_box_bottom) return std::vector<ScanRun>(); if (_y < _bounding_box_top) _y = _bounding_box_top; + FloatLigne line_rasterization; + FloatLigne line_decent_length_runs; + float line_text_height = (float)(line_height.emSize()); if (line_text_height == 0.0) line_text_height = 0.001; // Scan() doesn't work for zero height so this will have to do - _current_line_height = (float)line_height.total(); + _current_line_height = (float)line_height.emSize(); // I think what's going on here is that we're moving the top of the scanline to the given position... _rotated_shape->Scan(_rasterizer_y, _current_rasterization_point, _y, line_text_height); // ...then actually retreiving the scanline (which alters the first two parameters) _rotated_shape->Scan(_rasterizer_y, _current_rasterization_point, _y + line_text_height , &line_rasterization, true, line_text_height); - // sanitise the raw rasterisation, which could have weird overlaps + // sanitise the raw rasterisation, which could have weird overlaps line_rasterization.Flatten(); + // line_rasterization.Affiche(); // cut out runs that cover less than 90% of the line line_decent_length_runs.Over(&line_rasterization, 0.9 * line_text_height); @@ -145,7 +146,7 @@ std::vector<Layout::ScanlineMaker::ScanRun> Layout::ShapeScanlineMaker::makeScan std::vector<ScanRun> result(1); result[0].x_start = line_rasterization.runs[0].st; result[0].x_end = line_rasterization.runs[0].st; - result[0].y = _negative_block_progression ? -_current_line_height - _y : _y; + result[0].y = _negative_block_progression ? - _y : _y; return result; } @@ -154,7 +155,7 @@ std::vector<Layout::ScanlineMaker::ScanRun> Layout::ShapeScanlineMaker::makeScan for (unsigned i = 0 ; i < result.size() ; i++) { result[i].x_start = line_decent_length_runs.runs[i].st; result[i].x_end = line_decent_length_runs.runs[i].en; - result[i].y = _negative_block_progression ? -_current_line_height - _y : _y; + result[i].y = _negative_block_progression ? - _y : _y; } return result; @@ -167,23 +168,28 @@ void Layout::ShapeScanlineMaker::completeLine() double Layout::ShapeScanlineMaker::yCoordinate() { - if (_negative_block_progression) return -_current_line_height - _y; + if (_negative_block_progression) return - _y; return _y; } void Layout::ShapeScanlineMaker::setNewYCoordinate(double new_y) { _y = (float)new_y; - if (_negative_block_progression) _y = -_current_line_height - _y; + if (_negative_block_progression) _y = - _y; // what will happen with the rasteriser if we move off the shape? // it's not an important question because <flowSpan> doesn't have a y attribute } -bool Layout::ShapeScanlineMaker::canExtendCurrentScanline(Layout::LineHeight const &/*line_height*/) +bool Layout::ShapeScanlineMaker::canExtendCurrentScanline(Layout::FontMetrics const &/*line_height*/) { //we actually could return true if only the leading changed, but that's too much effort for something that rarely happens return false; } +void Layout::ShapeScanlineMaker::setLineHeight(Layout::FontMetrics const &line_height) +{ + _current_line_height = line_height.emSize(); +} + }//namespace Text }//namespace Inkscape diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h index 26db1fad9..97a05bde8 100644 --- a/src/libnrtype/Layout-TNG.h +++ b/src/libnrtype/Layout-TNG.h @@ -22,6 +22,7 @@ #include <vector> #include <boost/optional.hpp> #include <svg/svg-length.h> +#include "style-enums.h" #ifdef HAVE_CAIRO_PDF namespace Inkscape { @@ -157,6 +158,9 @@ public: both the 'direction' and 'block-progression' CSS attributes. */ enum Direction {LEFT_TO_RIGHT, RIGHT_TO_LEFT, TOP_TO_BOTTOM, BOTTOM_TO_TOP}; + /** Used to specify orientation of glyphs in vertical text. */ + enum Orientation {ORIENTATION_UPRIGHT, ORIENTATION_SIDEWAYS}; + /** Display alignment for shapes. See appendWrapShape(). */ enum DisplayAlign {DISPLAY_ALIGN_BEFORE, DISPLAY_ALIGN_CENTER, DISPLAY_ALIGN_AFTER}; @@ -603,19 +607,69 @@ public: //@} - /// it's useful for this to be public so that ScanlineMaker can use it - struct LineHeight { - double ascent; - double descent; - double leading; - inline double total() const {return ascent + descent + leading;} - inline void setZero() {ascent = descent = leading = 0.0;} - inline LineHeight& operator*=(double x) {ascent *= x; descent *= x; leading *= x; return *this;} - void max(LineHeight const &other); /// makes this object contain the largest of all three members between this object and other - inline double getAscent() const {return ascent; } - inline double getDescent() const {return descent; } - inline double getLeading() const {return leading; } - }; + + /** + * Keep track of font metrics. Two use cases: + * 1. Keep track of ascent, descent, and x-height of an individual font. + * 2. Keep track of effective ascent and descent that includes half-leading. + * + * Note: Leading refers to the "external" leading which is added (subtracted) due to + * a computed value of 'line-height' that differs from 'font-size'. "Internal" leading + * which is specified inside a font is not used in CSS. The 'font-size' is based on + * the font's em size which is 'ascent' + 'descent'. + * + * This structure was renamed (and modified) from "LineHeight". + * + * It's useful for this to be public so that ScanlineMaker can use it. + */ + class FontMetrics { + + public: + FontMetrics() { reset(); } + + void reset() { + ascent = 0.8; + descent = -0.2; + xheight = 0.5; + ascent_max = 0.8; + descent_max = 0.2; + } + + void set( font_instance *font ); + + // CSS 2.1 dictates that font-size is based on em-size which is defined as ascent + descent + inline double emSize() const {return ascent + descent;} + // Alternatively name function for use 2. + inline double lineSize() const { return ascent + descent; } + inline void setZero() {ascent = descent = xheight = ascent_max = descent_max = 0.0;} + + // For scaling for 'font-size'. + inline FontMetrics& operator*=(double x) { + ascent *= x; descent *= x; xheight *= x; ascent_max *= x; descent_max *= x; + return *this; + } + + /// Save the larger values of ascent and descent between this and other. Needed for laying + /// out a line with mixed font-sizes, fonts, or line spacings. + void max(FontMetrics const &other); + + /// Calculate the effective ascent and descent including half "leading". + void computeEffective( const double &line_height ); + + inline double getTypoAscent() const {return ascent; } + inline double getTypoDescent() const {return descent; } + inline double getXHeight() const {return xheight; } + inline double getMaxAscent() const {return ascent_max; } + inline double getMaxDescent() const {return descent_max; } + + // private: + double ascent; // Typographic ascent. + double descent; // Typographic descent. + double xheight; // Height of 'x' measured from alphabetic baseline. + double ascent_max; // Maximum ascent of all glyphs in font. + double descent_max; // Maximum descent of all glyphs in font. + + }; // End FontMetrics /// see _enum_converter() struct EnumConversionItem { @@ -664,11 +718,12 @@ private: LengthAdjust lengthAdjust; // a few functions for some of the more complicated style accesses - float styleComputeFontSize() const; /// The return value must be freed with pango_font_description_free() PangoFontDescription *styleGetFontDescription() const; font_instance *styleGetFontInstance() const; Direction styleGetBlockProgression() const; + SPCSSTextOrientation styleGetTextOrientation() const; + SPCSSBaseline styleGetDominantBaseline() const; Alignment styleGetAlignment(Direction para_direction, bool try_text_align) const; }; @@ -707,6 +762,22 @@ private: return TOP_TO_BOTTOM; } + /** The overall text-orientation of the whole flow. */ + inline SPCSSTextOrientation _blockTextOrientation() const + { + if(!_input_stream.empty()) + return static_cast<InputStreamTextSource*>(_input_stream.front())->styleGetTextOrientation(); + return SP_CSS_TEXT_ORIENTATION_MIXED; + } + + /** The overall text-orientation of the whole flow. */ + inline SPCSSBaseline _blockBaseline() const + { + if(!_input_stream.empty()) + return static_cast<InputStreamTextSource*>(_input_stream.front())->styleGetDominantBaseline(); + return SP_CSS_BASELINE_AUTO; + } + /** so that LEFT_TO_RIGHT == RIGHT_TO_LEFT but != TOP_TO_BOTTOM */ static bool _directions_are_orthogonal(Direction d1, Direction d2); @@ -748,6 +819,7 @@ private: float x; /// relative to the start of the chunk float y; /// relative to the current line's baseline float rotation; /// absolute, modulo any object transforms, which we don't know about + Orientation orientation; /// Orientation of glyph in vertical text float width; float vertical_scale; /// to implement lengthAdjust="spacingAndGlyphs" that must scale glyphs only horizontally; instead we change font size and then undo that change vertically only inline Span const & span(Layout const *l) const {return l->_spans[l->_characters[in_character].in_span];} @@ -772,8 +844,9 @@ private: float x_start; /// relative to the start of the chunk float x_end; /// relative to the start of the chunk inline float width() const {return std::abs(x_start - x_end);} - LineHeight line_height; + FontMetrics line_height; double baseline_shift; /// relative to the line's baseline + SPCSSTextOrientation text_orientation; Direction direction; /// See CSS3 section 3.2. Either rtl or ltr Direction block_progression; /// See CSS3 section 3.2. The direction in which lines go. unsigned in_input_stream_item; diff --git a/src/libnrtype/TextWrapper.cpp b/src/libnrtype/TextWrapper.cpp index 380e9ba3f..124f3f7b4 100644 --- a/src/libnrtype/TextWrapper.cpp +++ b/src/libnrtype/TextWrapper.cpp @@ -862,7 +862,7 @@ void text_wrapper::MeasureBoxes(void) for (int i = 0; i < nbBox; i++) { boxes[i].ascent = 0; boxes[i].descent = 0; - boxes[i].leading = 0; + boxes[i].xheight = 0; boxes[i].width = 0; PangoFont *curPF = glyph_text[boxes[i].g_st].font; @@ -870,7 +870,7 @@ void text_wrapper::MeasureBoxes(void) PangoFontDescription *pfd = pango_font_describe(curPF); font_instance *curF = f_src->Face(pfd); if ( curF ) { - curF->FontMetrics(boxes[i].ascent, boxes[i].descent, boxes[i].leading); + curF->FontMetrics(boxes[i].ascent, boxes[i].descent, boxes[i].xheight); curF->Unref(); } pango_font_description_free(pfd); diff --git a/src/libnrtype/font-instance.h b/src/libnrtype/font-instance.h index 5a71e353b..2fac7c19b 100644 --- a/src/libnrtype/font-instance.h +++ b/src/libnrtype/font-instance.h @@ -57,19 +57,31 @@ public: // nota: all coordinates returned by these functions are on a [0..1] scale; you need to multiply // by the fontsize to get the real sizes + + // Return 2geom pathvector for glyph. Deallocated when font instance dies. Geom::PathVector* PathVector(int glyph_id); - // returns the 2geom-type pathvector for this glyph. no refcounting needed, it's deallocated when the font_instance dies + + // Horizontal advance if 'vertical' is false, vertical advance if true. double Advance(int glyph_id, bool vertical); - // nominal advance of the font. + + double GetTypoAscent() { return _ascent; } + double GetTypoDescent() { return _descent; } + double GetXHeight() { return _xheight; } + double GetMaxAscent() { return _ascent_max; } + double GetMaxDescent() { return _descent_max; } + const double* GetBaselines() { return _baselines; } + bool FontMetrics(double &ascent, double &descent, double &leading); - bool FontDecoration(double &underline_position, double &underline_thickness, - double &linethrough_position, double &linethrough_thickness); + bool FontDecoration(double &underline_position, double &underline_thickness, + double &linethrough_position, double &linethrough_thickness); bool FontSlope(double &run, double &rise); // for generating slanted cursors for oblique fonts - Geom::OptRect BBox(int glyph_id); + Geom::OptRect BBox(int glyph_id); private: void FreeTheFace(); + // Find ascent, descent, x-height, and baselines. + void FindFontMetrics(); // Temp: make public public: @@ -81,6 +93,17 @@ public: // as long as pFont is valid, theFace is too #endif +private: + + // Font metrics in em-box units + double _ascent; // Typographic ascent. + double _descent; // Typographic descent. + double _xheight; // x-height of font. + double _ascent_max; // Maxiumum ascent of all glyphs in font. + double _descent_max; // Maxiumum descent of all glyphs in font. + + // Baselines + double _baselines[SP_CSS_BASELINE_SIZE]; }; diff --git a/src/libnrtype/font-lister.cpp b/src/libnrtype/font-lister.cpp index 70374864a..568a7c8cc 100644 --- a/src/libnrtype/font-lister.cpp +++ b/src/libnrtype/font-lister.cpp @@ -6,7 +6,6 @@ #include <gtkmm/treemodel.h> #include <libnrtype/font-instance.h> -#include <libnrtype/TextWrapper.h> #include <libnrtype/one-glyph.h> #include "font-lister.h" diff --git a/src/libnrtype/one-box.h b/src/libnrtype/one-box.h index c868cf23f..c3b36a3ce 100644 --- a/src/libnrtype/one-box.h +++ b/src/libnrtype/one-box.h @@ -9,7 +9,7 @@ // this time for sp-typeset struct one_box { int g_st, g_en; ///< First and last glyph of this word. - double ascent, descent, leading; + double ascent, descent, xheight; double width; bool word_start, word_end; }; diff --git a/src/live_effects/lpe-interpolate_points.cpp b/src/live_effects/lpe-interpolate_points.cpp index 4ac139752..cf70832ee 100644 --- a/src/live_effects/lpe-interpolate_points.cpp +++ b/src/live_effects/lpe-interpolate_points.cpp @@ -52,8 +52,11 @@ Geom::PathVector LPEInterpolatePoints::doEffect_path (Geom::PathVector const & path_in) { Geom::PathVector path_out; - +#if __cplusplus <= 199711L std::auto_ptr<Geom::Interpolate::Interpolator> interpolator( Geom::Interpolate::Interpolator::create(static_cast<Geom::Interpolate::InterpolatorType>(interpolator_type.get_value())) ); +#else + std::unique_ptr<Geom::Interpolate::Interpolator> interpolator( Geom::Interpolate::Interpolator::create(static_cast<Geom::Interpolate::InterpolatorType>(interpolator_type.get_value())) ); +#endif for(Geom::PathVector::const_iterator path_it = path_in.begin(); path_it != path_in.end(); ++path_it) { if (path_it->empty()) diff --git a/src/live_effects/lpe-roughen.cpp b/src/live_effects/lpe-roughen.cpp index 310f791a1..b4ee54f1a 100644 --- a/src/live_effects/lpe-roughen.cpp +++ b/src/live_effects/lpe-roughen.cpp @@ -91,44 +91,6 @@ LPERoughen::LPERoughen(LivePathEffectObject *lpeobject) LPERoughen::~LPERoughen() {} -static void -sp_get_better_default_size(SPItem *item, double &value) -{ - if (SP_IS_GROUP(item)) { - std::vector<SPItem*> const item_list = sp_item_group_item_list(SP_GROUP(item)); - for ( std::vector<SPItem*>::const_iterator iter=item_list.begin();iter!=item_list.end();iter++) { - SPItem *subitem = *iter; - sp_get_better_default_size(subitem, value); - } - if(item_list.size() > 0){ - value /= item_list.size(); - } - } else { - SPShape *shape = dynamic_cast<SPShape *>(item); - if (shape) { - SPCurve * c = NULL; - SPPath *path = dynamic_cast<SPPath *>(shape); - if (path) { - c = path->get_original_curve(); - } else { - c = shape->getCurve(); - } - if (c) { - value += Geom::length(paths_to_pw(c->get_pathvector()))/(c->get_segment_count () * 6); - } - } - } -} - -void LPERoughen::doOnApply(SPLPEItem const* lpeitem) -{ - SPLPEItem * splpeitem = const_cast<SPLPEItem *>(lpeitem); - double initial = 0; - sp_get_better_default_size(SP_ITEM(splpeitem), initial); - displace_x.param_set_value(initial, 0); - displace_y.param_set_value(initial, 0); -} - void LPERoughen::doBeforeEffect(SPLPEItem const *lpeitem) { if(spray_tool_friendly && seed == 0 && SP_OBJECT(lpeitem)->getId()){ diff --git a/src/live_effects/lpe-roughen.h b/src/live_effects/lpe-roughen.h index 7e6a19d5a..44a723c89 100644 --- a/src/live_effects/lpe-roughen.h +++ b/src/live_effects/lpe-roughen.h @@ -45,7 +45,6 @@ public: virtual void doEffect(SPCurve *curve); virtual double sign(double randNumber); - virtual void doOnApply(SPLPEItem const* lpeitem); virtual Geom::Point randomize(double max_lenght, bool is_node = false); virtual void doBeforeEffect(SPLPEItem const * lpeitem); virtual SPCurve const * addNodesAndJitter(Geom::Curve const * A, Geom::Point &prev, Geom::Point &last_move, double t, bool last); diff --git a/src/live_effects/lpe-simplify.cpp b/src/live_effects/lpe-simplify.cpp index 2d79d5b34..a919756df 100644 --- a/src/live_effects/lpe-simplify.cpp +++ b/src/live_effects/lpe-simplify.cpp @@ -28,8 +28,8 @@ namespace LivePathEffect { LPESimplify::LPESimplify(LivePathEffectObject *lpeobject) : Effect(lpeobject), steps(_("Steps:"),_("Change number of simplify steps "), "steps", &wr, this,1), - threshold(_("Roughly threshold:"), _("Roughly threshold:"), "threshold", &wr, this, 0.003), - smooth_angles(_("Smooth angles:"), _("Max degree difference on handles to perform a smooth"), "smooth_angles", &wr, this, 20.), + threshold(_("Roughly threshold:"), _("Roughly threshold:"), "threshold", &wr, this, 0.002), + smooth_angles(_("Smooth angles:"), _("Max degree difference on handles to perform a smooth"), "smooth_angles", &wr, this, 0.), helper_size(_("Helper size:"), _("Helper size"), "helper_size", &wr, this, 5), simplify_individual_paths(_("Paths separately"), _("Simplifying paths (separately)"), "simplify_individual_paths", &wr, this, false, "", INKSCAPE_ICON("on"), INKSCAPE_ICON("off")), @@ -51,7 +51,7 @@ LPESimplify::LPESimplify(LivePathEffectObject *lpeobject) steps.param_set_increments(1, 1); steps.param_set_digits(0); - smooth_angles.param_set_range(0.0, 365.0); + smooth_angles.param_set_range(0.0, 360.0); smooth_angles.param_set_increments(10, 10); smooth_angles.param_set_digits(2); @@ -138,6 +138,7 @@ LPESimplify::doEffect(SPCurve *curve) if(simplify_individual_paths) { size = Geom::L2(Geom::bounds_fast(original_pathv)->dimensions()); } + size /= sp_lpe_item->i2doc_affine().descrim(); for (int unsigned i = 0; i < steps; i++) { if ( simplify_just_coalesce ) { pathliv->Coalesce(threshold * size); @@ -198,13 +199,15 @@ LPESimplify::generateHelperPathAndSmooth(Geom::PathVector &result) Geom::Point point_at2 = curve_it1->finalPoint(); Geom::Point point_at3 = curve_it1->finalPoint(); Geom::Point point_at4 = curve_it1->finalPoint(); + + if(start == Geom::Point(0,0)) { + start = point_at1; + } + if (cubic) { point_at1 = (*cubic)[1]; point_at2 = (*cubic)[2]; } - if(start == Geom::Point(0,0)) { - start = point_at1; - } if(path_it->closed() && curve_it2 == curve_endit) { point_at4 = start; @@ -219,11 +222,11 @@ LPESimplify::generateHelperPathAndSmooth(Geom::PathVector &result) Geom::Ray ray2(point_at3, point_at4); double angle1 = Geom::rad_to_deg(ray1.angle()); double angle2 = Geom::rad_to_deg(ray2.angle()); - if((smooth_angles >= angle2 - angle1) && !are_near(point_at4,point_at3) && !are_near(point_at2,point_at3)) { + if((smooth_angles >= std::abs(angle2 - angle1)) && !are_near(point_at4,point_at3) && !are_near(point_at2,point_at3)) { double dist = Geom::distance(point_at2,point_at3); Geom::Angle angleFixed = ray2.angle(); angleFixed -= Geom::Angle::from_degrees(180.0); - point_at2 = Geom::Point::polar(angleFixed,dist) + point_at3; + point_at2 = Geom::Point::polar(angleFixed, dist) + point_at3; } nCurve->curveto(point_at1, point_at2, curve_it1->finalPoint()); cubic = dynamic_cast<Geom::CubicBezier const *>(nCurve->last_segment()); diff --git a/src/main.cpp b/src/main.cpp index 5393ddc6f..840643a90 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1045,6 +1045,10 @@ sp_main_gui(int argc, char const **argv) gchar *usericondir = Inkscape::Application::profile_path("icons"); gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), usericondir); gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_PIXMAPDIR); +#ifdef INKSCAPE_THEMEDIR + gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_THEMEDIR); + gtk_icon_theme_rescan_if_needed (gtk_icon_theme_get_default()); +#endif g_free(usericondir); gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL); diff --git a/src/menus-skeleton.h b/src/menus-skeleton.h index f5e815bf6..da78f99f1 100644 --- a/src/menus-skeleton.h +++ b/src/menus-skeleton.h @@ -1,7 +1,9 @@ #ifndef SEEN_MENUS_SKELETON_H #define SEEN_MENUS_SKELETON_H -#include "config.h" +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #ifdef __cplusplus #undef N_ @@ -221,7 +223,11 @@ static char const menus_skeleton[] = " <submenu name=\"" N_("_Path") "\">\n" " <verb verb-id=\"ObjectToPath\" />\n" " <verb verb-id=\"StrokeToPath\" />\n" + +#if HAVE_POTRACE " <verb verb-id=\"SelectionTrace\" />\n" +#endif + " <verb verb-id=\"SelectionPixelArt\" />\n" " <separator/>\n" " <verb verb-id=\"SelectionUnion\" />\n" @@ -284,7 +290,9 @@ static char const menus_skeleton[] = " <verb verb-id=\"TutorialsBasic\" />\n" " <verb verb-id=\"TutorialsShapes\" />\n" " <verb verb-id=\"TutorialsAdvanced\" />\n" +#if HAVE_POTRACE " <verb verb-id=\"TutorialsTracing\" />\n" +#endif " <verb verb-id=\"TutorialsTracingPixelArt\" />\n" " <verb verb-id=\"TutorialsCalligraphy\" />\n" " <verb verb-id=\"TutorialsInterpolate\" />\n" diff --git a/src/path-prefix.h b/src/path-prefix.h index 7042d5124..7f9bcec51 100644 --- a/src/path-prefix.h +++ b/src/path-prefix.h @@ -35,6 +35,7 @@ # define INKSCAPE_PATTERNSDIR BR_DATADIR( "/inkscape/patterns" ) # define INKSCAPE_SCREENSDIR BR_DATADIR( "/inkscape/screens" ) # define INKSCAPE_SYMBOLSDIR BR_DATADIR( "/inkscape/symbols" ) +# define INKSCAPE_THEMEDIR BR_DATADIR( "/icons" ) # define INKSCAPE_TUTORIALSDIR BR_DATADIR( "/inkscape/tutorials" ) # define INKSCAPE_TEMPLATESDIR BR_DATADIR( "/inkscape/templates" ) # define INKSCAPE_UIDIR BR_DATADIR( "/inkscape/ui" ) @@ -102,6 +103,7 @@ # define INKSCAPE_PATTERNSDIR INKSCAPE_DATADIR "/inkscape/patterns" # define INKSCAPE_SCREENSDIR INKSCAPE_DATADIR "/inkscape/screens" # define INKSCAPE_SYMBOLSDIR INKSCAPE_DATADIR "/inkscape/symbols" +# define INKSCAPE_THEMEDIR INKSCAPE_DATADIR "/icons" # define INKSCAPE_TUTORIALSDIR INKSCAPE_DATADIR "/inkscape/tutorials" # define INKSCAPE_TEMPLATESDIR INKSCAPE_DATADIR "/inkscape/templates" # define INKSCAPE_UIDIR INKSCAPE_DATADIR "/inkscape/ui" diff --git a/src/shortcuts.cpp b/src/shortcuts.cpp index e72c16de0..194d4d2a4 100644 --- a/src/shortcuts.cpp +++ b/src/shortcuts.cpp @@ -575,7 +575,14 @@ static void read_shortcuts_file(char const *filename, bool const is_user_set) { } Inkscape::Verb *verb=Inkscape::Verb::getbyid(verb_name); - if (!verb) { + if (!verb +#if !HAVE_POTRACE + // Squash warning about disabled features + && strcmp(verb_name, "ToolPaintBucket") != 0 + && strcmp(verb_name, "SelectionTrace") != 0 + && strcmp(verb_name, "PaintBucketPrefs") != 0 +#endif + ) { g_warning("Unknown verb name: %s", verb_name); continue; } diff --git a/src/sp-mesh-array.cpp b/src/sp-mesh-array.cpp index d76b884ae..355150893 100644 --- a/src/sp-mesh-array.cpp +++ b/src/sp-mesh-array.cpp @@ -2330,6 +2330,7 @@ guint SPMeshNodeArray::color_pick( std::vector<guint> icorners, SPItem* item ) { pick_doc->getRoot()->invoke_hide(pick_visionkey); delete pick_drawing; + picked = 1; // Picking always happens if( picked > 0 ) built = false; return picked; } diff --git a/src/sp-rect.cpp b/src/sp-rect.cpp index e17d7373c..2ba9a7023 100644 --- a/src/sp-rect.cpp +++ b/src/sp-rect.cpp @@ -61,13 +61,13 @@ void SPRect::set(unsigned key, gchar const *value) { switch (key) { case SP_ATTR_X: this->x.readOrUnset(value); - this->x.update( ex, em, w ); + this->x.update( em, ex, w ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_Y: this->y.readOrUnset(value); - this->y.update( ex, em, h ); + this->y.update( em, ex, h ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; @@ -75,7 +75,7 @@ void SPRect::set(unsigned key, gchar const *value) { if (!this->width.read(value) || this->width.value < 0.0) { this->width.unset(); } - this->width.update( ex, em, w ); + this->width.update( em, ex, w ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; @@ -83,7 +83,7 @@ void SPRect::set(unsigned key, gchar const *value) { if (!this->height.read(value) || this->height.value < 0.0) { this->height.unset(); } - this->height.update( ex, em, h ); + this->height.update( em, ex, h ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; @@ -91,7 +91,7 @@ void SPRect::set(unsigned key, gchar const *value) { if (!this->rx.read(value) || this->rx.value <= 0.0) { this->rx.unset(); } - this->rx.update( ex, em, w ); + this->rx.update( em, ex, w ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; @@ -99,7 +99,7 @@ void SPRect::set(unsigned key, gchar const *value) { if (!this->ry.read(value) || this->ry.value <= 0.0) { this->ry.unset(); } - this->ry.update( ex, em, h ); + this->ry.update( em, ex, h ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; diff --git a/src/sp-text.cpp b/src/sp-text.cpp index d351e58e3..c7dfdd694 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -93,7 +93,7 @@ void SPText::release() { void SPText::set(unsigned int key, const gchar* value) { //std::cout << "SPText::set: " << sp_attribute_name( key ) << ": " << (value?value:"Null") << std::endl; - if (this->attributes.readSingleAttribute(key, value)) { + if (this->attributes.readSingleAttribute(key, value, style, &viewport)) { this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } else { switch (key) { @@ -181,6 +181,16 @@ void SPText::update(SPCtx *ctx, guint flags) { SP_OBJECT_CHILD_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG ) ) { + + SPItemCtx const *ictx = reinterpret_cast<SPItemCtx const *>(ctx); + + double const w = ictx->viewport.width(); + double const h = ictx->viewport.height(); + double const em = style->font_size.computed; + double const ex = 0.5 * em; // fixme: get x height from pango or libnrtype. + + attributes.update( em, ex, w, h ); + /* fixme: It is not nice to have it here, but otherwise children content changes does not work */ /* fixme: Even now it may not work, as we are delayed */ /* fixme: So check modification flag everywhere immediate state is used */ @@ -576,7 +586,7 @@ unsigned SPText::_buildLayoutInput(SPObject *root, Inkscape::Text::Layout::Optio SPString *str = dynamic_cast<SPString *>(child); if (str) { Glib::ustring const &string = str->string; - // std::cout << " Appending: " << string << std::endl; + // std::cout << " Appending: >" << string << "<" << std::endl; layout.appendText(string, root->style, child, &optional_attrs, child_attrs_offset + length); length += string.length(); } else if (!sp_repr_is_meta_element(child->getRepr())) { @@ -671,25 +681,30 @@ void SPText::_clearFlow(Inkscape::DrawingGroup *in_arena) * TextTagAttributes implementation */ -void TextTagAttributes::readFrom(Inkscape::XML::Node const *node) -{ - readSingleAttribute(SP_ATTR_X, node->attribute("x")); - readSingleAttribute(SP_ATTR_Y, node->attribute("y")); - readSingleAttribute(SP_ATTR_DX, node->attribute("dx")); - readSingleAttribute(SP_ATTR_DY, node->attribute("dy")); - readSingleAttribute(SP_ATTR_ROTATE, node->attribute("rotate")); - readSingleAttribute(SP_ATTR_TEXTLENGTH, node->attribute("textLength")); - readSingleAttribute(SP_ATTR_LENGTHADJUST, node->attribute("lengthAdjust")); -} - -bool TextTagAttributes::readSingleAttribute(unsigned key, gchar const *value) +// Not used. +// void TextTagAttributes::readFrom(Inkscape::XML::Node const *node) +// { +// readSingleAttribute(SP_ATTR_X, node->attribute("x")); +// readSingleAttribute(SP_ATTR_Y, node->attribute("y")); +// readSingleAttribute(SP_ATTR_DX, node->attribute("dx")); +// readSingleAttribute(SP_ATTR_DY, node->attribute("dy")); +// readSingleAttribute(SP_ATTR_ROTATE, node->attribute("rotate")); +// readSingleAttribute(SP_ATTR_TEXTLENGTH, node->attribute("textLength")); +// readSingleAttribute(SP_ATTR_LENGTHADJUST, node->attribute("lengthAdjust")); +// } + +bool TextTagAttributes::readSingleAttribute(unsigned key, gchar const *value, SPStyle const *style, Geom::Rect const *viewport) { + // std::cout << "TextTagAttributes::readSingleAttribute: key: " << key + // << " value: " << (value?value:"Null") << std::endl; std::vector<SVGLength> *attr_vector; + bool update_x = false; + bool update_y = false; switch (key) { - case SP_ATTR_X: attr_vector = &attributes.x; break; - case SP_ATTR_Y: attr_vector = &attributes.y; break; - case SP_ATTR_DX: attr_vector = &attributes.dx; break; - case SP_ATTR_DY: attr_vector = &attributes.dy; break; + case SP_ATTR_X: attr_vector = &attributes.x; update_x = true; break; + case SP_ATTR_Y: attr_vector = &attributes.y; update_y = true; break; + case SP_ATTR_DX: attr_vector = &attributes.dx; update_x = true; break; + case SP_ATTR_DY: attr_vector = &attributes.dy; update_y = true; break; case SP_ATTR_ROTATE: attr_vector = &attributes.rotate; break; case SP_ATTR_TEXTLENGTH: attributes.textLength.readOrUnset(value); @@ -706,6 +721,19 @@ bool TextTagAttributes::readSingleAttribute(unsigned key, gchar const *value) // FIXME: sp_svg_length_list_read() amalgamates repeated separators. This prevents unset values. *attr_vector = sp_svg_length_list_read(value); + + if( (update_x || update_y) && style != NULL && viewport != NULL ) { + double const w = viewport->width(); + double const h = viewport->height(); + double const em = style->font_size.computed; + double const ex = em * 0.5; + for(std::vector<SVGLength>::iterator it = attr_vector->begin(); it != attr_vector->end(); ++it) { + if( update_x ) + it->update( em, ex, w ); + if( update_y ) + it->update( em, ex, h ); + } + } return true; } @@ -728,12 +756,26 @@ void TextTagAttributes::writeTo(Inkscape::XML::Node *node) const } } +void TextTagAttributes::update( double em, double ex, double w, double h ) +{ + for(std::vector<SVGLength>::iterator it = attributes.x.begin(); it != attributes.x.end(); ++it) { + it->update( em, ex, w ); + } + for(std::vector<SVGLength>::iterator it = attributes.y.begin(); it != attributes.y.end(); ++it) { + it->update( em, ex, h ); + } + for(std::vector<SVGLength>::iterator it = attributes.dx.begin(); it != attributes.dx.end(); ++it) { + it->update( em, ex, w ); + } + for(std::vector<SVGLength>::iterator it = attributes.dy.begin(); it != attributes.dy.end(); ++it) { + it->update( em, ex, h ); + } +} + void TextTagAttributes::writeSingleAttributeLength(Inkscape::XML::Node *node, gchar const *key, const SVGLength &length) { if (length._set) { - gchar single_value_string[32]; - g_ascii_formatd(single_value_string, sizeof (single_value_string), "%.8g", length.computed); - node->setAttribute(key, single_value_string); + node->setAttribute(key, length.write().c_str()); } else node->setAttribute(key, NULL); } @@ -744,13 +786,11 @@ void TextTagAttributes::writeSingleAttributeVector(Inkscape::XML::Node *node, gc node->setAttribute(key, NULL); else { Glib::ustring string; - gchar single_value_string[32]; // FIXME: this has no concept of unset values because sp_svg_length_list_read() can't read them back in for (std::vector<SVGLength>::const_iterator it = attr_vector.begin() ; it != attr_vector.end() ; ++it) { - g_ascii_formatd(single_value_string, sizeof (single_value_string), "%.8g", it->computed); if (!string.empty()) string += ' '; - string += single_value_string; + string += it->write(); } node->setAttribute(key, string.c_str()); } diff --git a/src/sp-tref.cpp b/src/sp-tref.cpp index aef18462a..ba592058b 100644 --- a/src/sp-tref.cpp +++ b/src/sp-tref.cpp @@ -92,7 +92,7 @@ void SPTRef::set(unsigned int key, const gchar* value) { debug("0x%p %s(%u): '%s'",this, sp_attribute_name(key),key,value ? value : "<no value>"); - if (this->attributes.readSingleAttribute(key, value)) { // x, y, dx, dy, rotate + if (this->attributes.readSingleAttribute(key, value, style, &viewport)) { // x, y, dx, dy, rotate this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } else if (key == SP_ATTR_XLINK_HREF) { // xlink:href if ( !value ) { diff --git a/src/sp-tspan.cpp b/src/sp-tspan.cpp index 7582cb9e6..05f8430ba 100644 --- a/src/sp-tspan.cpp +++ b/src/sp-tspan.cpp @@ -70,7 +70,7 @@ void SPTSpan::release() { } void SPTSpan::set(unsigned int key, const gchar* value) { - if (this->attributes.readSingleAttribute(key, value)) { + if (this->attributes.readSingleAttribute(key, value, style, &viewport)) { this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } else { switch (key) { @@ -103,6 +103,20 @@ void SPTSpan::update(SPCtx *ctx, guint flags) { } SPItem::update(ctx, flags); + + if (flags & ( SP_OBJECT_STYLE_MODIFIED_FLAG | + SP_OBJECT_CHILD_MODIFIED_FLAG | + SP_TEXT_LAYOUT_MODIFIED_FLAG ) ) + { + SPItemCtx const *ictx = reinterpret_cast<SPItemCtx const *>(ctx); + + double const w = ictx->viewport.width(); + double const h = ictx->viewport.height(); + double const em = style->font_size.computed; + double const ex = 0.5 * em; // fixme: get x height from pango or libnrtype. + + attributes.update( em, ex, w, h ); + } } void SPTSpan::modified(unsigned int flags) { @@ -264,7 +278,7 @@ void SPTextPath::release() { } void SPTextPath::set(unsigned int key, const gchar* value) { - if (this->attributes.readSingleAttribute(key, value)) { + if (this->attributes.readSingleAttribute(key, value, style, &viewport)) { this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } else { switch (key) { @@ -304,6 +318,20 @@ void SPTextPath::update(SPCtx *ctx, guint flags) { ochild->updateDisplay(ctx, flags); } } + + if (flags & ( SP_OBJECT_STYLE_MODIFIED_FLAG | + SP_OBJECT_CHILD_MODIFIED_FLAG | + SP_TEXT_LAYOUT_MODIFIED_FLAG ) ) + { + SPItemCtx const *ictx = reinterpret_cast<SPItemCtx const *>(ctx); + + double const w = ictx->viewport.width(); + double const h = ictx->viewport.height(); + double const em = style->font_size.computed; + double const ex = 0.5 * em; // fixme: get x height from pango or libnrtype. + + attributes.update( em, ex, w, h ); + } } diff --git a/src/style-enums.h b/src/style-enums.h index 03255b3b1..06207852c 100644 --- a/src/style-enums.h +++ b/src/style-enums.h @@ -174,10 +174,7 @@ enum SPCSSWritingMode { enum SPCSSTextOrientation { SP_CSS_TEXT_ORIENTATION_MIXED, SP_CSS_TEXT_ORIENTATION_UPRIGHT, - SP_CSS_TEXT_ORIENTATION_SIDEWAYS_RIGHT, - SP_CSS_TEXT_ORIENTATION_SIDEWAYS_LEFT, - SP_CSS_TEXT_ORIENTATION_SIDEWAYS, - SP_CSS_TEXT_ORIENTATION_USE_GLYPH_ORIENTATION + SP_CSS_TEXT_ORIENTATION_SIDEWAYS }; enum SPTextAnchor { @@ -194,10 +191,24 @@ enum SPWhiteSpace { SP_CSS_WHITE_SPACE_PRELINE }; +// Not complete list +enum SPCSSBaseline { + SP_CSS_BASELINE_AUTO, + SP_CSS_BASELINE_ALPHABETIC, + SP_CSS_BASELINE_IDEOGRAPHIC, + SP_CSS_BASELINE_HANGING, + SP_CSS_BASELINE_MATHEMATICAL, + SP_CSS_BASELINE_CENTRAL, + SP_CSS_BASELINE_MIDDLE, + SP_CSS_BASELINE_TEXT_BEFORE_EDGE, + SP_CSS_BASELINE_TEXT_AFTER_EDGE, + SP_CSS_BASELINE_SIZE // Size of enum, keep last. +}; + enum SPCSSBaselineShift { - SP_CSS_BASELINE_SHIFT_BASELINE, - SP_CSS_BASELINE_SHIFT_SUB, - SP_CSS_BASELINE_SHIFT_SUPER + SP_CSS_BASELINE_SHIFT_BASELINE, + SP_CSS_BASELINE_SHIFT_SUB, + SP_CSS_BASELINE_SHIFT_SUPER }; enum SPVisibility { @@ -519,10 +530,20 @@ static SPStyleEnum const enum_writing_mode[] = { static SPStyleEnum const enum_text_orientation[] = { {"mixed", SP_CSS_TEXT_ORIENTATION_MIXED}, // Default {"upright", SP_CSS_TEXT_ORIENTATION_UPRIGHT}, - {"sideways-right", SP_CSS_TEXT_ORIENTATION_SIDEWAYS_RIGHT}, - {"sideways-left", SP_CSS_TEXT_ORIENTATION_SIDEWAYS_LEFT}, {"sideways", SP_CSS_TEXT_ORIENTATION_SIDEWAYS}, - {"use-glyph-orientation", SP_CSS_TEXT_ORIENTATION_USE_GLYPH_ORIENTATION}, + {NULL, -1} +}; + +static SPStyleEnum const enum_baseline[] = { + {"auto", SP_CSS_BASELINE_AUTO}, // Default + {"alphabetic", SP_CSS_BASELINE_ALPHABETIC}, + {"ideographic", SP_CSS_BASELINE_IDEOGRAPHIC}, + {"hanging", SP_CSS_BASELINE_HANGING}, + {"mathematical", SP_CSS_BASELINE_MATHEMATICAL}, + {"central", SP_CSS_BASELINE_CENTRAL}, + {"middle", SP_CSS_BASELINE_MIDDLE}, + {"text-before-edge", SP_CSS_BASELINE_TEXT_BEFORE_EDGE}, + {"text-after-edge", SP_CSS_BASELINE_TEXT_AFTER_EDGE}, {NULL, -1} }; diff --git a/src/style-internal.h b/src/style-internal.h index 1ddab30f1..889292454 100644 --- a/src/style-internal.h +++ b/src/style-internal.h @@ -1260,7 +1260,6 @@ struct SPITextDecorationData { float tspan_width; // from libnrtype, when it calculates spans float ascender; // the rest from tspan's font float descender; - float line_gap; float underline_thickness; float underline_position; float line_through_thickness; diff --git a/src/style.cpp b/src/style.cpp index 369127792..2a216e940 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -113,7 +113,7 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : font_weight( "font-weight", enum_font_weight, SP_CSS_FONT_WEIGHT_NORMAL, SP_CSS_FONT_WEIGHT_400 ), font_stretch( "font-stretch", enum_font_stretch, SP_CSS_FONT_STRETCH_NORMAL ), font_size(), - line_height( "line-height", 1.0 ), // SPILengthOrNormal + line_height( "line-height", 1.25 ), // SPILengthOrNormal font_family( "font-family", "sans-serif" ), // SPIString w/default font(), // SPIFont font_specification( "-inkscape-font-specification" ), // SPIString @@ -138,6 +138,7 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : direction( "direction", enum_direction, SP_CSS_DIRECTION_LTR ), writing_mode( "writing-mode", enum_writing_mode, SP_CSS_WRITING_MODE_LR_TB ), text_orientation( "text-orientation",enum_text_orientation,SP_CSS_TEXT_ORIENTATION_MIXED ), + dominant_baseline("dominant-baseline",enum_baseline, SP_CSS_BASELINE_AUTO ), baseline_shift(), text_anchor( "text-anchor", enum_text_anchor, SP_CSS_TEXT_ANCHOR_START ), white_space( "white-space", enum_white_space, SP_CSS_WHITE_SPACE_NORMAL ), @@ -321,6 +322,7 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : _properties.push_back( &writing_mode ); _properties.push_back( &direction ); _properties.push_back( &text_orientation ); + _properties.push_back( &dominant_baseline ); _properties.push_back( &baseline_shift ); _properties.push_back( &text_anchor ); _properties.push_back( &white_space ); @@ -415,6 +417,7 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : // _propmap.insert( std::make_pair( direction.name, reinterpret_cast<SPIBasePtr>(&SPStyle::direction ) ) ); // _propmap.insert( std::make_pair( writing_mode.name, reinterpret_cast<SPIBasePtr>(&SPStyle::writing_mode ) ) ); // _propmap.insert( std::make_pair( text_orientation.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_orientation ) ) ); + // _propmap.insert( std::make_pair( dominant_baseline.name, reinterpret_cast<SPIBasePtr>(&SPStyle::dominant_baseline ) ) ); // _propmap.insert( std::make_pair( baseline_shift.name, reinterpret_cast<SPIBasePtr>(&SPStyle::baseline_shift ) ) ); // _propmap.insert( std::make_pair( text_anchor.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_anchor ) ) ); // _propmap.insert( std::make_pair( white_space.name, reinterpret_cast<SPIBasePtr>(&SPStyle::white_space ) ) ); @@ -796,6 +799,9 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { case SP_PROP_SHAPE_PADDING: shape_padding.readIfUnset( val ); break; + case SP_PROP_DOMINANT_BASELINE: + dominant_baseline.readIfUnset( val ); + break; case SP_PROP_BASELINE_SHIFT: baseline_shift.readIfUnset( val ); break; @@ -805,9 +811,6 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { case SP_PROP_ALIGNMENT_BASELINE: g_warning("Unimplemented style property SP_PROP_ALIGNMENT_BASELINE: value: %s", val); break; - case SP_PROP_DOMINANT_BASELINE: - g_warning("Unimplemented style property SP_PROP_DOMINANT_BASELINE: value: %s", val); - break; case SP_PROP_GLYPH_ORIENTATION_HORIZONTAL: g_warning("Unimplemented style property SP_PROP_ORIENTATION_HORIZONTAL: value: %s", val); break; diff --git a/src/style.h b/src/style.h index 3948b876c..0e8e34145 100644 --- a/src/style.h +++ b/src/style.h @@ -148,6 +148,8 @@ public: SPIEnum writing_mode; /** Text orientation (CSS Writing Modes 3) */ SPIEnum text_orientation; + /** Dominant baseline (svg1.1) */ + SPIEnum dominant_baseline; /** Baseline shift (svg1.1 10.9.2) */ SPIBaselineShift baseline_shift; diff --git a/src/svg/svg-length.cpp b/src/svg/svg-length.cpp index edbc59c36..cd995582d 100644 --- a/src/svg/svg-length.cpp +++ b/src/svg/svg-length.cpp @@ -463,7 +463,7 @@ unsigned int sp_svg_length_read_ldd(gchar const *str, SVGLength::Unit *unit, dou return r; } -std::string const SVGLength::write() +std::string SVGLength::write() const { return sp_svg_length_write_with_units(*this); } diff --git a/src/svg/svg-length.h b/src/svg/svg-length.h index 84056dd5f..2aaf248b1 100644 --- a/src/svg/svg-length.h +++ b/src/svg/svg-length.h @@ -57,7 +57,7 @@ public: bool read(char const *str); void readOrUnset(char const *str, Unit u = NONE, float v = 0, float c = 0); bool readAbsolute(char const *str); - std::string const write(); + std::string write() const; // To set 'v' use '=' void set(Unit u, float v); // Sets computed value based on u and v. void set(Unit u, float v, float c); // Sets all three values. diff --git a/src/text-tag-attributes.h b/src/text-tag-attributes.h index 7a389ed39..2cf7a8bde 100644 --- a/src/text-tag-attributes.h +++ b/src/text-tag-attributes.h @@ -31,11 +31,14 @@ public: /** Process the parameters from the set() function of SPObject. Returns true if \a key was a recognised attribute. */ - bool readSingleAttribute(unsigned key, gchar const *value); + bool readSingleAttribute(unsigned key, gchar const *value, SPStyle const *style, Geom::Rect const *viewport); /// Write out all the contents of #attributes to the given node. void writeTo(Inkscape::XML::Node *node) const; + /// Update relative values + void update( double em, double ex, double w, double h ); + /** For tspan role=line elements we should not use the set x,y coordinates since that would overrule the values calculated by the text layout engine, however if there are more than one element in diff --git a/src/trace/CMakeLists.txt b/src/trace/CMakeLists.txt index bf7cfa276..12cf495f6 100644 --- a/src/trace/CMakeLists.txt +++ b/src/trace/CMakeLists.txt @@ -1,3 +1,4 @@ +if (${HAVE_POTRACE}) set(trace_SRC filterset.cpp @@ -7,14 +8,7 @@ set(trace_SRC siox.cpp trace.cpp - potrace/curve.cpp - potrace/decompose.cpp - potrace/greymap.cpp potrace/inkscape-potrace.cpp - potrace/potracelib.cpp - potrace/render.cpp - potrace/trace.cpp - # ------- # Headers @@ -26,19 +20,11 @@ set(trace_SRC siox.h trace.h - potrace/auxiliary.h potrace/bitmap.h - potrace/bitops.h - potrace/curve.h - potrace/decompose.h - potrace/greymap.h potrace/inkscape-potrace.h - potrace/lists.h - potrace/potracelib.h - potrace/progress.h - potrace/render.h - potrace/trace.h ) # add_inkscape_lib(trace_LIB "${trace_SRC}") add_inkscape_source("${trace_SRC}") + +endif() diff --git a/src/trace/Makefile_insert b/src/trace/Makefile_insert index 21ec4ac0c..27353df15 100644 --- a/src/trace/Makefile_insert +++ b/src/trace/Makefile_insert @@ -1,5 +1,7 @@ ## Makefile.am fragment sourced by src/Makefile.am. +if HAVE_POTRACE + ink_common_sources += \ trace/pool.h \ trace/trace.h \ @@ -14,21 +16,8 @@ ink_common_sources += \ trace/filterset.cpp \ trace/siox.h \ trace/siox.cpp \ - trace/potrace/auxiliary.h \ - trace/potrace/bitmap.h \ - trace/potrace/curve.cpp \ - trace/potrace/curve.h \ - trace/potrace/decompose.cpp \ - trace/potrace/decompose.h \ - trace/potrace/greymap.cpp \ - trace/potrace/greymap.h \ - trace/potrace/lists.h \ - trace/potrace/potracelib.cpp \ - trace/potrace/potracelib.h \ - trace/potrace/progress.h \ - trace/potrace/render.cpp \ - trace/potrace/render.h \ - trace/potrace/trace.cpp \ - trace/potrace/trace.h \ + trace/potrace/bitmap.h \ trace/potrace/inkscape-potrace.cpp \ trace/potrace/inkscape-potrace.h + +endif diff --git a/src/trace/potrace/auxiliary.h b/src/trace/potrace/auxiliary.h deleted file mode 100644 index dbf124d7c..000000000 --- a/src/trace/potrace/auxiliary.h +++ /dev/null @@ -1,80 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -/* This header file collects some general-purpose macros (and static - inline functions) that are used in various places. */ - -#ifndef AUXILIARY_H -#define AUXILIARY_H - -#include <stdlib.h> - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -/* ---------------------------------------------------------------------- */ -/* point arithmetic */ - -#include "potracelib.h" - -struct point_s { - long x; - long y; -}; -typedef struct point_s point_t; - -typedef potrace_dpoint_t dpoint_t; - -/* convert point_t to dpoint_t */ -static inline dpoint_t dpoint(point_t p) { - dpoint_t res; - res.x = p.x; - res.y = p.y; - return res; -} - -/* range over the straight line segment [a,b] when lambda ranges over [0,1] */ -static inline dpoint_t interval(double lambda, dpoint_t a, dpoint_t b) { - dpoint_t res; - - res.x = a.x + lambda * (b.x - a.x); - res.y = a.y + lambda * (b.y - a.y); - return res; -} - -/* ---------------------------------------------------------------------- */ -/* some useful macros. Note: the "mod" macro works correctly for - negative a. Also note that the test for a>=n, while redundant, - speeds up the mod function by 70% in the average case (significant - since the program spends about 16% of its time here - or 40% - without the test). The "floordiv" macro returns the largest integer - <= a/n, and again this works correctly for negative a, as long as - a,n are integers and n>0. */ - -/* integer arithmetic */ - -static inline int mod(int a, int n) { - return a>=n ? a%n : a>=0 ? a : n-1-(-1-a)%n; -} - -static inline int floordiv(int a, int n) { - return a>=0 ? a/n : -1-(-1-a)/n; -} - -/* Note: the following work for integers and other numeric types. */ -#undef sign -#undef abs -#undef min -#undef max -#undef sq -#undef cu -#define sign(x) ((x)>0 ? 1 : (x)<0 ? -1 : 0) -#define abs(a) ((a)>0 ? (a) : -(a)) -#define min(a,b) ((a)<(b) ? (a) : (b)) -#define max(a,b) ((a)>(b) ? (a) : (b)) -#define sq(a) ((a)*(a)) -#define cu(a) ((a)*(a)*(a)) - -#endif /* AUXILIARY_H */ diff --git a/src/trace/potrace/bitmap.h b/src/trace/potrace/bitmap.h index 086bbb046..7b5a94bb1 100644 --- a/src/trace/potrace/bitmap.h +++ b/src/trace/potrace/bitmap.h @@ -8,6 +8,7 @@ #include <string.h> #include <stdlib.h> #include <errno.h> +#include <stddef.h> /* The bitmap type is defined in potracelib.h */ #include "potracelib.h" @@ -28,7 +29,7 @@ /* macros for accessing pixel at index (x,y). U* macros omit the bounds check. */ -#define bm_scanline(bm, y) ((bm)->map + (ssize_t)(y)*(ssize_t)(bm)->dy) +#define bm_scanline(bm, y) ((bm)->map + (ptrdiff_t)(y)*(ptrdiff_t)(bm)->dy) #define bm_index(bm, x, y) (&bm_scanline(bm, y)[(x)/BM_WORDBITS]) #define bm_mask(x) (BM_HIBIT >> ((x) & (BM_WORDBITS-1))) #define bm_range(x, a) ((int)(x) >= 0 && (int)(x) < (a)) @@ -57,10 +58,10 @@ static inline void bm_free(potrace_bitmap_t *bm) { static inline potrace_bitmap_t *bm_new(int w, int h) { potrace_bitmap_t *bm; int dy = w == 0 ? 0 : (w - 1) / BM_WORDBITS + 1; - ssize_t size = (ssize_t)dy * (ssize_t)h * (ssize_t)BM_WORDSIZE; + ptrdiff_t size = (ptrdiff_t)dy * (ptrdiff_t)h * (ptrdiff_t)BM_WORDSIZE; /* check for overflow error */ - if (size < 0 || size / h / dy != BM_WORDSIZE) { + if (size < 0 || (h != 0 && dy != 0 && size / h / dy != BM_WORDSIZE)) { errno = ENOMEM; return NULL; } @@ -83,15 +84,15 @@ static inline potrace_bitmap_t *bm_new(int w, int h) { /* clear the given bitmap. Set all bits to c. */ static inline void bm_clear(potrace_bitmap_t *bm, int c) { /* Note: if the bitmap was created with bm_new, then it is - guaranteed that size will fit into the ssize_t type. */ - ssize_t size = (ssize_t)bm->dy * (ssize_t)bm->h * (ssize_t)BM_WORDSIZE; + guaranteed that size will fit into the ptrdiff_t type. */ + ptrdiff_t size = (ptrdiff_t)bm->dy * (ptrdiff_t)bm->h * (ptrdiff_t)BM_WORDSIZE; memset(bm->map, c ? -1 : 0, size); } /* duplicate the given bitmap. Return NULL on error with errno set. */ static inline potrace_bitmap_t *bm_dup(const potrace_bitmap_t *bm) { potrace_bitmap_t *bm1 = bm_new(bm->w, bm->h); - ssize_t size = (ssize_t)bm->dy * (ssize_t)bm->h * (ssize_t)BM_WORDSIZE; + ptrdiff_t size = (ptrdiff_t)bm->dy * (ptrdiff_t)bm->h * (ptrdiff_t)BM_WORDSIZE; if (!bm1) { return NULL; } @@ -101,8 +102,8 @@ static inline potrace_bitmap_t *bm_dup(const potrace_bitmap_t *bm) { /* invert the given bitmap. */ static inline void bm_invert(potrace_bitmap_t *bm) { - ssize_t i; - ssize_t size = (ssize_t)bm->dy * (ssize_t)bm->h; + ptrdiff_t i; + ptrdiff_t size = (ptrdiff_t)bm->dy * (ptrdiff_t)bm->h; for (i = 0; i < size; i++) { bm->map[i] ^= BM_ALLBITS; diff --git a/src/trace/potrace/bitops.h b/src/trace/potrace/bitops.h deleted file mode 100644 index cff734a46..000000000 --- a/src/trace/potrace/bitops.h +++ /dev/null @@ -1,83 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - - -/* bits.h: this file defines some macros for bit manipulations. We - provide a generic implementation, as well as machine- and - compiler-specific fast implementations */ - -/* lobit: return the position of the rightmost "1" bit of an int, or - 32 if none. hibit: return 1 + the position of the leftmost "1" bit - of an int, or 0 if none. Note: these functions work on 32-bit - integers. */ - -#ifndef BITOPS_H -#define BITOPS_H - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -/* ---------------------------------------------------------------------- */ -/* machine specific macros */ - -#if defined(HAVE_I386) - -static inline unsigned int lobit(unsigned int x) { - unsigned int res; - asm ("bsf %1,%0\n\t" - "jnz 0f\n\t" - "movl $32,%0\n" - "0:" - : "=r" (res) - : "r" (x) - : "cc"); - return res; -} - -static inline unsigned int hibit(unsigned int x) { - unsigned int res; - - asm ("bsr %1,%0\n\t" - "jnz 0f\n\t" - "movl $-1,%0\n" - "0:" - : "=r" (res) - : "r" (x) - : "cc"); - return res+1; -} - -/* ---------------------------------------------------------------------- */ -#else /* generic macros */ - -static inline unsigned int lobit(unsigned int x) { - unsigned int res = 32; - while (x & 0xffffff) { - x <<= 8; - res -= 8; - } - while (x) { - x <<= 1; - res -= 1; - } - return res; -} - -static inline unsigned int hibit(unsigned int x) { - unsigned int res = 0; - while (x > 0xff) { - x >>= 8; - res += 8; - } - while (x) { - x >>= 1; - res += 1; - } - return res; -} - -#endif - -#endif /* BITOPS_H */ diff --git a/src/trace/potrace/curve.cpp b/src/trace/potrace/curve.cpp deleted file mode 100644 index e6a9a4721..000000000 --- a/src/trace/potrace/curve.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -/* private part of the path and curve data structures */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "potracelib.h" -#include "lists.h" -#include "curve.h" - -#define SAFE_CALLOC(var, n, typ) \ - if ((var = (typ *)calloc(n, sizeof(typ))) == NULL) goto calloc_error - -/* ---------------------------------------------------------------------- */ -/* allocate and free path objects */ - -path_t *path_new(void) { - path_t *p = NULL; - privpath_t *priv = NULL; - - SAFE_CALLOC(p, 1, path_t); - memset(p, 0, sizeof(path_t)); - SAFE_CALLOC(priv, 1, privpath_t); - memset(priv, 0, sizeof(privpath_t)); - p->priv = priv; - return p; - - calloc_error: - free(p); - free(priv); - return NULL; -} - -/* free the members of the given curve structure. Leave errno unchanged. */ -static void privcurve_free_members(privcurve_t *curve) { - free(curve->tag); - free(curve->c); - free(curve->vertex); - free(curve->alpha); - free(curve->alpha0); - free(curve->beta); -} - -/* free a path. Leave errno untouched. */ -void path_free(path_t *p) { - if (p) { - if (p->priv) { - free(p->priv->pt); - free(p->priv->lon); - free(p->priv->sums); - free(p->priv->po); - privcurve_free_members(&p->priv->curve); - privcurve_free_members(&p->priv->ocurve); - } - free(p->priv); - /* do not free p->fcurve ! */ - } - free(p); -} - -/* free a pathlist, leaving errno untouched. */ -void pathlist_free(path_t *plist) { - path_t *p; - - list_forall_unlink(p, plist) { - path_free(p); - } -} - -/* ---------------------------------------------------------------------- */ -/* initialize and finalize curve structures */ - -typedef dpoint_t dpoint3_t[3]; - -/* initialize the members of the given curve structure to size m. - Return 0 on success, 1 on error with errno set. */ -int privcurve_init(privcurve_t *curve, int n) { - memset(curve, 0, sizeof(privcurve_t)); - curve->n = n; - SAFE_CALLOC(curve->tag, n, int); - SAFE_CALLOC(curve->c, n, dpoint3_t); - SAFE_CALLOC(curve->vertex, n, dpoint_t); - SAFE_CALLOC(curve->alpha, n, double); - SAFE_CALLOC(curve->alpha0, n, double); - SAFE_CALLOC(curve->beta, n, double); - return 0; - - calloc_error: - free(curve->tag); - free(curve->c); - free(curve->vertex); - free(curve->alpha); - free(curve->alpha0); - free(curve->beta); - return 1; -} - -/* copy private to public curve structure */ -void privcurve_to_curve(privcurve_t *pc, potrace_curve_t *c) { - c->n = pc->n; - c->tag = pc->tag; - c->c = pc->c; -} - diff --git a/src/trace/potrace/curve.h b/src/trace/potrace/curve.h deleted file mode 100644 index feb95b39b..000000000 --- a/src/trace/potrace/curve.h +++ /dev/null @@ -1,77 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -#ifndef CURVE_H -#define CURVE_H - -#include "auxiliary.h" - -/* vertex is c[1] for tag=POTRACE_CORNER, and the intersection of - .c[-1][2]..c[0] and c[1]..c[2] for tag=POTRACE_CURVETO. alpha is only - defined for tag=POTRACE_CURVETO and is the alpha parameter of the curve: - .c[-1][2]..c[0] = alpha*(.c[-1][2]..vertex), and - c[2]..c[1] = alpha*(c[2]..vertex). - Beta is so that (.beta[i])[.vertex[i],.vertex[i+1]] = .c[i][2]. -*/ - -struct privcurve_s { - int n; /* number of segments */ - int *tag; /* tag[n]: POTRACE_CORNER or POTRACE_CURVETO */ - dpoint_t (*c)[3]; /* c[n][i]: control points. - c[n][0] is unused for tag[n]=POTRACE_CORNER */ - /* the remainder of this structure is special to privcurve, and is - used in EPS debug output and special EPS "short coding". These - fields are valid only if "alphacurve" is set. */ - int alphacurve; /* have the following fields been initialized? */ - dpoint_t *vertex; /* for POTRACE_CORNER, this equals c[1] */ - double *alpha; /* only for POTRACE_CURVETO */ - double *alpha0; /* "uncropped" alpha parameter - for debug output only */ - double *beta; -}; -typedef struct privcurve_s privcurve_t; - -struct sums_s { - double x; - double y; - double x2; - double xy; - double y2; -}; -typedef struct sums_s sums_t; - -/* the path structure is filled in with information about a given path - as it is accumulated and passed through the different stages of the - Potrace algorithm. Backends only need to read the fcurve and fm - fields of this data structure, but debugging backends may read - other fields. */ -struct potrace_privpath_s { - int len; - point_t *pt; /* pt[len]: path as extracted from bitmap */ - int *lon; /* lon[len]: (i,lon[i]) = longest straight line from i */ - - int x0, y0; /* origin for sums */ - sums_t *sums; /* sums[len+1]: cache for fast summing */ - - int m; /* length of optimal polygon */ - int *po; /* po[m]: optimal polygon */ - - privcurve_t curve; /* curve[m]: array of curve elements */ - privcurve_t ocurve; /* ocurve[om]: array of curve elements */ - privcurve_t *fcurve; /* final curve: this points to either curve or - ocurve. Do not free this separately. */ -}; -typedef struct potrace_privpath_s potrace_privpath_t; - -/* shorter names */ -typedef potrace_privpath_t privpath_t; -typedef potrace_path_t path_t; - -path_t *path_new(void); -void path_free(path_t *p); -void pathlist_free(path_t *plist); -int privcurve_init(privcurve_t *curve, int n); -void privcurve_to_curve(privcurve_t *pc, potrace_curve_t *c); - -#endif /* CURVE_H */ - diff --git a/src/trace/potrace/decompose.cpp b/src/trace/potrace/decompose.cpp deleted file mode 100644 index 7628b202d..000000000 --- a/src/trace/potrace/decompose.cpp +++ /dev/null @@ -1,511 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> - -#include "potracelib.h" -#include "curve.h" -#include "lists.h" -#include "bitmap.h" -#include "decompose.h" -#include "progress.h" - -/* ---------------------------------------------------------------------- */ -/* deterministically and efficiently hash (x,y) into a pseudo-random bit */ - -static inline int detrand(int x, int y) { - unsigned int z; - static const unsigned char t[256] = { - /* non-linear sequence: constant term of inverse in GF(8), - mod x^8+x^4+x^3+x+1 */ - 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, - 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, - 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, - 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, - 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, - 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, - }; - - /* 0x04b3e375 and 0x05a8ef93 are chosen to contain every possible - 5-bit sequence */ - z = ((0x04b3e375 * x) ^ y) * 0x05a8ef93; - z = t[z & 0xff] ^ t[(z>>8) & 0xff] ^ t[(z>>16) & 0xff] ^ t[(z>>24) & 0xff]; - return z; -} - -/* ---------------------------------------------------------------------- */ -/* auxiliary bitmap manipulations */ - -/* set the excess padding to 0 */ -static void bm_clearexcess(potrace_bitmap_t *bm) { - if (bm->w % BM_WORDBITS != 0) { - potrace_word mask = BM_ALLBITS << (BM_WORDBITS - (bm->w % BM_WORDBITS)); - for (int y=0; y<bm->h; y++) { - *bm_index(bm, bm->w, y) &= mask; - } - } -} - -struct bbox_s { - int x0, x1, y0, y1; /* bounding box */ -}; -typedef struct bbox_s bbox_t; - -/* clear the bm, assuming the bounding box is set correctly (faster - than clearing the whole bitmap) */ -static void clear_bm_with_bbox(potrace_bitmap_t *bm, bbox_t *bbox) { - int imin = (bbox->x0 / BM_WORDBITS); - int imax = ((bbox->x1 + BM_WORDBITS-1) / BM_WORDBITS); - int i, y; - - for (y=bbox->y0; y<bbox->y1; y++) { - for (i=imin; i<imax; i++) { - bm_scanline(bm, y)[i] = 0; - } - } -} - -/* ---------------------------------------------------------------------- */ -/* auxiliary functions */ - -/* return the "majority" value of bitmap bm at intersection (x,y). We - assume that the bitmap is balanced at "radius" 1. */ -static int majority(potrace_bitmap_t *bm, int x, int y) { - int i, a, ct; - - for (i=2; i<5; i++) { /* check at "radius" i */ - ct = 0; - for (a=-i+1; a<=i-1; a++) { - ct += BM_GET(bm, x+a, y+i-1) ? 1 : -1; - ct += BM_GET(bm, x+i-1, y+a-1) ? 1 : -1; - ct += BM_GET(bm, x+a-1, y-i) ? 1 : -1; - ct += BM_GET(bm, x-i, y+a) ? 1 : -1; - } - if (ct>0) { - return 1; - } else if (ct<0) { - return 0; - } - } - return 0; -} - -/* ---------------------------------------------------------------------- */ -/* decompose image into paths */ - -/* efficiently invert bits [x,infty) and [xa,infty) in line y. Here xa - must be a multiple of BM_WORDBITS. */ -static void xor_to_ref(potrace_bitmap_t *bm, int x, int y, int xa) { - int xhi = x & -BM_WORDBITS; - int xlo = x & (BM_WORDBITS-1); /* = x % BM_WORDBITS */ - int i; - - if (xhi<xa) { - for (i = xhi; i < xa; i+=BM_WORDBITS) { - *bm_index(bm, i, y) ^= BM_ALLBITS; - } - } else { - for (i = xa; i < xhi; i+=BM_WORDBITS) { - *bm_index(bm, i, y) ^= BM_ALLBITS; - } - } - /* note: the following "if" is needed because x86 treats a<<b as - a<<(b&31). I spent hours looking for this bug. */ - if (xlo) { - *bm_index(bm, xhi, y) ^= (BM_ALLBITS << (BM_WORDBITS - xlo)); - } -} - -/* a path is represented as an array of points, which are thought to - lie on the corners of pixels (not on their centers). The path point - (x,y) is the lower left corner of the pixel (x,y). Paths are - represented by the len/pt components of a path_t object (which - also stores other information about the path) */ - -/* xor the given pixmap with the interior of the given path. Note: the - path must be within the dimensions of the pixmap. */ -static void xor_path(potrace_bitmap_t *bm, path_t *p) { - int xa, x, y, k, y1; - - if (p->priv->len <= 0) { /* a path of length 0 is silly, but legal */ - return; - } - - y1 = p->priv->pt[p->priv->len-1].y; - - xa = p->priv->pt[0].x & -BM_WORDBITS; - for (k=0; k<p->priv->len; k++) { - x = p->priv->pt[k].x; - y = p->priv->pt[k].y; - - if (y != y1) { - /* efficiently invert the rectangle [x,xa] x [y,y1] */ - xor_to_ref(bm, x, min(y,y1), xa); - y1 = y; - } - } -} - -/* Find the bounding box of a given path. Path is assumed to be of - non-zero length. */ -static void setbbox_path(bbox_t *bbox, path_t *p) { - int x, y; - int k; - - bbox->y0 = INT_MAX; - bbox->y1 = 0; - bbox->x0 = INT_MAX; - bbox->x1 = 0; - - for (k=0; k<p->priv->len; k++) { - x = p->priv->pt[k].x; - y = p->priv->pt[k].y; - - if (x < bbox->x0) { - bbox->x0 = x; - } - if (x > bbox->x1) { - bbox->x1 = x; - } - if (y < bbox->y0) { - bbox->y0 = y; - } - if (y > bbox->y1) { - bbox->y1 = y; - } - } -} - -/* compute a path in the given pixmap, separating black from white. - Start path at the point (x0,x1), which must be an upper left corner - of the path. Also compute the area enclosed by the path. Return a - new path_t object, or NULL on error (note that a legitimate path - cannot have length 0). Sign is required for correct interpretation - of turnpolicies. */ -static path_t *findpath(potrace_bitmap_t *bm, int x0, int y0, int sign, int turnpolicy) { - int x, y, dirx, diry, len, size, area; - int c, d, tmp; - point_t *pt, *pt1; - path_t *p = NULL; - - x = x0; - y = y0; - dirx = 0; - diry = -1; - - len = size = 0; - pt = NULL; - area = 0; - - while (1) { - /* add point to path */ - if (len>=size) { - size += 100; - size = (int)(1.3 * size); - pt1 = (point_t *)realloc(pt, size * sizeof(point_t)); - if (!pt1) { - goto error; - } - pt = pt1; - } - pt[len].x = x; - pt[len].y = y; - len++; - - /* move to next point */ - x += dirx; - y += diry; - area += x*diry; - - /* path complete? */ - if (x==x0 && y==y0) { - break; - } - - /* determine next direction */ - c = BM_GET(bm, x + (dirx+diry-1)/2, y + (diry-dirx-1)/2); - d = BM_GET(bm, x + (dirx-diry-1)/2, y + (diry+dirx-1)/2); - - if (c && !d) { /* ambiguous turn */ - if (turnpolicy == POTRACE_TURNPOLICY_RIGHT - || (turnpolicy == POTRACE_TURNPOLICY_BLACK && sign == '+') - || (turnpolicy == POTRACE_TURNPOLICY_WHITE && sign == '-') - || (turnpolicy == POTRACE_TURNPOLICY_RANDOM && detrand(x,y)) - || (turnpolicy == POTRACE_TURNPOLICY_MAJORITY && majority(bm, x, y)) - || (turnpolicy == POTRACE_TURNPOLICY_MINORITY && !majority(bm, x, y))) { - tmp = dirx; /* right turn */ - dirx = diry; - diry = -tmp; - } else { - tmp = dirx; /* left turn */ - dirx = -diry; - diry = tmp; - } - } else if (c) { /* right turn */ - tmp = dirx; - dirx = diry; - diry = -tmp; - } else if (!d) { /* left turn */ - tmp = dirx; - dirx = -diry; - diry = tmp; - } - } /* while this path */ - - /* allocate new path object */ - p = path_new(); - if (!p) { - goto error; - } - - p->priv->pt = pt; - p->priv->len = len; - p->area = area; - p->sign = sign; - - return p; - - error: - free(pt); - return NULL; -} - -/* Give a tree structure to the given path list, based on "insideness" - testing. I.e., path A is considered "below" path B if it is inside - path B. The input pathlist is assumed to be ordered so that "outer" - paths occur before "inner" paths. The tree structure is stored in - the "childlist" and "sibling" components of the path_t - structure. The linked list structure is also changed so that - negative path components are listed immediately after their - positive parent. Note: some backends may ignore the tree - structure, others may use it e.g. to group path components. We - assume that in the input, point 0 of each path is an "upper left" - corner of the path, as returned by bm_to_pathlist. This makes it - easy to find an "interior" point. The bm argument should be a - bitmap of the correct size (large enough to hold all the paths), - and will be used as scratch space. Return 0 on success or -1 on - error with errno set. */ - -static void pathlist_to_tree(path_t *plist, potrace_bitmap_t *bm) { - path_t *p, *p1; - path_t *heap, *heap1; - path_t *cur; - path_t *head; - path_t **plist_hook; /* for fast appending to linked list */ - path_t **hook_in, **hook_out; /* for fast appending to linked list */ - bbox_t bbox; - - bm_clear(bm, 0); - - /* save original "next" pointers */ - list_forall(p, plist) { - p->sibling = p->next; - p->childlist = NULL; - } - - heap = plist; - - /* the heap holds a list of lists of paths. Use "childlist" field - for outer list, "next" field for inner list. Each of the sublists - is to be turned into a tree. This code is messy, but it is - actually fast. Each path is rendered exactly once. We use the - heap to get a tail recursive algorithm: the heap holds a list of - pathlists which still need to be transformed. */ - - while (heap) { - /* unlink first sublist */ - cur = heap; - heap = heap->childlist; - cur->childlist = NULL; - - /* unlink first path */ - head = cur; - cur = cur->next; - head->next = NULL; - - /* render path */ - xor_path(bm, head); - setbbox_path(&bbox, head); - - /* now do insideness test for each element of cur; append it to - head->childlist if it's inside head, else append it to - head->next. */ - hook_in=&head->childlist; - hook_out=&head->next; - list_forall_unlink(p, cur) { - if (p->priv->pt[0].y <= bbox.y0) { - list_insert_beforehook(p, hook_out); - /* append the remainder of the list to hook_out */ - *hook_out = cur; - break; - } - if (BM_GET(bm, p->priv->pt[0].x, p->priv->pt[0].y-1)) { - list_insert_beforehook(p, hook_in); - } else { - list_insert_beforehook(p, hook_out); - } - } - - /* clear bm */ - clear_bm_with_bbox(bm, &bbox); - - /* now schedule head->childlist and head->next for further - processing */ - if (head->next) { - head->next->childlist = heap; - heap = head->next; - } - if (head->childlist) { - head->childlist->childlist = heap; - heap = head->childlist; - } - } - - /* copy sibling structure from "next" to "sibling" component */ - p = plist; - while (p) { - p1 = p->sibling; - p->sibling = p->next; - p = p1; - } - - /* reconstruct a new linked list ("next") structure from tree - ("childlist", "sibling") structure. This code is slightly messy, - because we use a heap to make it tail recursive: the heap - contains a list of childlists which still need to be - processed. */ - heap = plist; - if (heap) { - heap->next = NULL; /* heap is a linked list of childlists */ - } - plist = NULL; - plist_hook = &plist; - while (heap) { - heap1 = heap->next; - for (p=heap; p; p=p->sibling) { - /* p is a positive path */ - /* append to linked list */ - list_insert_beforehook(p, plist_hook); - - /* go through its children */ - for (p1=p->childlist; p1; p1=p1->sibling) { - /* append to linked list */ - list_insert_beforehook(p1, plist_hook); - /* append its childlist to heap, if non-empty */ - if (p1->childlist) { - list_append(path_t, heap1, p1->childlist); - } - } - } - heap = heap1; - } - - return; -} - -/* find the next set pixel in a row <= y. Pixels are searched first - left-to-right, then top-down. In other words, (x,y)<(x',y') if y>y' - or y=y' and x<x'. If found, return 0 and store pixel in - (*xp,*yp). Else return 1. Note that this function assumes that - excess bytes have been cleared with bm_clearexcess. */ -static int findnext(potrace_bitmap_t *bm, int *xp, int *yp) { - int x; - int y; - int x0; - - x0 = (*xp) & ~(BM_WORDBITS-1); - - for (y=*yp; y>=0; y--) { - for (x=x0; x<bm->w; x+=BM_WORDBITS) { - if (*bm_index(bm, x, y)) { - while (!BM_GET(bm, x, y)) { - x++; - } - /* found */ - *xp = x; - *yp = y; - return 0; - } - } - x0 = 0; - } - /* not found */ - return 1; -} - -/* Decompose the given bitmap into paths. Returns a linked list of - path_t objects with the fields len, pt, area, sign filled - in. Returns 0 on success with plistp set, or -1 on error with errno - set. */ - -int bm_to_pathlist(const potrace_bitmap_t *bm, path_t **plistp, const potrace_param_t *param, progress_t *progress) { - int x; - int y; - path_t *p; - path_t *plist = NULL; /* linked list of path objects */ - path_t **plist_hook = &plist; /* used to speed up appending to linked list */ - potrace_bitmap_t *bm1 = NULL; - int sign; - - bm1 = bm_dup(bm); - if (!bm1) { - goto error; - } - - /* be sure the byte padding on the right is set to 0, as the fast - pixel search below relies on it */ - bm_clearexcess(bm1); - - /* iterate through components */ - x = 0; - y = bm1->h - 1; - while (findnext(bm1, &x, &y) == 0) { - /* calculate the sign by looking at the original */ - sign = BM_GET(bm, x, y) ? '+' : '-'; - - /* calculate the path */ - p = findpath(bm1, x, y+1, sign, param->turnpolicy); - if (p==NULL) { - goto error; - } - - /* update buffered image */ - xor_path(bm1, p); - - /* if it's a turd, eliminate it, else append it to the list */ - if (p->area <= param->turdsize) { - path_free(p); - } else { - list_insert_beforehook(p, plist_hook); - } - - if (bm1->h > 0) { /* to be sure */ - progress_update(1-y/(double)bm1->h, progress); - } - } - - pathlist_to_tree(plist, bm1); - bm_free(bm1); - *plistp = plist; - - progress_update(1.0, progress); - - return 0; - - error: - bm_free(bm1); - list_forall_unlink(p, plist) { - path_free(p); - } - return -1; -} diff --git a/src/trace/potrace/decompose.h b/src/trace/potrace/decompose.h deleted file mode 100644 index 8ae89b8ad..000000000 --- a/src/trace/potrace/decompose.h +++ /dev/null @@ -1,16 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - - -#ifndef DECOMPOSE_H -#define DECOMPOSE_H - -#include "potracelib.h" -#include "progress.h" -#include "curve.h" - -int bm_to_pathlist(const potrace_bitmap_t *bm, path_t **plistp, const potrace_param_t *param, progress_t *progress); - -#endif /* DECOMPOSE_H */ - diff --git a/src/trace/potrace/greymap.cpp b/src/trace/potrace/greymap.cpp deleted file mode 100644 index 4ef2ec8df..000000000 --- a/src/trace/potrace/greymap.cpp +++ /dev/null @@ -1,941 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - - -/* Routines for manipulating greymaps, including reading pgm files. We - only deal with greymaps of depth 8 bits. */ - -#include <stdlib.h> -#include <string.h> -#include <math.h> -#include <errno.h> - -#include "greymap.h" -#include "bitops.h" - -#define INTBITS (8*sizeof(int)) - -#define mod(a,n) ((a)>=(n) ? (a)%(n) : (a)>=0 ? (a) : (n)-1-(-1-(a))%(n)) - -static int gm_readbody_pnm(FILE *f, greymap_t **gmp, int magic); -static int gm_readbody_bmp(FILE *f, greymap_t **gmp); - -/* ---------------------------------------------------------------------- */ -/* basic greymap routines */ - -/* return new un-initialized greymap. NULL with errno on error. - Assumes w, h >= 0. */ -greymap_t *gm_new(int w, int h) { - greymap_t *gm; - ssize_t size = (ssize_t)w * (ssize_t)h * (ssize_t)sizeof(signed short int); - - /* check for overflow error */ - if (size < 0 || size / w / h != sizeof(signed short int)) { - errno = ENOMEM; - return NULL; - } - - gm = (greymap_t *) malloc(sizeof(greymap_t)); - if (!gm) { - return NULL; - } - gm->w = w; - gm->h = h; - gm->map = (signed short int *) malloc(size); - if (!gm->map) { - free(gm); - return NULL; - } - return gm; -} - -/* free the given greymap */ -void gm_free(greymap_t *gm) { - if (gm) { - free(gm->map); - } - free(gm); -} - -/* duplicate the given greymap. Return NULL on error with errno set. */ -greymap_t *gm_dup(greymap_t *gm) { - greymap_t *gm1 = gm_new(gm->w, gm->h); - if (!gm1) { - return NULL; - } - memcpy(gm1->map, gm->map, gm->w*gm->h*sizeof(signed short int)); - return gm1; -} - -/* clear the given greymap to color b. */ -void gm_clear(greymap_t *gm, int b) { - if (b==0) { - memset(gm->map, 0, gm->w*gm->h*sizeof(signed short int)); - } else { - for (int i=0; i<gm->w*gm->h; i++) { - gm->map[i] = b; - } - } -} - -/* ---------------------------------------------------------------------- */ -/* routines for reading pnm streams */ - -/* read next character after whitespace and comments. Return EOF on - end of file or error. */ -static int fgetc_ws(FILE *f) { - int c; - - while (1) { - c = fgetc(f); - if (c=='#') { - while (1) { - c = fgetc(f); - if (c=='\n' || c==EOF) { - break; - } - } - } - /* space, tab, line feed, carriage return, form-feed */ - if (c!=' ' && c!='\t' && c!='\r' && c!='\n' && c!=12) { - return c; - } - } -} - -/* skip whitespace and comments, then read a non-negative decimal - number from a stream. Return -1 on EOF. Tolerate other errors (skip - bad characters). Do not the read any characters following the - number (put next character back into the stream) */ - -static int readnum(FILE *f) { - int c; - int acc; - - /* skip whitespace and comments */ - while (1) { - c = fgetc_ws(f); - if (c==EOF) { - return -1; - } - if (c>='0' && c<='9') { - break; - } - } - - /* first digit is already in c */ - acc = c-'0'; - while (1) { - c = fgetc(f); - if (c==EOF) { - break; - } - if (c<'0' || c>'9') { - ungetc(c, f); - break; - } - acc *= 10; - acc += c-'0'; - } - return acc; -} - -/* similar to readnum, but read only a single 0 or 1, and do not read - any characters after it. */ - -static int readbit(FILE *f) { - int c; - - /* skip whitespace and comments */ - while (1) { - c = fgetc_ws(f); - if (c==EOF) { - return -1; - } - if (c>='0' && c<='1') { - break; - } - } - - return c-'0'; -} - -/* ---------------------------------------------------------------------- */ - -/* read a PNM stream: P1-P6 format (see pnm(5)), or a BMP stream, and - convert the output to a greymap. Return greymap in *gmp. Return 0 - on success, -1 on error with errno set, -2 on bad file format (with - error message in gm_read_error), and 1 on premature end of file, -3 - on empty file (including files with only whitespace and comments), - -4 if wrong magic number. If the return value is >=0, *gmp is - valid. */ - -char const *gm_read_error = NULL; - -int gm_read(FILE *f, greymap_t **gmp) { - int magic[2]; - - /* read magic number. We ignore whitespace and comments before the - magic, for the benefit of concatenated files in P1-P3 format. - Multiple P1-P3 images in a single file are not formally allowed - by the PNM standard, but there is no harm in being lenient. */ - - magic[0] = fgetc_ws(f); - if (magic[0] == EOF) { - /* files which contain only comments and whitespace count as "empty" */ - return -3; - } - magic[1] = fgetc(f); - if (magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6') { - return gm_readbody_pnm(f, gmp, magic[1]); - } - if (magic[0] == 'B' && magic[1] == 'M') { - return gm_readbody_bmp(f, gmp); - } - return -4; -} - -/* ---------------------------------------------------------------------- */ -/* read PNM format */ - -/* read PNM stream after magic number. Return values as for gm_read */ -static int gm_readbody_pnm(FILE *f, greymap_t **gmp, int magic) { - greymap_t *gm; - int x, y, i, j, b, b1, sum; - int bpr; /* bytes per row (as opposed to 4*gm->c) */ - int w, h, max; - - gm = NULL; - - w = readnum(f); - if (w<0) { - goto format_error; - } - - h = readnum(f); - if (h<0) { - goto format_error; - } - - /* allocate greymap */ - gm = gm_new(w, h); - if (!gm) { - return -1; - } - - /* zero it out */ - gm_clear(gm, 0); - - switch (magic) { - default: - /* not reached */ - goto format_error; - - case '1': - /* read P1 format: PBM ascii */ - - for (y=h-1; y>=0; y--) { - for (x=0; x<w; x++) { - b = readbit(f); - if (b<0) { - goto eof; - } - GM_UPUT(gm, x, y, b ? 0 : 255); - } - } - break; - - case '2': - /* read P2 format: PGM ascii */ - - max = readnum(f); - if (max<1) { - goto format_error; - } - - for (y=h-1; y>=0; y--) { - for (x=0; x<w; x++) { - b = readnum(f); - if (b<0) { - goto eof; - } - GM_UPUT(gm, x, y, b*255/max); - } - } - break; - - case '3': - /* read P3 format: PPM ascii */ - - max = readnum(f); - if (max<1) { - goto format_error; - } - - for (y=h-1; y>=0; y--) { - for (x=0; x<w; x++) { - sum = 0; - for (i=0; i<3; i++) { - b = readnum(f); - if (b<0) { - goto eof; - } - sum += b; - } - GM_UPUT(gm, x, y, sum*(255/3)/max); - } - } - break; - - case '4': - /* read P4 format: PBM raw */ - - b = fgetc(f); /* read single white-space character after height */ - if (b==EOF) { - goto format_error; - } - - bpr = (w+7)/8; - - for (y=h-1; y>=0; y--) { - for (i=0; i<bpr; i++) { - b = fgetc(f); - if (b==EOF) { - goto eof; - } - for (j=0; j<8; j++) { - GM_PUT(gm, i*8+j, y, b & (0x80 >> j) ? 0 : 255); - } - } - } - break; - - case '5': - /* read P5 format: PGM raw */ - - max = readnum(f); - if (max<1) { - goto format_error; - } - - b = fgetc(f); /* read single white-space character after max */ - if (b==EOF) { - goto format_error; - } - - for (y=h-1; y>=0; y--) { - for (x=0; x<w; x++) { - b = fgetc(f); - if (b==EOF) - goto eof; - if (max>=256) { - b <<= 8; - b1 = fgetc(f); - if (b1==EOF) - goto eof; - b |= b1; - } - GM_UPUT(gm, x, y, b*255/max); - } - } - break; - - case '6': - /* read P6 format: PPM raw */ - - max = readnum(f); - if (max<1) { - goto format_error; - } - - b = fgetc(f); /* read single white-space character after max */ - if (b==EOF) { - goto format_error; - } - - for (y=h-1; y>=0; y--) { - for (x=0; x<w; x++) { - sum = 0; - for (i=0; i<3; i++) { - b = fgetc(f); - if (b==EOF) { - goto eof; - } - if (max>=256) { - b <<= 8; - b1 = fgetc(f); - if (b1==EOF) - goto eof; - b |= b1; - } - sum += b; - } - GM_UPUT(gm, x, y, sum*(255/3)/max); - } - } - break; - } - - *gmp = gm; - return 0; - - eof: - *gmp = gm; - return 1; - - format_error: - gm_free(gm); - if (magic == '1' || magic == '4') { - gm_read_error = "invalid pbm file"; - } else if (magic == '2' || magic == '5') { - gm_read_error = "invalid pgm file"; - } else { - gm_read_error = "invalid ppm file"; - } - return -2; -} - -/* ---------------------------------------------------------------------- */ -/* read BMP format */ - -struct bmp_info_s { - unsigned int FileSize; - unsigned int reserved; - unsigned int DataOffset; - unsigned int InfoSize; - unsigned int w; /* width */ - unsigned int h; /* height */ - unsigned int Planes; - unsigned int bits; /* bits per sample */ - unsigned int comp; /* compression mode */ - unsigned int ImageSize; - unsigned int XpixelsPerM; - unsigned int YpixelsPerM; - unsigned int ncolors; /* number of colors in palette */ - unsigned int ColorsImportant; - unsigned int RedMask; - unsigned int GreenMask; - unsigned int BlueMask; - unsigned int AlphaMask; - unsigned int ctbits; /* sample size for color table */ - int topdown; /* top-down mode? */ -}; -typedef struct bmp_info_s bmp_info_t; - -/* auxiliary */ - -static int bmp_count = 0; /* counter for byte padding */ -static int bmp_pos = 0; /* counter from start of BMP data */ - -/* read n-byte little-endian integer. Return 1 on EOF or error, else - 0. Assume n<=4. */ -static int bmp_readint(FILE *f, int n, unsigned int *p) { - int i; - unsigned int sum = 0; - int b; - - for (i=0; i<n; i++) { - b = fgetc(f); - if (b==EOF) { - return 1; - } - sum += b << (8*i); - } - bmp_count += n; - bmp_pos += n; - *p = sum; - return 0; -} - -/* reset padding boundary */ -static void bmp_pad_reset(void) { - bmp_count = 0; -} - -/* read padding bytes to 4-byte boundary. Return 1 on EOF or error, - else 0. */ -static int bmp_pad(FILE *f) { - int c, i, b; - - c = (-bmp_count) & 3; - for (i=0; i<c; i++) { - b = fgetc(f); - if (b==EOF) { - return 1; - } - } - bmp_pos += c; - bmp_count = 0; - return 0; -} - -/* forward to the new file position. Return 1 on EOF or error, else 0 */ -static int bmp_forward(FILE *f, int pos) { - int b; - - while (bmp_pos < pos) { - b = fgetc(f); - if (b==EOF) { - return 1; - } - bmp_pos++; - bmp_count++; - } - return 0; -} - -#define TRY(x) if (x) goto try_error -#define TRY_EOF(x) if (x) goto eof - -/* correct y-coordinate for top-down format */ -#define ycorr(y) (bmpinfo.topdown ? bmpinfo.h-1-y : y) - -/* read BMP stream after magic number. Return values as for gm_read. - We choose to be as permissive as possible, since there are many - programs out there which produce BMP. For instance, ppmtobmp can - produce codings with anywhere from 1-8 or 24 bits per sample, - although most specifications only allow 1,4,8,24,32. We can also - read both the old and new OS/2 BMP formats in addition to the - Windows BMP format. */ -static int gm_readbody_bmp(FILE *f, greymap_t **gmp) { - bmp_info_t bmpinfo; - int *coltable; - unsigned int b, c; - unsigned int i, j; - greymap_t *gm; - unsigned int x, y; - int col[2]; - unsigned int bitbuf; - unsigned int n; - unsigned int redshift, greenshift, blueshift; - - gm_read_error = NULL; - gm = NULL; - coltable = NULL; - - bmp_pos = 2; /* set file position */ - - /* file header (minus magic number) */ - TRY(bmp_readint(f, 4, &bmpinfo.FileSize)); - TRY(bmp_readint(f, 4, &bmpinfo.reserved)); - TRY(bmp_readint(f, 4, &bmpinfo.DataOffset)); - - /* info header */ - TRY(bmp_readint(f, 4, &bmpinfo.InfoSize)); - if (bmpinfo.InfoSize == 40 || bmpinfo.InfoSize == 64 - || bmpinfo.InfoSize == 108 || bmpinfo.InfoSize == 124) { - /* Windows or new OS/2 format */ - bmpinfo.ctbits = 32; /* sample size in color table */ - TRY(bmp_readint(f, 4, &bmpinfo.w)); - TRY(bmp_readint(f, 4, &bmpinfo.h)); - TRY(bmp_readint(f, 2, &bmpinfo.Planes)); - TRY(bmp_readint(f, 2, &bmpinfo.bits)); - TRY(bmp_readint(f, 4, &bmpinfo.comp)); - TRY(bmp_readint(f, 4, &bmpinfo.ImageSize)); - TRY(bmp_readint(f, 4, &bmpinfo.XpixelsPerM)); - TRY(bmp_readint(f, 4, &bmpinfo.YpixelsPerM)); - TRY(bmp_readint(f, 4, &bmpinfo.ncolors)); - TRY(bmp_readint(f, 4, &bmpinfo.ColorsImportant)); - if (bmpinfo.InfoSize >= 108) { /* V4 and V5 bitmaps */ - TRY(bmp_readint(f, 4, &bmpinfo.RedMask)); - TRY(bmp_readint(f, 4, &bmpinfo.GreenMask)); - TRY(bmp_readint(f, 4, &bmpinfo.BlueMask)); - TRY(bmp_readint(f, 4, &bmpinfo.AlphaMask)); - } - if (bmpinfo.w > 0x7fffffff) { - goto format_error; - } - if (bmpinfo.h > 0x7fffffff) { - bmpinfo.h = (-bmpinfo.h) & 0xffffffff; - bmpinfo.topdown = 1; - } else { - bmpinfo.topdown = 0; - } - if (bmpinfo.h > 0x7fffffff) { - goto format_error; - } - } else if (bmpinfo.InfoSize == 12) { - /* old OS/2 format */ - bmpinfo.ctbits = 24; /* sample size in color table */ - TRY(bmp_readint(f, 2, &bmpinfo.w)); - TRY(bmp_readint(f, 2, &bmpinfo.h)); - TRY(bmp_readint(f, 2, &bmpinfo.Planes)); - TRY(bmp_readint(f, 2, &bmpinfo.bits)); - bmpinfo.comp = 0; - bmpinfo.ncolors = 0; - bmpinfo.topdown = 0; - } else { - goto format_error; - } - - if (bmpinfo.comp == 3 && bmpinfo.InfoSize < 108) { - /* bitfield feature is only understood with V4 and V5 format */ - goto format_error; - } - - /* forward to color table (e.g., if bmpinfo.InfoSize == 64) */ - TRY(bmp_forward(f, 14+bmpinfo.InfoSize)); - - if (bmpinfo.Planes != 1) { - gm_read_error = "cannot handle bmp planes"; - goto format_error; /* can't handle planes */ - } - - if (bmpinfo.ncolors == 0) { - bmpinfo.ncolors = 1 << bmpinfo.bits; - } - - /* color table, present only if bmpinfo.bits <= 8. */ - if (bmpinfo.bits <= 8) { - coltable = (int *) calloc(bmpinfo.ncolors, sizeof(int)); - if (!coltable) { - goto std_error; - } - /* NOTE: since we are reading a greymap, we can immediately convert - the color table entries to grey values. */ - for (i=0; i<bmpinfo.ncolors; i++) { - TRY(bmp_readint(f, bmpinfo.ctbits/8, &c)); - c = ((c>>16) & 0xff) + ((c>>8) & 0xff) + (c & 0xff); - coltable[i] = c/3; - } - } - - /* forward to data */ - if (bmpinfo.InfoSize != 12) { /* not old OS/2 format */ - TRY(bmp_forward(f, bmpinfo.DataOffset)); - } - - /* allocate greymap */ - gm = gm_new(bmpinfo.w, bmpinfo.h); - if (!gm) { - goto std_error; - } - - /* zero it out */ - gm_clear(gm, 0); - - switch (bmpinfo.bits + 0x100*bmpinfo.comp) { - - default: - goto format_error; - break; - - case 0x001: /* monochrome palette */ - - /* raster data */ - for (y=0; y<bmpinfo.h; y++) { - bmp_pad_reset(); - for (i=0; 8*i<bmpinfo.w; i++) { - TRY_EOF(bmp_readint(f, 1, &b)); - for (j=0; j<8; j++) { - GM_PUT(gm, i*8+j, ycorr(y), b & (0x80 >> j) ? coltable[1] : coltable[0]); - } - } - TRY(bmp_pad(f)); - } - break; - - case 0x002: /* 2-bit to 8-bit palettes */ - case 0x003: - case 0x004: - case 0x005: - case 0x006: - case 0x007: - case 0x008: - for (y=0; y<bmpinfo.h; y++) { - bmp_pad_reset(); - bitbuf = 0; /* bit buffer: bits in buffer are high-aligned */ - n = 0; /* number of bits currently in bitbuffer */ - for (x=0; x<bmpinfo.w; x++) { - if (n < bmpinfo.bits) { - TRY_EOF(bmp_readint(f, 1, &b)); - bitbuf |= b << (INTBITS - 8 - n); - n += 8; - } - b = bitbuf >> (INTBITS - bmpinfo.bits); - bitbuf <<= bmpinfo.bits; - n -= bmpinfo.bits; - GM_UPUT(gm, x, ycorr(y), coltable[b]); - } - TRY(bmp_pad(f)); - } - break; - - case 0x010: /* 16-bit encoding */ - /* can't do this format because it is not well-documented and I - don't have any samples */ - gm_read_error = "cannot handle bmp 16-bit coding"; - goto format_error; - break; - - case 0x018: /* 24-bit encoding */ - case 0x020: /* 32-bit encoding */ - for (y=0; y<bmpinfo.h; y++) { - bmp_pad_reset(); - for (x=0; x<bmpinfo.w; x++) { - TRY_EOF(bmp_readint(f, bmpinfo.bits/8, &c)); - c = ((c>>16) & 0xff) + ((c>>8) & 0xff) + (c & 0xff); - GM_UPUT(gm, x, ycorr(y), c/3); - } - TRY(bmp_pad(f)); - } - break; - - case 0x320: /* 32-bit encoding with bitfields */ - redshift = lobit(bmpinfo.RedMask); - greenshift = lobit(bmpinfo.GreenMask); - blueshift = lobit(bmpinfo.BlueMask); - - for (y=0; y<bmpinfo.h; y++) { - bmp_pad_reset(); - for (x=0; x<bmpinfo.w; x++) { - TRY_EOF(bmp_readint(f, bmpinfo.bits/8, &c)); - c = ((c & bmpinfo.RedMask) >> redshift) + ((c & bmpinfo.GreenMask) >> greenshift) + ((c & bmpinfo.BlueMask) >> blueshift); - GM_UPUT(gm, x, ycorr(y), c/3); - } - TRY(bmp_pad(f)); - } - break; - - case 0x204: /* 4-bit runlength compressed encoding (RLE4) */ - x = 0; - y = 0; - while (1) { - TRY_EOF(bmp_readint(f, 1, &b)); /* opcode */ - TRY_EOF(bmp_readint(f, 1, &c)); /* argument */ - if (b>0) { - /* repeat count */ - col[0] = coltable[(c>>4) & 0xf]; - col[1] = coltable[c & 0xf]; - for (i=0; i<b && x<bmpinfo.w; i++) { - if (x>=bmpinfo.w) { - x=0; - y++; - } - if (y>=bmpinfo.h) { - break; - } - GM_UPUT(gm, x, ycorr(y), col[i&1]); - x++; - } - } else if (c == 0) { - /* end of line */ - y++; - x = 0; - } else if (c == 1) { - /* end of greymap */ - break; - } else if (c == 2) { - /* "delta": skip pixels in x and y directions */ - TRY_EOF(bmp_readint(f, 1, &b)); /* x offset */ - TRY_EOF(bmp_readint(f, 1, &c)); /* y offset */ - x += b; - y += c; - } else { - /* verbatim segment */ - for (i=0; i<c; i++) { - if ((i&1)==0) { - TRY_EOF(bmp_readint(f, 1, &b)); - } - if (x>=bmpinfo.w) { - x=0; - y++; - } - if (y>=bmpinfo.h) { - break; - } - GM_PUT(gm, x, ycorr(y), coltable[(b>>(4-4*(i&1))) & 0xf]); - x++; - } - if ((c+1) & 2) { - /* pad to 16-bit boundary */ - TRY_EOF(bmp_readint(f, 1, &b)); - } - } - } - break; - - case 0x108: /* 8-bit runlength compressed encoding (RLE8) */ - x = 0; - y = 0; - while (1) { - TRY_EOF(bmp_readint(f, 1, &b)); /* opcode */ - TRY_EOF(bmp_readint(f, 1, &c)); /* argument */ - if (b>0) { - /* repeat count */ - for (i=0; i<b; i++) { - if (x>=bmpinfo.w) { - x=0; - y++; - } - if (y>=bmpinfo.h) { - break; - } - GM_UPUT(gm, x, ycorr(y), coltable[c]); - x++; - } - } else if (c == 0) { - /* end of line */ - y++; - x = 0; - } else if (c == 1) { - /* end of greymap */ - break; - } else if (c == 2) { - /* "delta": skip pixels in x and y directions */ - TRY_EOF(bmp_readint(f, 1, &b)); /* x offset */ - TRY_EOF(bmp_readint(f, 1, &c)); /* y offset */ - x += b; - y += c; - } else { - /* verbatim segment */ - for (i=0; i<c; i++) { - TRY_EOF(bmp_readint(f, 1, &b)); - if (x>=bmpinfo.w) { - x=0; - y++; - } - if (y>=bmpinfo.h) { - break; - } - GM_PUT(gm, x, ycorr(y), coltable[b]); - x++; - } - if (c & 1) { - /* pad input to 16-bit boundary */ - TRY_EOF(bmp_readint(f, 1, &b)); - } - } - } - break; - - } /* switch */ - - /* skip any potential junk after the data section, but don't - complain in case EOF is encountered */ - bmp_forward(f, bmpinfo.FileSize); - - free(coltable); - *gmp = gm; - return 0; - - eof: - free(coltable); - *gmp = gm; - return 1; - - format_error: - try_error: - free(coltable); - free(gm); - if (!gm_read_error) { - gm_read_error = "invalid bmp file"; - } - return -2; - - std_error: - free(coltable); - free(gm); - return -1; -} - -/* ---------------------------------------------------------------------- */ - -/* write a pgm stream, either P2 or (if raw != 0) P5 format. Include - one-line comment if non-NULL. Mode determines how out-of-range - color values are converted. Gamma is the desired gamma correction, - if any (set to 2.2 if the image is to look optimal on a CRT monitor, - 2.8 for LCD). Set to 1.0 for no gamma correction */ - -int gm_writepgm(FILE *f, greymap_t *gm, char *comment, int raw, int mode, double gamma) { - int x, y, v; - int gammatable[256]; - - /* prepare gamma correction lookup table */ - if (gamma != 1.0) { - gammatable[0] = 0; - for (v=1; v<256; v++) { - gammatable[v] = (int)(255 * exp(log(v/255.0)/gamma) + 0.5); - } - } else { - for (v=0; v<256; v++) { - gammatable[v] = v; - } - } - - fprintf(f, raw ? "P5\n" : "P2\n"); - if (comment && *comment) { - fprintf(f, "# %s\n", comment); - } - fprintf(f, "%d %d 255\n", gm->w, gm->h); - for (y=gm->h-1; y>=0; y--) { - for (x=0; x<gm->w; x++) { - v = GM_UGET(gm, x, y); - if (mode == GM_MODE_NONZERO) { - if (v > 255) { - v = 510 - v; - } - if (v < 0) { - v = 0; - } - } else if (mode == GM_MODE_ODD) { - v = mod(v, 510); - if (v > 255) { - v = 510 - v; - } - } else if (mode == GM_MODE_POSITIVE) { - if (v < 0) { - v = 0; - } else if (v > 255) { - v = 255; - } - } else if (mode == GM_MODE_NEGATIVE) { - v = 510 - v; - if (v < 0) { - v = 0; - } else if (v > 255) { - v = 255; - } - } - v = gammatable[v]; - - if (raw) { - fputc(v, f); - } else { - fprintf(f, x == gm->w-1 ? "%d\n" : "%d ", v); - } - } - } - return 0; -} - -/* ---------------------------------------------------------------------- */ -/* output - for primitive debugging purposes only! */ - -/* print greymap to screen */ -int gm_print(FILE *f, greymap_t *gm) { - int x, y; - int xx, yy; - int d, t; - int sw, sh; - - sw = gm->w < 79 ? gm->w : 79; - sh = gm->w < 79 ? gm->h : gm->h*sw*44/(79*gm->w); - - for (yy=sh-1; yy>=0; yy--) { - for (xx=0; xx<sw; xx++) { - d=0; - t=0; - for (x=xx*gm->w/sw; x<(xx+1)*gm->w/sw; x++) { - for (y=yy*gm->h/sh; y<(yy+1)*gm->h/sh; y++) { - d += GM_GET(gm, x, y); - t += 256; - } - } - fputc("*#=- "[5*d/t], f); /* what a cute trick :) */ - } - fputc('\n', f); - } - return 0; -} diff --git a/src/trace/potrace/greymap.h b/src/trace/potrace/greymap.h deleted file mode 100644 index 8c9db9bbf..000000000 --- a/src/trace/potrace/greymap.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - - -#ifndef GREYMAP_H -#define GREYMAP_H - -#include <stdio.h> -#include <stdlib.h> - -/* internal format for greymaps. Note: in this format, rows are - ordered from bottom to top. The pixels in each row are given from - left to right. */ - -struct greymap_s { - int w; /* width, in pixels */ - int h; /* height, in pixels */ - signed short int *map; /* raw data, w*h values */ -}; -typedef struct greymap_s greymap_t; - -/* macros for accessing pixel at index (x,y). Note that the origin is - in the *lower* left corner. U* macros omit the bounds check. */ - -#define gm_index(gm, x, y) (&(gm)->map[(x)+(y)*(ssize_t)(gm)->w]) -#define gm_safe(gm, x, y) ((int)(x)>=0 && (int)(x)<(gm)->w && (int)(y)>=0 && (int)(y)<(gm)->h) -#define gm_bound(x, m) ((x)<0 ? 0 : (x)>=(m) ? (m)-1 : (x)) -#define GM_UGET(gm, x, y) (*gm_index(gm, x, y)) -#define GM_UINC(gm, x, y, b) (*gm_index(gm, x, y) += (short int)(b)) -#define GM_UINV(gm, x, y) (*gm_index(gm, x, y) = 255 - *gm_index(gm, x, y)) -#define GM_UPUT(gm, x, y, b) (*gm_index(gm, x, y) = (short int)(b)) -#define GM_GET(gm, x, y) (gm_safe(gm, x, y) ? GM_UGET(gm, x, y) : 0) -#define GM_INC(gm, x, y, b) (gm_safe(gm, x, y) ? GM_UINC(gm, x, y, b) : 0) -#define GM_INV(gm, x, y) (gm_safe(gm, x, y) ? GM_UINV(gm, x, y) : 0) -#define GM_PUT(gm, x, y, b) (gm_safe(gm, x, y) ? GM_UPUT(gm, x, y, b) : 0) -#define GM_BGET(gm, x, y) GM_UGET(gm, gm_bound(x, gm->w), gm_bound(y, gm->h)) - -/* modes for cutting off out-of-range values. The following names - refer to winding numbers. I.e., make a pixel black if winding - number is nonzero, odd, or positive, respectively. We assume that 0 - winding number corresponds to white (255). */ -#define GM_MODE_NONZERO 1 -#define GM_MODE_ODD 2 -#define GM_MODE_POSITIVE 3 -#define GM_MODE_NEGATIVE 4 - -extern char const *gm_read_error; - -greymap_t *gm_new(int w, int h); -greymap_t *gm_dup(greymap_t *gm); -void gm_free(greymap_t *gm); -void gm_clear(greymap_t *gm, int b); -int gm_read(FILE *f, greymap_t **gmp); -int gm_writepgm(FILE *f, greymap_t *gm, char *comment, int raw, int mode, double gamma); -int gm_print(FILE *f, greymap_t *gm); - -#endif /* GREYMAP_H */ diff --git a/src/trace/potrace/inkscape-potrace.cpp b/src/trace/potrace/inkscape-potrace.cpp index 8f3bb7bdf..a0b0df1f6 100644 --- a/src/trace/potrace/inkscape-potrace.cpp +++ b/src/trace/potrace/inkscape-potrace.cpp @@ -29,7 +29,6 @@ #include "message-stack.h" #include <sp-path.h> #include <svg/path-string.h> -#include "curve.h" #include "bitmap.h" using Glib::ustring; @@ -128,7 +127,7 @@ static bool hasPoint(std::vector<Point> &points, double x, double y) /** - * Recursively descend the path_t node tree, writing paths in SVG + * Recursively descend the potrace_path_t node tree, writing paths in SVG * format into the output stream. The Point vector is used to prevent * redundant paths. Returns number of paths processed. */ @@ -144,7 +143,7 @@ static long writePaths(PotraceTracingEngine *engine, potrace_path_t *plist, //g_message("node->fm:%d\n", node->fm); if (!curve->n) continue; - dpoint_t *pt = curve->c[curve->n - 1]; + const potrace_dpoint_t *pt = curve->c[curve->n - 1]; double x0 = 0.0; double y0 = 0.0; double x1 = 0.0; @@ -192,7 +191,7 @@ static long writePaths(PotraceTracingEngine *engine, potrace_path_t *plist, } data.closePath(); - for (path_t *child=node->childlist; child ; child=child->sibling) + for (potrace_path_t *child=node->childlist; child ; child=child->sibling) { nodeCount += writePaths(engine, child, data, points); } diff --git a/src/trace/potrace/inkscape-potrace.h b/src/trace/potrace/inkscape-potrace.h index 88da56abb..e8e654973 100644 --- a/src/trace/potrace/inkscape-potrace.h +++ b/src/trace/potrace/inkscape-potrace.h @@ -18,7 +18,7 @@ #define __INKSCAPE_POTRACE_H__ #include <trace/trace.h> -#include "potracelib.h" +#include <potracelib.h> struct GrayMap_def; typedef GrayMap_def GrayMap; diff --git a/src/trace/potrace/lists.h b/src/trace/potrace/lists.h deleted file mode 100644 index 394262c23..000000000 --- a/src/trace/potrace/lists.h +++ /dev/null @@ -1,285 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - - -#ifndef _PS_LISTS_H -#define _PS_LISTS_H - -/* here we define some general list macros. Because they are macros, - they should work on any datatype with a "->next" component. Some of - them use a "hook". If elt and list are of type t* then hook is of - type t**. A hook stands for an insertion point in the list, i.e., - either before the first element, or between two elements, or after - the last element. If an operation "sets the hook" for an element, - then the hook is set to just before the element. One can insert - something at a hook. One can also unlink at a hook: this means, - unlink the element just after the hook. By "to unlink", we mean the - element is removed from the list, but not deleted. Thus, it and its - components still need to be freed. */ - -/* Note: these macros are somewhat experimental. Only the ones that - are actually *used* have been tested. So be careful to test any - that you use. Looking at the output of the preprocessor, "gcc -E" - (possibly piped though "indent"), might help too. Also: these - macros define some internal (local) variables that start with - "_". */ - -/* we enclose macro definitions whose body consists of more than one - statement in MACRO_BEGIN and MACRO_END, rather than '{' and '}'. The - reason is that we want to be able to use the macro in a context - such as "if (...) macro(...); else ...". If we didn't use this obscure - trick, we'd have to omit the ";" in such cases. */ - -#define MACRO_BEGIN do { -#define MACRO_END } while (0) - -/* ---------------------------------------------------------------------- */ -/* macros for singly-linked lists */ - -/* traverse list. At the end, elt is set to NULL. */ -#define list_forall(elt, list) for (elt=list; elt!=NULL; elt=elt->next) - -/* set elt to the first element of list satisfying boolean condition - c, or NULL if not found */ -#define list_find(elt, list, c) \ - MACRO_BEGIN list_forall(elt, list) if (c) break; MACRO_END - -/* like forall, except also set hook for elt. */ -#define list_forall2(elt, list, hook) \ - for (elt=list, hook=&list; elt!=NULL; hook=&elt->next, elt=elt->next) - -/* same as list_find, except also set hook for elt. */ -#define list_find2(elt, list, c, hook) \ - MACRO_BEGIN list_forall2(elt, list, hook) if (c) break; MACRO_END - -/* same, except only use hook. */ -#define _list_forall_hook(list, hook) \ - for (hook=&list; *hook!=NULL; hook=&(*hook)->next) - -/* same, except only use hook. Note: c may only refer to *hook, not elt. */ -#define _list_find_hook(list, c, hook) \ - MACRO_BEGIN _list_forall_hook(list, hook) if (c) break; MACRO_END - -/* insert element after hook */ -#define list_insert_athook(elt, hook) \ - MACRO_BEGIN elt->next = *hook; *hook = elt; MACRO_END - -/* insert element before hook */ -#define list_insert_beforehook(elt, hook) \ - MACRO_BEGIN elt->next = *hook; *hook = elt; hook=&elt->next; MACRO_END - -/* unlink element after hook, let elt be unlinked element, or NULL. - hook remains. */ -#define list_unlink_athook(list, elt, hook) \ - MACRO_BEGIN \ - elt = hook ? *hook : NULL; if (elt) { *hook = elt->next; elt->next = NULL; }\ - MACRO_END - -/* unlink the specific element, if it is in the list. Otherwise, set - elt to NULL */ -#define list_unlink(listtype, list, elt) \ - MACRO_BEGIN \ - listtype **_hook; \ - _list_find_hook(list, *_hook==elt, _hook); \ - list_unlink_athook(list, elt, _hook); \ - MACRO_END - -/* prepend elt to list */ -#define list_prepend(list, elt) \ - MACRO_BEGIN elt->next = list; list = elt; MACRO_END - -/* append elt to list. */ -#define list_append(listtype, list, elt) \ - MACRO_BEGIN \ - listtype **_hook; \ - _list_forall_hook(list, _hook) {} \ - list_insert_athook(elt, _hook); \ - MACRO_END - -/* unlink the first element that satisfies the condition. */ -#define list_unlink_cond(listtype, list, elt, c) \ - MACRO_BEGIN \ - listtype **_hook; \ - list_find2(elt, list, c, _hook); \ - list_unlink_athook(list, elt, _hook); \ - MACRO_END - -/* let elt be the nth element of the list, starting to count from 0. - Return NULL if out of bounds. */ -#define list_nth(elt, list, n) \ - MACRO_BEGIN \ - int _x; /* only evaluate n once */ \ - for (_x=(n), elt=list; _x && elt; _x--, elt=elt->next) {} \ - MACRO_END - -/* let elt be the nth element of the list, starting to count from 0. - Return NULL if out of bounds. */ -#define list_nth_hook(elt, list, n, hook) \ - MACRO_BEGIN \ - int _x; /* only evaluate n once */ \ - for (_x=(n), elt=list, hook=&list; _x && elt; _x--, hook=&elt->next, elt=elt->next) {} \ - MACRO_END - -/* set n to the length of the list */ -#define list_length(listtype, list, n) \ - MACRO_BEGIN \ - listtype *_elt; \ - n=0; \ - list_forall(_elt, list) \ - n++; \ - MACRO_END - -/* set n to the index of the first element satisfying cond, or -1 if - none found. Also set elt to the element, or NULL if none found. */ -#define list_index(list, n, elt, c) \ - MACRO_BEGIN \ - n=0; \ - list_forall(elt, list) { \ - if (c) break; \ - n++; \ - } \ - if (!elt) \ - n=-1; \ - MACRO_END - -/* set n to the number of elements in the list that satisfy condition c */ -#define list_count(list, n, elt, c) \ - MACRO_BEGIN \ - n=0; \ - list_forall(elt, list) { \ - if (c) n++; \ - } \ - MACRO_END - -/* let elt be each element of the list, unlinked. At the end, set list=NULL. */ -#define list_forall_unlink(elt, list) \ - for (elt=list; elt ? (list=elt->next, elt->next=NULL), 1 : 0; elt=list) - -/* reverse a list (efficient) */ -#define list_reverse(listtype, list) \ - MACRO_BEGIN \ - listtype *_list1=NULL, *elt; \ - list_forall_unlink(elt, list) \ - list_prepend(_list1, elt); \ - list = _list1; \ - MACRO_END - -/* insert the element ELT just before the first element TMP of the - list for which COND holds. Here COND must be a condition of ELT and - TMP. Typical usage is to insert an element into an ordered list: - for instance, list_insert_ordered(listtype, list, elt, tmp, - elt->size <= tmp->size). Note: if we give a "less than or equal" - condition, the new element will be inserted just before a sequence - of equal elements. If we give a "less than" condition, the new - element will be inserted just after a list of equal elements. - Note: it is much more efficient to construct a list with - list_prepend and then order it with list_merge_sort, than to - construct it with list_insert_ordered. */ -#define list_insert_ordered(listtype, list, elt, tmp, cond) \ - MACRO_BEGIN \ - listtype **_hook; \ - _list_find_hook(list, (tmp=*_hook, (cond)), _hook); \ - list_insert_athook(elt, _hook); \ - MACRO_END - -/* sort the given list, according to the comparison condition. - Typical usage is list_sort(listtype, list, a, b, a->size < - b->size). Note: if we give "less than or equal" condition, each - segment of equal elements will be reversed in order. If we give a - "less than" condition, each segment of equal elements will retain - the original order. The latter is slower but sometimes - prettier. Average running time: n*n/2. */ -#define list_sort(listtype, list, a, b, cond) \ - MACRO_BEGIN \ - listtype *_newlist=NULL; \ - list_forall_unlink(a, list) \ - list_insert_ordered(listtype, _newlist, a, b, cond); \ - list = _newlist; \ - MACRO_END - -/* a much faster sort algorithm (merge sort, n log n worst case). It - is required that the list type has an additional, unused next1 - component. Note there is no curious reversal of order of equal - elements as for list_sort. */ - -#define list_mergesort(listtype, list, a, b, cond) \ - MACRO_BEGIN \ - listtype *_elt, **_hook1; \ - \ - for (_elt=list; _elt; _elt=_elt->next1) { \ - _elt->next1 = _elt->next; \ - _elt->next = NULL; \ - } \ - do { \ - _hook1 = &(list); \ - while ((a = *_hook1) != NULL && (b = a->next1) != NULL ) { \ - _elt = b->next1; \ - _list_merge_cond(listtype, a, b, cond, *_hook1); \ - _hook1 = &((*_hook1)->next1); \ - *_hook1 = _elt; \ - } \ - } while (_hook1 != &(list)); \ - MACRO_END - -/* merge two sorted lists. Store result at &result */ -#define _list_merge_cond(listtype, a, b, cond, result) \ - MACRO_BEGIN \ - listtype **_hook; \ - _hook = &(result); \ - while (1) { \ - if (a==NULL) { \ - *_hook = b; \ - break; \ - } else if (b==NULL) { \ - *_hook = a; \ - break; \ - } else if (cond) { \ - *_hook = a; \ - _hook = &(a->next); \ - a = a->next; \ - } else { \ - *_hook = b; \ - _hook = &(b->next); \ - b = b->next; \ - } \ - } \ - MACRO_END - -/* ---------------------------------------------------------------------- */ -/* macros for doubly-linked lists */ - -#define dlist_append(head, end, elt) \ - MACRO_BEGIN \ - elt->prev = end; \ - elt->next = NULL; \ - if (end) { \ - end->next = elt; \ - } else { \ - head = elt; \ - } \ - end = elt; \ - MACRO_END - -/* let elt be each element of the list, unlinked. At the end, set list=NULL. */ -#define dlist_forall_unlink(elt, head, end) \ - for (elt=head; elt ? (head=elt->next, elt->next=NULL, elt->prev=NULL), 1 : (end=NULL, 0); elt=head) - -/* unlink the first element of the list */ -#define dlist_unlink_first(head, end, elt) \ - MACRO_BEGIN \ - elt = head; \ - if (head) { \ - head = head->next; \ - if (head) { \ - head->prev = NULL; \ - } else { \ - end = NULL; \ - } \ - elt->prev = NULL; \ - elt->next = NULL; \ - } \ - MACRO_END - -#endif /* _PS_LISTS_H */ - diff --git a/src/trace/potrace/potracelib.cpp b/src/trace/potrace/potracelib.cpp deleted file mode 100644 index b5463d676..000000000 --- a/src/trace/potrace/potracelib.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -#include <stdlib.h> -#include <string.h> -#include <glib.h> - -#include "potracelib.h" -#include "inkscape-version.h" -#include "curve.h" -#include "decompose.h" -#include "trace.h" -#include "progress.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -/* default parameters */ -static const potrace_param_t param_default = { - 2, /* turdsize */ - POTRACE_TURNPOLICY_MINORITY, /* turnpolicy */ - 1.0, /* alphamax */ - 1, /* opticurve */ - 0.2, /* opttolerance */ - { - NULL, /* callback function */ - NULL, /* callback data */ - 0.0, 1.0, /* progress range */ - 0.0, /* granularity */ - }, -}; - -/* Return a fresh copy of the set of default parameters, or NULL on - failure with errno set. */ -potrace_param_t *potrace_param_default(void) { - potrace_param_t *p; - - p = (potrace_param_t *) malloc(sizeof(potrace_param_t)); - if (!p) { - return NULL; - } - memcpy(p, ¶m_default, sizeof(potrace_param_t)); - return p; -} - -/* On success, returns a Potrace state st with st->status == - POTRACE_STATUS_OK. On failure, returns NULL if no Potrace state - could be created (with errno set), or returns an incomplete Potrace - state (with st->status == POTRACE_STATUS_INCOMPLETE, and with errno - set). Complete or incomplete Potrace state can be freed with - potrace_state_free(). */ -potrace_state_t *potrace_trace(const potrace_param_t *param, const potrace_bitmap_t *bm) { - int r; - path_t *plist = NULL; - potrace_state_t *st; - progress_t prog; - progress_t subprog; - - /* prepare private progress bar state */ - prog.callback = param->progress.callback; - prog.data = param->progress.data; - prog.min = param->progress.min; - prog.max = param->progress.max; - prog.epsilon = param->progress.epsilon; - prog.d_prev = param->progress.min; - - /* allocate state object */ - st = (potrace_state_t *)malloc(sizeof(potrace_state_t)); - if (!st) { - return NULL; - } - - progress_subrange_start(0.0, 0.1, &prog, &subprog); - - /* process the image */ - r = bm_to_pathlist(bm, &plist, param, &subprog); - if (r) { - free(st); - return NULL; - } - - st->status = POTRACE_STATUS_OK; - st->plist = plist; - st->priv = NULL; /* private state currently unused */ - - progress_subrange_end(&prog, &subprog); - - progress_subrange_start(0.1, 1.0, &prog, &subprog); - - /* partial success. */ - r = process_path(plist, param, &subprog); - if (r) { - st->status = POTRACE_STATUS_INCOMPLETE; - } - - progress_subrange_end(&prog, &subprog); - - return st; -} - -/* free a Potrace state, without disturbing errno. */ -void potrace_state_free(potrace_state_t *st) { - pathlist_free(st->plist); - free(st); -} - -/* free a parameter list, without disturbing errno. */ -void potrace_param_free(potrace_param_t *p) { - free(p); -} - -char *potrace_version(void) { - static char *ver = g_strdup_printf("potracelib %s", Inkscape::version_string); - return ver; -} - -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/trace/potrace/potracelib.h b/src/trace/potrace/potracelib.h deleted file mode 100644 index 0a6ddbf1f..000000000 --- a/src/trace/potrace/potracelib.h +++ /dev/null @@ -1,139 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -#ifndef POTRACELIB_H -#define POTRACELIB_H - -/* this file defines the API for the core Potrace library. For a more - detailed description of the API, see potracelib.pdf */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* ---------------------------------------------------------------------- */ -/* tracing parameters */ - -/* turn policies */ -#define POTRACE_TURNPOLICY_BLACK 0 -#define POTRACE_TURNPOLICY_WHITE 1 -#define POTRACE_TURNPOLICY_LEFT 2 -#define POTRACE_TURNPOLICY_RIGHT 3 -#define POTRACE_TURNPOLICY_MINORITY 4 -#define POTRACE_TURNPOLICY_MAJORITY 5 -#define POTRACE_TURNPOLICY_RANDOM 6 - -/* structure to hold progress bar callback data */ -struct potrace_progress_s { - void (*callback)(double progress, void *privdata); /* callback fn */ - void *data; /* callback function's private data */ - double min, max; /* desired range of progress, e.g. 0.0 to 1.0 */ - double epsilon; /* granularity: can skip smaller increments */ -}; -typedef struct potrace_progress_s potrace_progress_t; - -/* structure to hold tracing parameters */ -struct potrace_param_s { - int turdsize; /* area of largest path to be ignored */ - int turnpolicy; /* resolves ambiguous turns in path decomposition */ - double alphamax; /* corner threshold */ - int opticurve; /* use curve optimization? */ - double opttolerance; /* curve optimization tolerance */ - potrace_progress_t progress; /* progress callback function */ -}; -typedef struct potrace_param_s potrace_param_t; - -/* ---------------------------------------------------------------------- */ -/* bitmaps */ - -/* native word size */ -typedef unsigned long potrace_word; - -/* Internal bitmap format. The n-th scanline starts at scanline(n) = - (map + n*dy). Raster data is stored as a sequence of potrace_words - (NOT bytes). The leftmost bit of scanline n is the most significant - bit of scanline(n)[0]. */ -struct potrace_bitmap_s { - int w, h; /* width and height, in pixels */ - int dy; /* words per scanline (not bytes) */ - potrace_word *map; /* raw data, dy*h words */ -}; -typedef struct potrace_bitmap_s potrace_bitmap_t; - -/* ---------------------------------------------------------------------- */ -/* curves */ - -/* point */ -struct potrace_dpoint_s { - double x, y; -}; -typedef struct potrace_dpoint_s potrace_dpoint_t; - -/* segment tags */ -#define POTRACE_CURVETO 1 -#define POTRACE_CORNER 2 - -/* closed curve segment */ -struct potrace_curve_s { - int n; /* number of segments */ - int *tag; /* tag[n]: POTRACE_CURVETO or POTRACE_CORNER */ - potrace_dpoint_t (*c)[3]; /* c[n][3]: control points. - c[n][0] is unused for tag[n]=POTRACE_CORNER */ -}; -typedef struct potrace_curve_s potrace_curve_t; - -/* Linked list of signed curve segments. Also carries a tree structure. */ -struct potrace_path_s { - int area; /* area of the bitmap path */ - int sign; /* '+' or '-', depending on orientation */ - potrace_curve_t curve; /* this path's vector data */ - - struct potrace_path_s *next; /* linked list structure */ - - struct potrace_path_s *childlist; /* tree structure */ - struct potrace_path_s *sibling; /* tree structure */ - - struct potrace_privpath_s *priv; /* private state */ -}; -typedef struct potrace_path_s potrace_path_t; - -/* ---------------------------------------------------------------------- */ -/* Potrace state */ - -#define POTRACE_STATUS_OK 0 -#define POTRACE_STATUS_INCOMPLETE 1 - -struct potrace_state_s { - int status; - potrace_path_t *plist; /* vector data */ - - struct potrace_privstate_s *priv; /* private state */ -}; -typedef struct potrace_state_s potrace_state_t; - -/* ---------------------------------------------------------------------- */ -/* API functions */ - -/* get default parameters */ -potrace_param_t *potrace_param_default(void); - -/* free parameter set */ -void potrace_param_free(potrace_param_t *p); - -/* trace a bitmap*/ -potrace_state_t *potrace_trace(const potrace_param_t *param, - const potrace_bitmap_t *bm); - -/* free a Potrace state */ -void potrace_state_free(potrace_state_t *st); - -/* return a static plain text version string identifying this version - of potracelib */ -char *potrace_version(void); - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* POTRACELIB_H */ diff --git a/src/trace/potrace/progress.h b/src/trace/potrace/progress.h deleted file mode 100644 index ecc2ba217..000000000 --- a/src/trace/potrace/progress.h +++ /dev/null @@ -1,77 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -/* operations on potrace_progress_t objects, which are defined in - potracelib.h. Note: the code attempts to minimize runtime overhead - when no progress monitoring was requested. It also tries to - minimize excessive progress calculations beneath the "epsilon" - threshold. */ - -#ifndef PROGRESS_H -#define PROGRESS_H - -/* structure to hold progress bar callback data */ -struct progress_s { - void (*callback)(double progress, void *privdata); /* callback fn */ - void *data; /* callback function's private data */ - double min, max; /* desired range of progress, e.g. 0.0 to 1.0 */ - double epsilon; /* granularity: can skip smaller increments */ - double b; /* upper limit of subrange in superrange units */ - double d_prev; /* previous value of d */ -}; -typedef struct progress_s progress_t; - -/* notify given progress object of current progress. Note that d is - given in the 0.0-1.0 range, which will be scaled and translated to - the progress object's range. */ -static inline void progress_update(double d, progress_t *prog) { - if (prog != NULL && prog->callback != NULL) { - double d_scaled = prog->min * (1-d) + prog->max * d; - if (d == 1.0 || d_scaled >= prog->d_prev + prog->epsilon) { - prog->callback(prog->min * (1-d) + prog->max * d, prog->data); - prog->d_prev = d_scaled; - } - } -} - -/* start a subrange of the given progress object. The range is - narrowed to [a..b], relative to 0.0-1.0 coordinates. If new range - is below granularity threshold, disable further subdivisions. */ -static inline void progress_subrange_start(double a, double b, const progress_t *prog, progress_t *sub) { - double min, max; - - if (prog == NULL || prog->callback == NULL) { - sub->callback = NULL; - return; - } - - min = prog->min * (1-a) + prog->max * a; - max = prog->min * (1-b) + prog->max * b; - - if (max - min < prog->epsilon) { - sub->callback = NULL; /* no further progress info in subrange */ - sub->b = b; - return; - } - sub->callback = prog->callback; - sub->data = prog->data; - sub->epsilon = prog->epsilon; - sub->min = min; - sub->max = max; - sub->d_prev = prog->d_prev; - return; -} - -static inline void progress_subrange_end(progress_t *prog, progress_t *sub) { - if (prog != NULL && prog->callback != NULL) { - if (sub->callback == NULL) { - progress_update(sub->b, prog); - } else { - prog->d_prev = sub->d_prev; - } - } -} - -#endif /* PROGRESS_H */ - diff --git a/src/trace/potrace/render.cpp b/src/trace/potrace/render.cpp deleted file mode 100644 index 4f44ae6a6..000000000 --- a/src/trace/potrace/render.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - - -#include <stdio.h> -#include <stdlib.h> -#include <math.h> -#include <string.h> - -#include "render.h" -#include "greymap.h" -#include "auxiliary.h" - -/* ---------------------------------------------------------------------- */ -/* routines for anti-aliased rendering of curves */ - -/* we use the following method. Given a point (x,y) (with real-valued - coordinates) in the plane, let (xi,yi) be the integer part of the - coordinates, i.e., xi=floor(x), yi=floor(y). Define a path from - (x,y) to infinity as follows: path(x,y) = - (x,y)--(xi+1,y)--(xi+1,yi)--(+infty,yi). Now as the point (x,y) - moves smoothly across the plane, the path path(x,y) sweeps - (non-smoothly) across a certain area. We proportionately blacken - the area as the path moves "downward", and we whiten the area as - the path moves "upward". This way, after the point has traversed a - closed curve, the interior of the curve has been darkened - (counterclockwise movement) or lightened (clockwise movement). (The - "grey shift" is actually proportional to the winding number). By - choosing the above path with mostly integer coordinates, we achieve - that only pixels close to (x,y) receive grey values and are subject - to round-off errors. The grey value of pixels far away from (x,y) - is always in "integer" (where 0=black, 1=white). As a special - trick, we keep an accumulator rm->a1, which holds a double value to - be added to the grey value to be added to the current pixel - (xi,yi). Only when changing "current" pixels, we convert this - double value to an integer. This way we avoid round-off errors at - the meeting points of line segments. Another speedup measure is - that we sometimes use the rm->incrow_buf array to postpone - incrementing or decrementing an entire row. If incrow_buf[y]=x+1!=0, - then all the pixels (x,y),(x+1,y),(x+2,y),... are scheduled to be - incremented/decremented (which one is the case will be clear from - context). This keeps the greymap operations reasonably local. */ - -/* allocate a new rendering state */ -render_t *render_new(greymap_t *gm) { - render_t *rm; - - rm = (render_t *) malloc(sizeof(render_t)); - if (!rm) { - return NULL; - } - memset(rm, 0, sizeof(render_t)); - rm->gm = gm; - rm->incrow_buf = (int *) calloc(gm->h, sizeof(int)); - if (!rm->incrow_buf) { - free(rm); - return NULL; - } - memset(rm->incrow_buf, 0, gm->h * sizeof(int)); - return rm; -} - -/* free a given rendering state. Note: this does not free the - underlying greymap. */ -void render_free(render_t *rm) { - free(rm->incrow_buf); - free(rm); -} - -/* close path */ -void render_close(render_t *rm) { - if (rm->x0 != rm->x1 || rm->y0 != rm->y1) { - render_lineto(rm, rm->x0, rm->y0); - } - GM_INC(rm->gm, rm->x0i, rm->y0i, (rm->a0+rm->a1)*255); - - /* assert (rm->x0i != rm->x1i || rm->y0i != rm->y1i); */ - - /* the persistent state is now undefined */ -} - -/* move point */ -void render_moveto(render_t *rm, double x, double y) { - /* close the previous path */ - render_close(rm); - - rm->x0 = rm->x1 = x; - rm->y0 = rm->y1 = y; - rm->x0i = (int)floor(rm->x0); - rm->x1i = (int)floor(rm->x1); - rm->y0i = (int)floor(rm->y0); - rm->y1i = (int)floor(rm->y1); - rm->a0 = rm->a1 = 0; -} - -/* add b to pixels (x,y) and all pixels to the right of it. However, - use rm->incrow_buf as a buffer to economize on multiple calls */ -static void incrow(render_t *rm, int x, int y, int b) { - int i, x0; - - if (y < 0 || y >= rm->gm->h) { - return; - } - - if (x < 0) { - x = 0; - } else if (x > rm->gm->w) { - x = rm->gm->w; - } - if (rm->incrow_buf[y] == 0) { - rm->incrow_buf[y] = x+1; /* store x+1 so that we can use 0 for "vacant" */ - return; - } - x0 = rm->incrow_buf[y]-1; - rm->incrow_buf[y] = 0; - if (x0 < x) { - for (i=x0; i<x; i++) { - GM_INC(rm->gm, i, y, -b); - } - } else { - for (i=x; i<x0; i++) { - GM_INC(rm->gm, i, y, b); - } - } -} - -/* render a straight line */ -void render_lineto(render_t *rm, double x2, double y2) { - int x2i, y2i; - double t0=2, s0=2; - int sn, tn; - double ss=2, ts=2; - double r0, r1; - int i, j; - int rxi, ryi; - int s; - - x2i = (int)floor(x2); - y2i = (int)floor(y2); - - sn = abs(x2i - rm->x1i); - tn = abs(y2i - rm->y1i); - - if (sn) { - s0 = ((x2>rm->x1 ? rm->x1i+1 : rm->x1i) - rm->x1)/(x2-rm->x1); - ss = fabs(1.0/(x2-rm->x1)); - } - if (tn) { - t0 = ((y2>rm->y1 ? rm->y1i+1 : rm->y1i) - rm->y1)/(y2-rm->y1); - ts = fabs(1.0/(y2-rm->y1)); - } - - r0 = 0; - - i = 0; - j = 0; - - rxi = rm->x1i; - ryi = rm->y1i; - - while (i<sn || j<tn) { - if (j>=tn || (i<sn && s0+i*ss < t0+j*ts)) { - r1 = s0+i*ss; - i++; - s = 1; - } else { - r1 = t0+j*ts; - j++; - s = 0; - } - /* render line from r0 to r1 segment of (rm->x1,rm->y1)..(x2,y2) */ - - /* move point to r1 */ - rm->a1 += (r1-r0)*(y2-rm->y1)*(rxi+1-((r0+r1)/2.0*(x2-rm->x1)+rm->x1)); - - /* move point across pixel boundary */ - if (s && x2>rm->x1) { - GM_INC(rm->gm, rxi, ryi, rm->a1*255); - rm->a1 = 0; - rxi++; - rm->a1 += rm->y1+r1*(y2-rm->y1)-ryi; - } else if (!s && y2>rm->y1) { - GM_INC(rm->gm, rxi, ryi, rm->a1*255); - rm->a1 = 0; - incrow(rm, rxi+1, ryi, 255); - ryi++; - } else if (s && x2<=rm->x1) { - rm->a1 -= rm->y1+r1*(y2-rm->y1)-ryi; - GM_INC(rm->gm, rxi, ryi, rm->a1*255); - rm->a1 = 0; - rxi--; - } else if (!s && y2<=rm->y1) { - GM_INC(rm->gm, rxi, ryi, rm->a1*255); - rm->a1 = 0; - ryi--; - incrow(rm, rxi+1, ryi, -255); - } - - r0 = r1; - } - - /* move point to (x2,y2) */ - - r1 = 1; - rm->a1 += (r1-r0)*(y2-rm->y1)*(rxi+1-((r0+r1)/2.0*(x2-rm->x1)+rm->x1)); - - rm->x1i = x2i; - rm->y1i = y2i; - rm->x1 = x2; - rm->y1 = y2; - - /* assert (rxi != rm->x1i || ryi != rm->y1i); */ -} - -/* render a Bezier curve. */ -void render_curveto(render_t *rm, double x2, double y2, double x3, double y3, double x4, double y4) { - double x1, y1, dd0, dd1, dd, delta, e2, epsilon, t; - - x1 = rm->x1; /* starting point */ - y1 = rm->y1; - - /* we approximate the curve by small line segments. The interval - size, epsilon, is determined on the fly so that the distance - between the true curve and its approximation does not exceed the - desired accuracy delta. */ - - delta = .1; /* desired accuracy, in pixels */ - - /* let dd = maximal value of 2nd derivative over curve - this must - occur at an endpoint. */ - dd0 = sq(x1-2*x2+x3) + sq(y1-2*y2+y3); - dd1 = sq(x2-2*x3+x4) + sq(y2-2*y3+y4); - dd = 6*sqrt(max(dd0, dd1)); - e2 = 8*delta <= dd ? 8*delta/dd : 1; - epsilon = sqrt(e2); /* necessary interval size */ - - for (t=epsilon; t<1; t+=epsilon) { - render_lineto(rm, x1*cu(1-t)+3*x2*sq(1-t)*t+3*x3*(1-t)*sq(t)+x4*cu(t), - y1*cu(1-t)+3*y2*sq(1-t)*t+3*y3*(1-t)*sq(t)+y4*cu(t)); - } - render_lineto(rm, x4, y4); -} diff --git a/src/trace/potrace/render.h b/src/trace/potrace/render.h deleted file mode 100644 index 0caaedff6..000000000 --- a/src/trace/potrace/render.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - - -#ifndef RENDER_H -#define RENDER_H - -#include "greymap.h" - -struct render_s { - greymap_t *gm; - double x0, y0, x1, y1; - int x0i, y0i, x1i, y1i; - double a0, a1; - int *incrow_buf; -}; -typedef struct render_s render_t; - -render_t *render_new(greymap_t *gm); -void render_free(render_t *rm); -void render_close(render_t *rm); -void render_moveto(render_t *rm, double x, double y); -void render_lineto(render_t *rm, double x, double y); -void render_curveto(render_t *rm, double x2, double y2, double x3, double y3, double x4, double y4); - -#endif /* RENDER_H */ diff --git a/src/trace/potrace/trace.cpp b/src/trace/potrace/trace.cpp deleted file mode 100644 index 469262b67..000000000 --- a/src/trace/potrace/trace.cpp +++ /dev/null @@ -1,1245 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -/* transform jaggy paths into smooth curves */ - -#include <stdio.h> -#include <math.h> -#include <stdlib.h> -#include <string.h> - -#include "potracelib.h" -#include "curve.h" -#include "lists.h" -#include "auxiliary.h" -#include "trace.h" -#include "progress.h" - -#define INFTY 10000000 /* it suffices that this is longer than any - path; it need not be really infinite */ -#define COS179 -0.999847695156 /* the cosine of 179 degrees */ - -/* ---------------------------------------------------------------------- */ -#define SAFE_CALLOC(var, n, typ) \ - if ((var = (typ *)calloc(n, sizeof(typ))) == NULL) goto calloc_error - -/* ---------------------------------------------------------------------- */ -/* auxiliary functions */ - -/* return a direction that is 90 degrees counterclockwise from p2-p0, - but then restricted to one of the major wind directions (n, nw, w, etc) */ -static inline point_t dorth_infty(dpoint_t p0, dpoint_t p2) { - point_t r; - - r.y = sign(p2.x-p0.x); - r.x = -sign(p2.y-p0.y); - - return r; -} - -/* return (p1-p0)x(p2-p0), the area of the parallelogram */ -static inline double dpara(dpoint_t p0, dpoint_t p1, dpoint_t p2) { - double x1, y1, x2, y2; - - x1 = p1.x-p0.x; - y1 = p1.y-p0.y; - x2 = p2.x-p0.x; - y2 = p2.y-p0.y; - - return x1*y2 - x2*y1; -} - -/* ddenom/dpara have the property that the square of radius 1 centered - at p1 intersects the line p0p2 iff |dpara(p0,p1,p2)| <= ddenom(p0,p2) */ -static inline double ddenom(dpoint_t p0, dpoint_t p2) { - point_t r = dorth_infty(p0, p2); - - return r.y*(p2.x-p0.x) - r.x*(p2.y-p0.y); -} - -/* return 1 if a <= b < c < a, in a cyclic sense (mod n) */ -static inline int cyclic(int a, int b, int c) { - if (a<=c) { - return (a<=b && b<c); - } else { - return (a<=b || b<c); - } -} - -/* determine the center and slope of the line i..j. Assume i<j. Needs - "sum" components of p to be set. */ -static void pointslope(privpath_t *pp, int i, int j, dpoint_t *ctr, dpoint_t *dir) { - /* assume i<j */ - - int n = pp->len; - sums_t *sums = pp->sums; - - double x, y, x2, xy, y2; - double k; - double a, b, c, lambda2, l; - int r=0; /* rotations from i to j */ - - while (j>=n) { - j-=n; - r+=1; - } - while (i>=n) { - i-=n; - r-=1; - } - while (j<0) { - j+=n; - r-=1; - } - while (i<0) { - i+=n; - r+=1; - } - - x = sums[j+1].x-sums[i].x+r*sums[n].x; - y = sums[j+1].y-sums[i].y+r*sums[n].y; - x2 = sums[j+1].x2-sums[i].x2+r*sums[n].x2; - xy = sums[j+1].xy-sums[i].xy+r*sums[n].xy; - y2 = sums[j+1].y2-sums[i].y2+r*sums[n].y2; - k = j+1-i+r*n; - - ctr->x = x/k; - ctr->y = y/k; - - a = (x2-(double)x*x/k)/k; - b = (xy-(double)x*y/k)/k; - c = (y2-(double)y*y/k)/k; - - lambda2 = (a+c+sqrt((a-c)*(a-c)+4*b*b))/2; /* larger e.value */ - - /* now find e.vector for lambda2 */ - a -= lambda2; - c -= lambda2; - - if (fabs(a) >= fabs(c)) { - l = sqrt(a*a+b*b); - if (l!=0) { - dir->x = -b/l; - dir->y = a/l; - } - } else { - l = sqrt(c*c+b*b); - if (l!=0) { - dir->x = -c/l; - dir->y = b/l; - } - } - if (l==0) { - dir->x = dir->y = 0; /* sometimes this can happen when k=4: - the two eigenvalues coincide */ - } -} - -/* the type of (affine) quadratic forms, represented as symmetric 3x3 - matrices. The value of the quadratic form at a vector (x,y) is v^t - Q v, where v = (x,y,1)^t. */ -typedef double quadform_t[3][3]; - -/* Apply quadratic form Q to vector w = (w.x,w.y) */ -static inline double quadform(quadform_t Q, dpoint_t w) { - double v[3]; - int i, j; - double sum; - - v[0] = w.x; - v[1] = w.y; - v[2] = 1; - sum = 0.0; - - for (i=0; i<3; i++) { - for (j=0; j<3; j++) { - sum += v[i] * Q[i][j] * v[j]; - } - } - return sum; -} - -/* calculate p1 x p2 */ -static inline int xprod(point_t p1, point_t p2) { - return p1.x*p2.y - p1.y*p2.x; -} - -/* calculate (p1-p0)x(p3-p2) */ -static inline double cprod(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { - double x1, y1, x2, y2; - - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p3.x - p2.x; - y2 = p3.y - p2.y; - - return x1*y2 - x2*y1; -} - -/* calculate (p1-p0)*(p2-p0) */ -static inline double iprod(dpoint_t p0, dpoint_t p1, dpoint_t p2) { - double x1, y1, x2, y2; - - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p2.x - p0.x; - y2 = p2.y - p0.y; - - return x1*x2 + y1*y2; -} - -/* calculate (p1-p0)*(p3-p2) */ -static inline double iprod1(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { - double x1, y1, x2, y2; - - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p3.x - p2.x; - y2 = p3.y - p2.y; - - return x1*x2 + y1*y2; -} - -/* calculate distance between two points */ -static inline double ddist(dpoint_t p, dpoint_t q) { - return sqrt(sq(p.x-q.x)+sq(p.y-q.y)); -} - -/* calculate point of a bezier curve */ -static inline dpoint_t bezier(double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { - double s = 1-t; - dpoint_t res; - - /* Note: a good optimizing compiler (such as gcc-3) reduces the - following to 16 multiplications, using common subexpression - elimination. */ - - res.x = s*s*s*p0.x + 3*(s*s*t)*p1.x + 3*(t*t*s)*p2.x + t*t*t*p3.x; - res.y = s*s*s*p0.y + 3*(s*s*t)*p1.y + 3*(t*t*s)*p2.y + t*t*t*p3.y; - - return res; -} - -/* calculate the point t in [0..1] on the (convex) bezier curve - (p0,p1,p2,p3) which is tangent to q1-q0. Return -1.0 if there is no - solution in [0..1]. */ -static double tangent(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3, dpoint_t q0, dpoint_t q1) { - double A, B, C; /* (1-t)^2 A + 2(1-t)t B + t^2 C = 0 */ - double a, b, c; /* a t^2 + b t + c = 0 */ - double d, s, r1, r2; - - A = cprod(p0, p1, q0, q1); - B = cprod(p1, p2, q0, q1); - C = cprod(p2, p3, q0, q1); - - a = A - 2*B + C; - b = -2*A + 2*B; - c = A; - - d = b*b - 4*a*c; - - if (a==0 || d<0) { - return -1.0; - } - - s = sqrt(d); - - r1 = (-b + s) / (2 * a); - r2 = (-b - s) / (2 * a); - - if (r1 >= 0 && r1 <= 1) { - return r1; - } else if (r2 >= 0 && r2 <= 1) { - return r2; - } else { - return -1.0; - } -} - -/* ---------------------------------------------------------------------- */ -/* Preparation: fill in the sum* fields of a path (used for later - rapid summing). Return 0 on success, 1 with errno set on - failure. */ -static int calc_sums(privpath_t *pp) { - int i, x, y; - int n = pp->len; - - SAFE_CALLOC(pp->sums, pp->len+1, sums_t); - - /* origin */ - pp->x0 = pp->pt[0].x; - pp->y0 = pp->pt[0].y; - - /* preparatory computation for later fast summing */ - pp->sums[0].x2 = pp->sums[0].xy = pp->sums[0].y2 = pp->sums[0].x = pp->sums[0].y = 0; - for (i=0; i<n; i++) { - x = pp->pt[i].x - pp->x0; - y = pp->pt[i].y - pp->y0; - pp->sums[i+1].x = pp->sums[i].x + x; - pp->sums[i+1].y = pp->sums[i].y + y; - pp->sums[i+1].x2 = pp->sums[i].x2 + x*x; - pp->sums[i+1].xy = pp->sums[i].xy + x*y; - pp->sums[i+1].y2 = pp->sums[i].y2 + y*y; - } - return 0; - - calloc_error: - return 1; -} - -/* ---------------------------------------------------------------------- */ -/* Stage 1: determine the straight subpaths (Sec. 2.2.1). Fill in the - "lon" component of a path object (based on pt/len). For each i, - lon[i] is the furthest index such that a straight line can be drawn - from i to lon[i]. Return 1 on error with errno set, else 0. */ - -/* this algorithm depends on the fact that the existence of straight - subpaths is a triplewise property. I.e., there exists a straight - line through squares i0,...,in iff there exists a straight line - through i,j,k, for all i0<=i<j<k<=in. (Proof?) */ - -/* this implementation of calc_lon is O(n^2). It replaces an older - O(n^3) version. A "constraint" means that future points must - satisfy xprod(constraint[0], cur) >= 0 and xprod(constraint[1], - cur) <= 0. */ - -/* Remark for Potrace 1.1: the current implementation of calc_lon is - more complex than the implementation found in Potrace 1.0, but it - is considerably faster. The introduction of the "nc" data structure - means that we only have to test the constraints for "corner" - points. On a typical input file, this speeds up the calc_lon - function by a factor of 31.2, thereby decreasing its time share - within the overall Potrace algorithm from 72.6% to 7.82%, and - speeding up the overall algorithm by a factor of 3.36. On another - input file, calc_lon was sped up by a factor of 6.7, decreasing its - time share from 51.4% to 13.61%, and speeding up the overall - algorithm by a factor of 1.78. In any case, the savings are - substantial. */ - -/* returns 0 on success, 1 on error with errno set */ -static int calc_lon(privpath_t *pp) { - point_t *pt = pp->pt; - int n = pp->len; - int i, j, k, k1; - int ct[4], dir; - point_t constraint[2]; - point_t cur; - point_t off; - int *pivk = NULL; /* pivk[n] */ - int *nc = NULL; /* nc[n]: next corner */ - point_t dk; /* direction of k-k1 */ - int a, b, c, d; - - SAFE_CALLOC(pivk, n, int); - SAFE_CALLOC(nc, n, int); - - /* initialize the nc data structure. Point from each point to the - furthest future point to which it is connected by a vertical or - horizontal segment. We take advantage of the fact that there is - always a direction change at 0 (due to the path decomposition - algorithm). But even if this were not so, there is no harm, as - in practice, correctness does not depend on the word "furthest" - above. */ - k = 0; - for (i=n-1; i>=0; i--) { - if (pt[i].x != pt[k].x && pt[i].y != pt[k].y) { - k = i+1; /* necessarily i<n-1 in this case */ - } - nc[i] = k; - } - - SAFE_CALLOC(pp->lon, n, int); - - /* determine pivot points: for each i, let pivk[i] be the furthest k - such that all j with i<j<k lie on a line connecting i,k. */ - - for (i=n-1; i>=0; i--) { - ct[0] = ct[1] = ct[2] = ct[3] = 0; - - /* keep track of "directions" that have occurred */ - dir = (3+3*(pt[mod(i+1,n)].x-pt[i].x)+(pt[mod(i+1,n)].y-pt[i].y))/2; - ct[dir]++; - - constraint[0].x = 0; - constraint[0].y = 0; - constraint[1].x = 0; - constraint[1].y = 0; - - /* find the next k such that no straight line from i to k */ - k = nc[i]; - k1 = i; - while (1) { - - dir = (3+3*sign(pt[k].x-pt[k1].x)+sign(pt[k].y-pt[k1].y))/2; - ct[dir]++; - - /* if all four "directions" have occurred, cut this path */ - if (ct[0] && ct[1] && ct[2] && ct[3]) { - pivk[i] = k1; - goto foundk; - } - - cur.x = pt[k].x - pt[i].x; - cur.y = pt[k].y - pt[i].y; - - /* see if current constraint is violated */ - if (xprod(constraint[0], cur) < 0 || xprod(constraint[1], cur) > 0) { - goto constraint_viol; - } - - /* else, update constraint */ - if (abs(cur.x) <= 1 && abs(cur.y) <= 1) { - /* no constraint */ - } else { - off.x = cur.x + ((cur.y>=0 && (cur.y>0 || cur.x<0)) ? 1 : -1); - off.y = cur.y + ((cur.x<=0 && (cur.x<0 || cur.y<0)) ? 1 : -1); - if (xprod(constraint[0], off) >= 0) { - constraint[0] = off; - } - off.x = cur.x + ((cur.y<=0 && (cur.y<0 || cur.x<0)) ? 1 : -1); - off.y = cur.y + ((cur.x>=0 && (cur.x>0 || cur.y<0)) ? 1 : -1); - if (xprod(constraint[1], off) <= 0) { - constraint[1] = off; - } - } - k1 = k; - k = nc[k1]; - if (!cyclic(k,i,k1)) { - break; - } - } - constraint_viol: - /* k1 was the last "corner" satisfying the current constraint, and - k is the first one violating it. We now need to find the last - point along k1..k which satisfied the constraint. */ - dk.x = sign(pt[k].x-pt[k1].x); - dk.y = sign(pt[k].y-pt[k1].y); - cur.x = pt[k1].x - pt[i].x; - cur.y = pt[k1].y - pt[i].y; - /* find largest integer j such that xprod(constraint[0], cur+j*dk) - >= 0 and xprod(constraint[1], cur+j*dk) <= 0. Use bilinearity - of xprod. */ - a = xprod(constraint[0], cur); - b = xprod(constraint[0], dk); - c = xprod(constraint[1], cur); - d = xprod(constraint[1], dk); - /* find largest integer j such that a+j*b>=0 and c+j*d<=0. This - can be solved with integer arithmetic. */ - j = INFTY; - if (b<0) { - j = floordiv(a,-b); - } - if (d>0) { - j = min(j, floordiv(-c,d)); - } - pivk[i] = mod(k1+j,n); - foundk: - ; - } /* for i */ - - /* clean up: for each i, let lon[i] be the largest k such that for - all i' with i<=i'<k, i'<k<=pivk[i']. */ - - j=pivk[n-1]; - pp->lon[n-1]=j; - for (i=n-2; i>=0; i--) { - if (cyclic(i+1,pivk[i],j)) { - j=pivk[i]; - } - pp->lon[i]=j; - } - - for (i=n-1; cyclic(mod(i+1,n),j,pp->lon[i]); i--) { - pp->lon[i] = j; - } - - free(pivk); - free(nc); - return 0; - - calloc_error: - free(pivk); - free(nc); - return 1; -} - - -/* ---------------------------------------------------------------------- */ -/* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). */ - -/* Auxiliary function: calculate the penalty of an edge from i to j in - the given path. This needs the "lon" and "sum*" data. */ - -static double penalty3(privpath_t *pp, int i, int j) { - int n = pp->len; - point_t *pt = pp->pt; - sums_t *sums = pp->sums; - - /* assume 0<=i<j<=n */ - double x, y, x2, xy, y2; - double k; - double a, b, c, s; - double px, py, ex, ey; - - int r = 0; /* rotations from i to j */ - - if (j>=n) { - j -= n; - r = 1; - } - - /* critical inner loop: the "if" gives a 4.6 percent speedup */ - if (r == 0) { - x = sums[j+1].x - sums[i].x; - y = sums[j+1].y - sums[i].y; - x2 = sums[j+1].x2 - sums[i].x2; - xy = sums[j+1].xy - sums[i].xy; - y2 = sums[j+1].y2 - sums[i].y2; - k = j+1 - i; - } else { - x = sums[j+1].x - sums[i].x + sums[n].x; - y = sums[j+1].y - sums[i].y + sums[n].y; - x2 = sums[j+1].x2 - sums[i].x2 + sums[n].x2; - xy = sums[j+1].xy - sums[i].xy + sums[n].xy; - y2 = sums[j+1].y2 - sums[i].y2 + sums[n].y2; - k = j+1 - i + n; - } - - px = (pt[i].x + pt[j].x) / 2.0 - pt[0].x; - py = (pt[i].y + pt[j].y) / 2.0 - pt[0].y; - ey = (pt[j].x - pt[i].x); - ex = -(pt[j].y - pt[i].y); - - a = ((x2 - 2*x*px) / k + px*px); - b = ((xy - x*py - y*px) / k + px*py); - c = ((y2 - 2*y*py) / k + py*py); - - s = ex*ex*a + 2*ex*ey*b + ey*ey*c; - - return sqrt(s); -} - -/* find the optimal polygon. Fill in the m and po components. Return 1 - on failure with errno set, else 0. Non-cyclic version: assumes i=0 - is in the polygon. Fixme: implement cyclic version. */ -static int bestpolygon(privpath_t *pp) -{ - int i, j, m, k; - int n = pp->len; - double *pen = NULL; /* pen[n+1]: penalty vector */ - int *prev = NULL; /* prev[n+1]: best path pointer vector */ - int *clip0 = NULL; /* clip0[n]: longest segment pointer, non-cyclic */ - int *clip1 = NULL; /* clip1[n+1]: backwards segment pointer, non-cyclic */ - int *seg0 = NULL; /* seg0[m+1]: forward segment bounds, m<=n */ - int *seg1 = NULL; /* seg1[m+1]: backward segment bounds, m<=n */ - double thispen; - double best; - int c; - - SAFE_CALLOC(pen, n+1, double); - SAFE_CALLOC(prev, n+1, int); - SAFE_CALLOC(clip0, n, int); - SAFE_CALLOC(clip1, n+1, int); - SAFE_CALLOC(seg0, n+1, int); - SAFE_CALLOC(seg1, n+1, int); - - /* calculate clipped paths */ - for (i=0; i<n; i++) { - c = mod(pp->lon[mod(i-1,n)]-1,n); - if (c == i) { - c = mod(i+1,n); - } - if (c < i) { - clip0[i] = n; - } else { - clip0[i] = c; - } - } - - /* calculate backwards path clipping, non-cyclic. j <= clip0[i] iff - clip1[j] <= i, for i,j=0..n. */ - j = 1; - for (i=0; i<n; i++) { - while (j <= clip0[i]) { - clip1[j] = i; - j++; - } - } - - /* calculate seg0[j] = longest path from 0 with j segments */ - i = 0; - for (j=0; i<n; j++) { - seg0[j] = i; - i = clip0[i]; - } - seg0[j] = n; - m = j; - - /* calculate seg1[j] = longest path to n with m-j segments */ - i = n; - for (j=m; j>0; j--) { - seg1[j] = i; - i = clip1[i]; - } - seg1[0] = 0; - - /* now find the shortest path with m segments, based on penalty3 */ - /* note: the outer 2 loops jointly have at most n iterations, thus - the worst-case behavior here is quadratic. In practice, it is - close to linear since the inner loop tends to be short. */ - pen[0]=0; - for (j=1; j<=m; j++) { - for (i=seg1[j]; i<=seg0[j]; i++) { - best = -1; - for (k=seg0[j-1]; k>=clip1[i]; k--) { - thispen = penalty3(pp, k, i) + pen[k]; - if (best < 0 || thispen < best) { - prev[i] = k; - best = thispen; - } - } - pen[i] = best; - } - } - - pp->m = m; - SAFE_CALLOC(pp->po, m, int); - - /* read off shortest path */ - for (i=n, j=m-1; i>0; j--) { - i = prev[i]; - pp->po[j] = i; - } - - free(pen); - free(prev); - free(clip0); - free(clip1); - free(seg0); - free(seg1); - return 0; - - calloc_error: - free(pen); - free(prev); - free(clip0); - free(clip1); - free(seg0); - free(seg1); - return 1; -} - -/* ---------------------------------------------------------------------- */ -/* Stage 3: vertex adjustment (Sec. 2.3.1). */ - -/* Adjust vertices of optimal polygon: calculate the intersection of - the two "optimal" line segments, then move it into the unit square - if it lies outside. Return 1 with errno set on error; 0 on - success. */ - -static int adjust_vertices(privpath_t *pp) { - int m = pp->m; - int *po = pp->po; - int n = pp->len; - point_t *pt = pp->pt; - int x0 = pp->x0; - int y0 = pp->y0; - - dpoint_t *ctr = NULL; /* ctr[m] */ - dpoint_t *dir = NULL; /* dir[m] */ - quadform_t *q = NULL; /* q[m] */ - double v[3]; - double d; - int i, j, k, l; - dpoint_t s; - int r; - - SAFE_CALLOC(ctr, m, dpoint_t); - SAFE_CALLOC(dir, m, dpoint_t); - SAFE_CALLOC(q, m, quadform_t); - - r = privcurve_init(&pp->curve, m); - if (r) { - goto calloc_error; - } - - /* calculate "optimal" point-slope representation for each line - segment */ - for (i=0; i<m; i++) { - j = po[mod(i+1,m)]; - j = mod(j-po[i],n)+po[i]; - pointslope(pp, po[i], j, &ctr[i], &dir[i]); - } - - /* represent each line segment as a singular quadratic form; the - distance of a point (x,y) from the line segment will be - (x,y,1)Q(x,y,1)^t, where Q=q[i]. */ - for (i=0; i<m; i++) { - d = sq(dir[i].x) + sq(dir[i].y); - if (d == 0.0) { - for (j=0; j<3; j++) { - for (k=0; k<3; k++) { - q[i][j][k] = 0; - } - } - } else { - v[0] = dir[i].y; - v[1] = -dir[i].x; - v[2] = - v[1] * ctr[i].y - v[0] * ctr[i].x; - for (l=0; l<3; l++) { - for (k=0; k<3; k++) { - q[i][l][k] = v[l] * v[k] / d; - } - } - } - } - - /* now calculate the "intersections" of consecutive segments. - Instead of using the actual intersection, we find the point - within a given unit square which minimizes the square distance to - the two lines. */ - for (i=0; i<m; i++) { - quadform_t Q; - dpoint_t w; - double dx, dy; - double det; - double min, cand; /* minimum and candidate for minimum of quad. form */ - double xmin, ymin; /* coordinates of minimum */ - int z; - - /* let s be the vertex, in coordinates relative to x0/y0 */ - s.x = pt[po[i]].x-x0; - s.y = pt[po[i]].y-y0; - - /* intersect segments i-1 and i */ - - j = mod(i-1,m); - - /* add quadratic forms */ - for (l=0; l<3; l++) { - for (k=0; k<3; k++) { - Q[l][k] = q[j][l][k] + q[i][l][k]; - } - } - - while(1) { - /* minimize the quadratic form Q on the unit square */ - /* find intersection */ - -#ifdef HAVE_GCC_LOOP_BUG - /* work around gcc bug #12243 */ - free(NULL); -#endif - - det = Q[0][0]*Q[1][1] - Q[0][1]*Q[1][0]; - if (det != 0.0) { - w.x = (-Q[0][2]*Q[1][1] + Q[1][2]*Q[0][1]) / det; - w.y = ( Q[0][2]*Q[1][0] - Q[1][2]*Q[0][0]) / det; - break; - } - - /* matrix is singular - lines are parallel. Add another, - orthogonal axis, through the center of the unit square */ - if (Q[0][0]>Q[1][1]) { - v[0] = -Q[0][1]; - v[1] = Q[0][0]; - } else if (Q[1][1]) { - v[0] = -Q[1][1]; - v[1] = Q[1][0]; - } else { - v[0] = 1; - v[1] = 0; - } - d = sq(v[0]) + sq(v[1]); - v[2] = - v[1] * s.y - v[0] * s.x; - for (l=0; l<3; l++) { - for (k=0; k<3; k++) { - Q[l][k] += v[l] * v[k] / d; - } - } - } - dx = fabs(w.x-s.x); - dy = fabs(w.y-s.y); - if (dx <= .5 && dy <= .5) { - pp->curve.vertex[i].x = w.x+x0; - pp->curve.vertex[i].y = w.y+y0; - continue; - } - - /* the minimum was not in the unit square; now minimize quadratic - on boundary of square */ - min = quadform(Q, s); - xmin = s.x; - ymin = s.y; - - if (Q[0][0] == 0.0) { - goto fixx; - } - for (z=0; z<2; z++) { /* value of the y-coordinate */ - w.y = s.y-0.5+z; - w.x = - (Q[0][1] * w.y + Q[0][2]) / Q[0][0]; - dx = fabs(w.x-s.x); - cand = quadform(Q, w); - if (dx <= .5 && cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - fixx: - if (Q[1][1] == 0.0) { - goto corners; - } - for (z=0; z<2; z++) { /* value of the x-coordinate */ - w.x = s.x-0.5+z; - w.y = - (Q[1][0] * w.x + Q[1][2]) / Q[1][1]; - dy = fabs(w.y-s.y); - cand = quadform(Q, w); - if (dy <= .5 && cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - corners: - /* check four corners */ - for (l=0; l<2; l++) { - for (k=0; k<2; k++) { - w.x = s.x-0.5+l; - w.y = s.y-0.5+k; - cand = quadform(Q, w); - if (cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - } - - pp->curve.vertex[i].x = xmin + x0; - pp->curve.vertex[i].y = ymin + y0; - continue; - } - - free(ctr); - free(dir); - free(q); - return 0; - - calloc_error: - free(ctr); - free(dir); - free(q); - return 1; -} - -/* ---------------------------------------------------------------------- */ -/* Stage 4: smoothing and corner analysis (Sec. 2.3.3) */ - -/* reverse orientation of a path */ -static void reverse(privcurve_t *curve) { - int m = curve->n; - int i, j; - dpoint_t tmp; - - for (i=0, j=m-1; i<j; i++, j--) { - tmp = curve->vertex[i]; - curve->vertex[i] = curve->vertex[j]; - curve->vertex[j] = tmp; - } -} - -/* Always succeeds */ -static void smooth(privcurve_t *curve, double alphamax) { - int m = curve->n; - - int i, j, k; - double dd, denom, alpha; - dpoint_t p2, p3, p4; - - /* examine each vertex and find its best fit */ - for (i=0; i<m; i++) { - j = mod(i+1, m); - k = mod(i+2, m); - p4 = interval(1/2.0, curve->vertex[k], curve->vertex[j]); - - denom = ddenom(curve->vertex[i], curve->vertex[k]); - if (denom != 0.0) { - dd = dpara(curve->vertex[i], curve->vertex[j], curve->vertex[k]) / denom; - dd = fabs(dd); - alpha = dd>1 ? (1 - 1.0/dd) : 0; - alpha = alpha / 0.75; - } else { - alpha = 4/3.0; - } - curve->alpha0[j] = alpha; /* remember "original" value of alpha */ - - if (alpha >= alphamax) { /* pointed corner */ - curve->tag[j] = POTRACE_CORNER; - curve->c[j][1] = curve->vertex[j]; - curve->c[j][2] = p4; - } else { - if (alpha < 0.55) { - alpha = 0.55; - } else if (alpha > 1) { - alpha = 1; - } - p2 = interval(.5+.5*alpha, curve->vertex[i], curve->vertex[j]); - p3 = interval(.5+.5*alpha, curve->vertex[k], curve->vertex[j]); - curve->tag[j] = POTRACE_CURVETO; - curve->c[j][0] = p2; - curve->c[j][1] = p3; - curve->c[j][2] = p4; - } - curve->alpha[j] = alpha; /* store the "cropped" value of alpha */ - curve->beta[j] = 0.5; - } - curve->alphacurve = 1; - - return; -} - -/* ---------------------------------------------------------------------- */ -/* Stage 5: Curve optimization (Sec. 2.4) */ - -/* a private type for the result of opti_penalty */ -struct opti_s { - double pen; /* penalty */ - dpoint_t c[2]; /* curve parameters */ - double t, s; /* curve parameters */ - double alpha; /* curve parameter */ -}; -typedef struct opti_s opti_t; - -/* calculate best fit from i+.5 to j+.5. Assume i<j (cyclically). - Return 0 and set badness and parameters (alpha, beta), if - possible. Return 1 if impossible. */ -static int opti_penalty(privpath_t *pp, int i, int j, opti_t *res, double opttolerance, int *convc, double *areac) { - int m = pp->curve.n; - int k, k1, k2, conv, i1; - double area, alpha, d, d1, d2; - dpoint_t p0, p1, p2, p3, pt; - double A, R, A1, A2, A3, A4; - double s, t; - - /* check convexity, corner-freeness, and maximum bend < 179 degrees */ - - if (i==j) { /* sanity - a full loop can never be an opticurve */ - return 1; - } - - k = i; - i1 = mod(i+1, m); - k1 = mod(k+1, m); - conv = convc[k1]; - if (conv == 0) { - return 1; - } - d = ddist(pp->curve.vertex[i], pp->curve.vertex[i1]); - for (k=k1; k!=j; k=k1) { - k1 = mod(k+1, m); - k2 = mod(k+2, m); - if (convc[k1] != conv) { - return 1; - } - if (sign(cprod(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2])) != conv) { - return 1; - } - if (iprod1(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2]) < d * ddist(pp->curve.vertex[k1], pp->curve.vertex[k2]) * COS179) { - return 1; - } - } - - /* the curve we're working in: */ - p0 = pp->curve.c[mod(i,m)][2]; - p1 = pp->curve.vertex[mod(i+1,m)]; - p2 = pp->curve.vertex[mod(j,m)]; - p3 = pp->curve.c[mod(j,m)][2]; - - /* determine its area */ - area = areac[j] - areac[i]; - area -= dpara(pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2])/2; - if (i>=j) { - area += areac[m]; - } - - /* find intersection o of p0p1 and p2p3. Let t,s such that o = - interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the - triangle (p0,o,p3). */ - - A1 = dpara(p0, p1, p2); - A2 = dpara(p0, p1, p3); - A3 = dpara(p0, p2, p3); - /* A4 = dpara(p1, p2, p3); */ - A4 = A1+A3-A2; - - if (A2 == A1) { /* this should never happen */ - return 1; - } - - t = A3/(A3-A4); - s = A2/(A2-A1); - A = A2 * t / 2.0; - - if (A == 0.0) { /* this should never happen */ - return 1; - } - - R = area / A; /* relative area */ - alpha = 2 - sqrt(4 - R / 0.3); /* overall alpha for p0-o-p3 curve */ - - res->c[0] = interval(t * alpha, p0, p1); - res->c[1] = interval(s * alpha, p3, p2); - res->alpha = alpha; - res->t = t; - res->s = s; - - p1 = res->c[0]; - p2 = res->c[1]; /* the proposed curve is now (p0,p1,p2,p3) */ - - res->pen = 0; - - /* calculate penalty */ - /* check tangency with edges */ - for (k=mod(i+1,m); k!=j; k=k1) { - k1 = mod(k+1,m); - t = tangent(p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1]); - if (t<-.5) { - return 1; - } - pt = bezier(t, p0, p1, p2, p3); - d = ddist(pp->curve.vertex[k], pp->curve.vertex[k1]); - if (d == 0.0) { /* this should never happen */ - return 1; - } - d1 = dpara(pp->curve.vertex[k], pp->curve.vertex[k1], pt) / d; - if (fabs(d1) > opttolerance) { - return 1; - } - if (iprod(pp->curve.vertex[k], pp->curve.vertex[k1], pt) < 0 || iprod(pp->curve.vertex[k1], pp->curve.vertex[k], pt) < 0) { - return 1; - } - res->pen += sq(d1); - } - - /* check corners */ - for (k=i; k!=j; k=k1) { - k1 = mod(k+1,m); - t = tangent(p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2]); - if (t<-.5) { - return 1; - } - pt = bezier(t, p0, p1, p2, p3); - d = ddist(pp->curve.c[k][2], pp->curve.c[k1][2]); - if (d == 0.0) { /* this should never happen */ - return 1; - } - d1 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pt) / d; - d2 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1]) / d; - d2 *= 0.75 * pp->curve.alpha[k1]; - if (d2 < 0) { - d1 = -d1; - d2 = -d2; - } - if (d1 < d2 - opttolerance) { - return 1; - } - if (d1 < d2) { - res->pen += sq(d1 - d2); - } - } - - return 0; -} - -/* optimize the path p, replacing sequences of Bezier segments by a - single segment when possible. Return 0 on success, 1 with errno set - on failure. */ -static int opticurve(privpath_t *pp, double opttolerance) { - int m = pp->curve.n; - int *pt = NULL; /* pt[m+1] */ - double *pen = NULL; /* pen[m+1] */ - int *len = NULL; /* len[m+1] */ - opti_t *opt = NULL; /* opt[m+1] */ - int om; - int i,j,r; - opti_t o; - dpoint_t p0; - int i1; - double area; - double alpha; - double *s = NULL; - double *t = NULL; - - int *convc = NULL; /* conv[m]: pre-computed convexities */ - double *areac = NULL; /* cumarea[m+1]: cache for fast area computation */ - - SAFE_CALLOC(pt, m+1, int); - SAFE_CALLOC(pen, m+1, double); - SAFE_CALLOC(len, m+1, int); - SAFE_CALLOC(opt, m+1, opti_t); - SAFE_CALLOC(convc, m, int); - SAFE_CALLOC(areac, m+1, double); - - /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */ - for (i=0; i<m; i++) { - if (pp->curve.tag[i] == POTRACE_CURVETO) { - convc[i] = sign(dpara(pp->curve.vertex[mod(i-1,m)], pp->curve.vertex[i], pp->curve.vertex[mod(i+1,m)])); - } else { - convc[i] = 0; - } - } - - /* pre-calculate areas */ - area = 0.0; - areac[0] = 0.0; - p0 = pp->curve.vertex[0]; - for (i=0; i<m; i++) { - i1 = mod(i+1, m); - if (pp->curve.tag[i1] == POTRACE_CURVETO) { - alpha = pp->curve.alpha[i1]; - area += 0.3*alpha*(4-alpha)*dpara(pp->curve.c[i][2], pp->curve.vertex[i1], pp->curve.c[i1][2])/2; - area += dpara(p0, pp->curve.c[i][2], pp->curve.c[i1][2])/2; - } - areac[i+1] = area; - } - - pt[0] = -1; - pen[0] = 0; - len[0] = 0; - - /* Fixme: we always start from a fixed point -- should find the best - curve cyclically */ - - for (j=1; j<=m; j++) { - /* calculate best path from 0 to j */ - pt[j] = j-1; - pen[j] = pen[j-1]; - len[j] = len[j-1]+1; - - for (i=j-2; i>=0; i--) { - r = opti_penalty(pp, i, mod(j,m), &o, opttolerance, convc, areac); - if (r) { - break; - } - if (len[j] > len[i]+1 || (len[j] == len[i]+1 && pen[j] > pen[i] + o.pen)) { - pt[j] = i; - pen[j] = pen[i] + o.pen; - len[j] = len[i] + 1; - opt[j] = o; - } - } - } - om = len[m]; - r = privcurve_init(&pp->ocurve, om); - if (r) { - goto calloc_error; - } - SAFE_CALLOC(s, om, double); - SAFE_CALLOC(t, om, double); - - j = m; - for (i=om-1; i>=0; i--) { - if (pt[j]==j-1) { - pp->ocurve.tag[i] = pp->curve.tag[mod(j,m)]; - pp->ocurve.c[i][0] = pp->curve.c[mod(j,m)][0]; - pp->ocurve.c[i][1] = pp->curve.c[mod(j,m)][1]; - pp->ocurve.c[i][2] = pp->curve.c[mod(j,m)][2]; - pp->ocurve.vertex[i] = pp->curve.vertex[mod(j,m)]; - pp->ocurve.alpha[i] = pp->curve.alpha[mod(j,m)]; - pp->ocurve.alpha0[i] = pp->curve.alpha0[mod(j,m)]; - pp->ocurve.beta[i] = pp->curve.beta[mod(j,m)]; - s[i] = t[i] = 1.0; - } else { - pp->ocurve.tag[i] = POTRACE_CURVETO; - pp->ocurve.c[i][0] = opt[j].c[0]; - pp->ocurve.c[i][1] = opt[j].c[1]; - pp->ocurve.c[i][2] = pp->curve.c[mod(j,m)][2]; - pp->ocurve.vertex[i] = interval(opt[j].s, pp->curve.c[mod(j,m)][2], pp->curve.vertex[mod(j,m)]); - pp->ocurve.alpha[i] = opt[j].alpha; - pp->ocurve.alpha0[i] = opt[j].alpha; - s[i] = opt[j].s; - t[i] = opt[j].t; - } - j = pt[j]; - } - - /* calculate beta parameters */ - for (i=0; i<om; i++) { - i1 = mod(i+1,om); - pp->ocurve.beta[i] = s[i] / (s[i] + t[i1]); - } - pp->ocurve.alphacurve = 1; - - free(pt); - free(pen); - free(len); - free(opt); - free(s); - free(t); - free(convc); - free(areac); - return 0; - - calloc_error: - free(pt); - free(pen); - free(len); - free(opt); - free(s); - free(t); - free(convc); - free(areac); - return 1; -} - -/* ---------------------------------------------------------------------- */ - -#define TRY(x) if (x) goto try_error - -/* return 0 on success, 1 on error with errno set. */ -int process_path(path_t *plist, const potrace_param_t *param, progress_t *progress) { - path_t *p; - double nn = 0, cn = 0; - - if (progress->callback) { - /* precompute task size for progress estimates */ - nn = 0; - list_forall (p, plist) { - nn += p->priv->len; - } - cn = 0; - } - - /* call downstream function with each path */ - list_forall (p, plist) { - TRY(calc_sums(p->priv)); - TRY(calc_lon(p->priv)); - TRY(bestpolygon(p->priv)); - TRY(adjust_vertices(p->priv)); - if (p->sign == '-') { /* reverse orientation of negative paths */ - reverse(&p->priv->curve); - } - smooth(&p->priv->curve, param->alphamax); - if (param->opticurve) { - TRY(opticurve(p->priv, param->opttolerance)); - p->priv->fcurve = &p->priv->ocurve; - } else { - p->priv->fcurve = &p->priv->curve; - } - privcurve_to_curve(p->priv->fcurve, &p->curve); - - if (progress->callback) { - cn += p->priv->len; - progress_update(cn/nn, progress); - } - } - - progress_update(1.0, progress); - - return 0; - - try_error: - return 1; -} diff --git a/src/trace/potrace/trace.h b/src/trace/potrace/trace.h deleted file mode 100644 index c53cdd10f..000000000 --- a/src/trace/potrace/trace.h +++ /dev/null @@ -1,15 +0,0 @@ -/* Copyright (C) 2001-2015 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - - -#ifndef TRACE_H -#define TRACE_H - -#include "potracelib.h" -#include "progress.h" -#include "curve.h" - -int process_path(path_t *plist, const potrace_param_t *param, progress_t *progress); - -#endif /* TRACE_H */ diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 58af7d935..46b0af6bb 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -34,7 +34,6 @@ set(ui_SRC tools/dropper-tool.cpp tools/dynamic-base.cpp tools/eraser-tool.cpp - tools/flood-tool.cpp tools/freehand-base.cpp tools/gradient-tool.cpp tools/lpe-tool.cpp @@ -107,7 +106,6 @@ set(ui_SRC dialog/template-widget.cpp dialog/text-edit.cpp dialog/tile.cpp - dialog/tracedialog.cpp dialog/transformation.cpp dialog/undo-history.cpp dialog/xml-tree.cpp @@ -248,7 +246,6 @@ set(ui_SRC dialog/template-widget.h dialog/text-edit.h dialog/tile.h - dialog/tracedialog.h dialog/transformation.h dialog/undo-history.h dialog/xml-tree.h @@ -276,7 +273,6 @@ set(ui_SRC tools/dropper-tool.h tools/dynamic-base.h tools/eraser-tool.h - tools/flood-tool.h tools/freehand-base.h tools/gradient-tool.h tools/lpe-tool.h @@ -364,3 +360,14 @@ endif() # add_inkscape_lib(ui_LIB "${ui_SRC}") add_inkscape_source("${ui_SRC}") + +set ( ui_flood_and_trace_SRC + tools/flood-tool.h + tools/flood-tool.cpp + dialog/tracedialog.cpp + dialog/tracedialog.h +) + +if ("${HAVE_POTRACE}") + add_inkscape_source("${ui_flood_and_trace_SRC}") +endif() diff --git a/src/ui/control-manager.h b/src/ui/control-manager.h index 05a53f6a0..964ad0a29 100644 --- a/src/ui/control-manager.h +++ b/src/ui/control-manager.h @@ -74,9 +74,11 @@ public: private: ControlManager(); - +#if __cplusplus <= 199711L std::auto_ptr<ControlManagerImpl> _impl; - +#else + std::unique_ptr<ControlManagerImpl> _impl; +#endif friend class ControlManagerImpl; }; diff --git a/src/ui/dialog/Makefile_insert b/src/ui/dialog/Makefile_insert index cbdae1cb0..793988a7d 100644 --- a/src/ui/dialog/Makefile_insert +++ b/src/ui/dialog/Makefile_insert @@ -106,8 +106,6 @@ ink_common_sources += \ ui/dialog/text-edit.h \ ui/dialog/tile.cpp \ ui/dialog/tile.h \ - ui/dialog/tracedialog.cpp \ - ui/dialog/tracedialog.h \ ui/dialog/pixelartdialog.cpp \ ui/dialog/pixelartdialog.h \ ui/dialog/transformation.cpp \ @@ -123,3 +121,11 @@ ink_common_sources += \ ui/dialog/lpe-fillet-chamfer-properties.cpp \ ui/dialog/lpe-fillet-chamfer-properties.h \ $(inkboard_dialogs) + +if HAVE_POTRACE + +ink_common_sources += \ + ui/dialog/tracedialog.cpp \ + ui/dialog/tracedialog.h + +endif diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp index 7b1b36908..49853277c 100644 --- a/src/ui/dialog/dialog-manager.cpp +++ b/src/ui/dialog/dialog-manager.cpp @@ -34,7 +34,11 @@ #include "ui/dialog/messages.h" #include "ui/dialog/symbols.h" #include "ui/dialog/tile.h" -#include "ui/dialog/tracedialog.h" + +#if HAVE_POTRACE +# include "ui/dialog/tracedialog.h" +#endif + #include "ui/dialog/pixelartdialog.h" #include "ui/dialog/transformation.h" #include "ui/dialog/undo-history.h" @@ -124,7 +128,11 @@ DialogManager::DialogManager() { registerFactory("Swatches", &create<SwatchesPanel, FloatingBehavior>); registerFactory("TileDialog", &create<ArrangeDialog, FloatingBehavior>); registerFactory("Symbols", &create<SymbolsDialog, FloatingBehavior>); + +#if HAVE_POTRACE registerFactory("Trace", &create<TraceDialog, FloatingBehavior>); +#endif + registerFactory("PixelArt", &create<PixelArtDialog, FloatingBehavior>); registerFactory("Transformation", &create<Transformation, FloatingBehavior>); registerFactory("UndoHistory", &create<UndoHistory, FloatingBehavior>); @@ -159,7 +167,11 @@ DialogManager::DialogManager() { registerFactory("Swatches", &create<SwatchesPanel, DockBehavior>); registerFactory("TileDialog", &create<ArrangeDialog, DockBehavior>); registerFactory("Symbols", &create<SymbolsDialog, DockBehavior>); + +#if HAVE_POTRACE registerFactory("Trace", &create<TraceDialog, DockBehavior>); +#endif + registerFactory("PixelArt", &create<PixelArtDialog, DockBehavior>); registerFactory("Transformation", &create<Transformation, DockBehavior>); registerFactory("UndoHistory", &create<UndoHistory, DockBehavior>); diff --git a/src/ui/dialog/filter-effects-dialog.h b/src/ui/dialog/filter-effects-dialog.h index a067cd70c..283abb5b0 100644 --- a/src/ui/dialog/filter-effects-dialog.h +++ b/src/ui/dialog/filter-effects-dialog.h @@ -127,7 +127,11 @@ private: Gtk::Button _add; Glib::RefPtr<Gtk::Menu> _menu; sigc::signal<void> _signal_filter_changed; +#if __cplusplus <= 199711L std::auto_ptr<Inkscape::XML::SignalObserver> _observer; +#else + std::unique_ptr<Inkscape::XML::SignalObserver> _observer; +#endif }; class PrimitiveColumns : public Gtk::TreeModel::ColumnRecord @@ -243,7 +247,11 @@ private: sigc::connection _scroll_connection; int _autoscroll_y; int _autoscroll_x; +#if __cplusplus <= 199711L std::auto_ptr<Inkscape::XML::SignalObserver> _observer; +#else + std::unique_ptr<Inkscape::XML::SignalObserver> _observer; +#endif int _input_type_width; int _input_type_height; }; diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index fec49d484..b6d7e25ec 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -478,10 +478,12 @@ void InkscapePreferences::initPageTools() this->AddPage(_page_eraser, _("Eraser"), iter_tools, PREFS_PAGE_TOOLS_ERASER); this->AddNewObjectsStyle(_page_eraser, "/tools/eraser"); +#if HAVE_POTRACE //Paint Bucket this->AddPage(_page_paintbucket, _("Paint Bucket"), iter_tools, PREFS_PAGE_TOOLS_PAINTBUCKET); this->AddSelcueCheckbox(_page_paintbucket, "/tools/paintbucket", false); this->AddNewObjectsStyle(_page_paintbucket, "/tools/paintbucket"); +#endif //Gradient this->AddPage(_page_gradient, _("Gradient"), iter_tools, PREFS_PAGE_TOOLS_GRADIENT); diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index 7e0184c55..b7696ab8c 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -15,6 +15,10 @@ #ifndef INKSCAPE_UI_DIALOG_INKSCAPE_PREFERENCES_H #define INKSCAPE_UI_DIALOG_INKSCAPE_PREFERENCES_H +#if HAVE_CONFIG_H +# include "config.h" +#endif + #include <iostream> #include <vector> #include "ui/widget/preferences-widget.h" @@ -56,7 +60,11 @@ enum { PREFS_PAGE_TOOLS_TEXT, PREFS_PAGE_TOOLS_SPRAY, PREFS_PAGE_TOOLS_ERASER, + +#if HAVE_POTRACE PREFS_PAGE_TOOLS_PAINTBUCKET, +#endif + PREFS_PAGE_TOOLS_GRADIENT, PREFS_PAGE_TOOLS_DROPPER, PREFS_PAGE_TOOLS_CONNECTOR, diff --git a/src/ui/interface.cpp b/src/ui/interface.cpp index 9a4e1d773..0f719b40f 100644 --- a/src/ui/interface.cpp +++ b/src/ui/interface.cpp @@ -846,13 +846,11 @@ static void sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, I #endif } } else if (menu_pntr->attribute("check") != NULL) { - SPAction *action = NULL; if (verb->get_code() != SP_VERB_NONE) { - action = verb->get_action(Inkscape::ActionContext(view)); - } - sp_ui_menu_append_check_item_from_verb(GTK_MENU(menu), view, action->name, action->tip, NULL, + SPAction *action = verb->get_action(Inkscape::ActionContext(view)); + sp_ui_menu_append_check_item_from_verb(GTK_MENU(menu), view, action->name, action->tip, NULL, checkitem_toggled, checkitem_update, verb); - + } } else { sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view); group = NULL; diff --git a/src/ui/tool-factory.cpp b/src/ui/tool-factory.cpp index 700bd40ce..c6c579c9e 100644 --- a/src/ui/tool-factory.cpp +++ b/src/ui/tool-factory.cpp @@ -16,7 +16,11 @@ #include "ui/tools/connector-tool.h" #include "ui/tools/dropper-tool.h" #include "ui/tools/eraser-tool.h" -#include "ui/tools/flood-tool.h" + +#if HAVE_POTRACE +# include "ui/tools/flood-tool.h" +#endif + #include "ui/tools/gradient-tool.h" #include "ui/tools/lpe-tool.h" #include "ui/tools/measure-tool.h" @@ -52,8 +56,10 @@ ToolBase *ToolFactory::createObject(std::string const& id) tool = new DropperTool; else if (id == "/tools/eraser") tool = new EraserTool; +#if HAVE_POTRACE else if (id == "/tools/paintbucket") tool = new FloodTool; +#endif else if (id == "/tools/gradient") tool = new GradientTool; else if (id == "/tools/lpetool") diff --git a/src/ui/tools-switch.h b/src/ui/tools-switch.h index 280837e87..d396597ca 100644 --- a/src/ui/tools-switch.h +++ b/src/ui/tools-switch.h @@ -12,6 +12,10 @@ #ifndef SEEN_TOOLS_SWITCH_H #define SEEN_TOOLS_SWITCH_H +#if HAVE_CONFIG_H +# include "config.h" +#endif + class SPDesktop; class SPItem; namespace Geom { @@ -40,7 +44,11 @@ enum { TOOLS_MEASURE, TOOLS_DROPPER, TOOLS_CONNECTOR, + +#if HAVE_POTRACE TOOLS_PAINTBUCKET, +#endif + TOOLS_ERASER, TOOLS_LPETOOL }; diff --git a/src/ui/tools/Makefile_insert b/src/ui/tools/Makefile_insert index cd09a3230..686dfedd8 100644 --- a/src/ui/tools/Makefile_insert +++ b/src/ui/tools/Makefile_insert @@ -8,7 +8,6 @@ ink_common_sources += \ ui/tools/dropper-tool.cpp ui/tools/dropper-tool.h \ ui/tools/dynamic-base.cpp ui/tools/dynamic-base.h \ ui/tools/eraser-tool.cpp ui/tools/eraser-tool.h \ - ui/tools/flood-tool.cpp ui/tools/flood-tool.h \ ui/tools/freehand-base.cpp ui/tools/freehand-base.h \ ui/tools/gradient-tool.cpp ui/tools/gradient-tool.h \ ui/tools/lpe-tool.cpp ui/tools/lpe-tool.h \ @@ -25,4 +24,11 @@ ink_common_sources += \ ui/tools/text-tool.cpp ui/tools/text-tool.h \ ui/tools/tool-base.cpp ui/tools/tool-base.h \ ui/tools/tweak-tool.cpp ui/tools/tweak-tool.h \ - ui/tools/zoom-tool.cpp ui/tools/zoom-tool.h
\ No newline at end of file + ui/tools/zoom-tool.cpp ui/tools/zoom-tool.h + +if HAVE_POTRACE + +ink_common_sources += \ + ui/tools/flood-tool.cpp ui/tools/flood-tool.h + +endif diff --git a/src/ui/tools/mesh-tool.cpp b/src/ui/tools/mesh-tool.cpp index 303757493..794296329 100644 --- a/src/ui/tools/mesh-tool.cpp +++ b/src/ui/tools/mesh-tool.cpp @@ -57,7 +57,7 @@ namespace Inkscape { namespace UI { namespace Tools { -static void sp_mesh_drag(MeshTool &rc, Geom::Point const pt, guint state, guint32 etime); +static void sp_mesh_end_drag(MeshTool &rc); const std::string& MeshTool::getPrefsPath() { return MeshTool::prefsPath; @@ -560,8 +560,9 @@ bool MeshTool::root_handler(GdkEvent* event) { Inkscape::Rubberband::get(desktop)->move(motion_dt); this->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Draw around</b> handles to select them")); } else { - // Create new gradient with coordinates determined by drag. - sp_mesh_drag(*this, motion_dt, event->motion.state, event->motion.time); + // Do nothing. For a linear/radial gradient we follow the drag, updating the + // gradient as the end node is dragged. For a mesh gradient, the gradient is always + // created to fill the object when the drag ends. } gobble_motion_events(GDK_BUTTON1_MASK); @@ -649,7 +650,7 @@ bool MeshTool::root_handler(GdkEvent* event) { } if (!this->within_tolerance) { - // we've been dragging, either do nothing (grdrag handles that), + // we've been dragging, either create a new gradient // or rubberband-select if we have rubberband Inkscape::Rubberband *r = Inkscape::Rubberband::get(desktop); @@ -659,6 +660,9 @@ bool MeshTool::root_handler(GdkEvent* event) { Geom::OptRect const b = r->getRectangle(); drag->selectRect(*b); } + } else { + // Create a new mesh gradient + sp_mesh_end_drag(*this); } } else if (this->item_to_select) { if (over_line && line) { @@ -922,7 +926,7 @@ bool MeshTool::root_handler(GdkEvent* event) { return ret; } -static void sp_mesh_drag(MeshTool &rc, Geom::Point const /*pt*/, guint /*state*/, guint32 /*etime*/) { +static void sp_mesh_end_drag(MeshTool &rc) { SPDesktop *desktop = SP_EVENT_CONTEXT(&rc)->desktop; Inkscape::Selection *selection = desktop->getSelection(); SPDocument *document = desktop->getDocument(); @@ -963,19 +967,8 @@ static void sp_mesh_drag(MeshTool &rc, Geom::Point const /*pt*/, guint /*state*/ (*i)->requestModified(SP_OBJECT_MODIFIED_FLAG); } - // if (ec->_grdrag) { - // ec->_grdrag->updateDraggers(); - // // prevent regenerating draggers by selection modified signal, which sometimes - // // comes too late and thus destroys the knot which we will now grab: - // ec->_grdrag->local_change = true; - // // give the grab out-of-bounds values of xp/yp because we're already dragging - // // and therefore are already out of tolerance - // ec->_grdrag->grabKnot (SP_ITEM(selection->itemList()->data), - // type == SP_GRADIENT_TYPE_LINEAR? POINT_LG_END : POINT_RG_R1, - // -1, // ignore number (though it is always 1) - // fill_or_stroke, 99999, 99999, etime); - // } - // We did an undoable action, but SPDocumentUndo::done will be called by the knot when released + + DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_MESH, _("Create mesh")); // status text; we do not track coords because this branch is run once, not all the time // during drag diff --git a/src/ui/tools/spray-tool.cpp b/src/ui/tools/spray-tool.cpp index 7de2a0b04..71cad5c0d 100644 --- a/src/ui/tools/spray-tool.cpp +++ b/src/ui/tools/spray-tool.cpp @@ -593,16 +593,14 @@ static bool fit_item(SPDesktop *desktop, } std::vector<SPItem*> const items_selected(selection->itemList()); std::vector<SPItem*> items_down_erased; - for (std::vector<SPItem*>::const_iterator i=items_down.begin(); i!=items_down.end(); i++) { + for (std::vector<SPItem*>::const_iterator i=items_down.begin(); i!=items_down.end(); ++i) { SPItem *item_down = *i; Geom::OptRect bbox_down = item_down->documentVisualBounds(); - width = bbox_down->width(); - height = bbox_down->height(); double bbox_left = bbox_down->left(); double bbox_top = bbox_down->top(); gchar const * item_down_sharp = g_strdup_printf("#%s", item_down->getId()); items_down_erased.push_back(item_down); - for (std::vector<SPItem*>::const_iterator j=items_selected.begin(); j!=items_selected.end(); j++) { + for (std::vector<SPItem*>::const_iterator j=items_selected.begin(); j!=items_selected.end(); ++j) { SPItem *item_selected = *j; gchar const * spray_origin; if(!item_selected->getAttribute("inkscape:spray-origin")){ diff --git a/src/uri-references.cpp b/src/uri-references.cpp index b6ccdbf5f..db46a156f 100644 --- a/src/uri-references.cpp +++ b/src/uri-references.cpp @@ -19,7 +19,7 @@ #include "uri.h" #include "uri-references.h" #include "extract-uri.h" - +#include "sp-tag-use.h" #include <glibmm/miscutils.h> #include <sigc++/functors/mem_fun.h> @@ -84,7 +84,14 @@ bool URIReference::_acceptObject(SPObject *obj) const positions.push_back(position); owner = owner->parent; } - owner = ((SPUse *)owner)->get_original(); + if (dynamic_cast<SPUse *>(owner)) + owner = ((SPUse *)owner)->get_original(); + else if (dynamic_cast<SPTagUse *>(owner)) + owner = ((SPTagUse *)owner)->get_original(); + else { + g_warning("cloned object with no known type\n"); + return false; + } for (int i = positions.size() - 2; i >= 0; i--) owner = owner->childList(false)[positions[i]]; } diff --git a/src/verbs.cpp b/src/verbs.cpp index e0ef27b0d..6b13eabb6 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -832,7 +832,14 @@ Verb *Verb::getbyid(gchar const *id) verb = verb_found->second; } - if (verb == NULL) + if (verb == NULL +#if !HAVE_POTRACE + // Squash warning about disabled features + && strcmp(id, "ToolPaintBucket") != 0 + && strcmp(id, "SelectionTrace") != 0 + && strcmp(id, "PaintBucketPrefs") != 0 +#endif + ) printf("Unable to find: %s\n", id); return verb; @@ -1201,10 +1208,14 @@ void SelectionVerb::perform(SPAction *action, void *data) case SP_VERB_SELECTION_REVERSE: SelectionHelper::reverse(dt); break; + +#if HAVE_POTRACE case SP_VERB_SELECTION_TRACE: INKSCAPE.dialogs_unhide(); dt->_dlg_mgr->showDialog("Trace"); break; +#endif + case SP_VERB_SELECTION_PIXEL_ART: INKSCAPE.dialogs_unhide(); dt->_dlg_mgr->showDialog("PixelArt"); @@ -1606,7 +1617,7 @@ void ContextVerb::perform(SPAction *action, void *data) /** \todo !!! hopefully this can go away soon and actions can look after * themselves */ - for (vidx = SP_VERB_CONTEXT_SELECT; vidx <= SP_VERB_CONTEXT_PAINTBUCKET_PREFS; vidx++) + for (vidx = SP_VERB_CONTEXT_SELECT; vidx <= SP_VERB_CONTEXT_LPETOOL_PREFS; vidx++) { SPAction *tool_action= get((sp_verb_t)vidx)->get_action(action->context); if (tool_action) { @@ -1673,9 +1684,13 @@ void ContextVerb::perform(SPAction *action, void *data) case SP_VERB_CONTEXT_CONNECTOR: tools_switch(dt, TOOLS_CONNECTOR); break; + +#if HAVE_POTRACE case SP_VERB_CONTEXT_PAINTBUCKET: tools_switch(dt, TOOLS_PAINTBUCKET); break; +#endif + case SP_VERB_CONTEXT_ERASER: tools_switch(dt, TOOLS_ERASER); break; @@ -1759,10 +1774,14 @@ void ContextVerb::perform(SPAction *action, void *data) prefs->setInt("/dialogs/preferences/page", PREFS_PAGE_TOOLS_CONNECTOR); dt->_dlg_mgr->showDialog("InkscapePreferences"); break; + +#if HAVE_POTRACE case SP_VERB_CONTEXT_PAINTBUCKET_PREFS: prefs->setInt("/dialogs/preferences/page", PREFS_PAGE_TOOLS_PAINTBUCKET); dt->_dlg_mgr->showDialog("InkscapePreferences"); break; +#endif + case SP_VERB_CONTEXT_ERASER_PREFS: prefs->setInt("/dialogs/preferences/page", PREFS_PAGE_TOOLS_ERASER); dt->_dlg_mgr->showDialog("InkscapePreferences"); @@ -2160,10 +2179,14 @@ void TutorialVerb::perform(SPAction *action, void *data) // TRANSLATORS: See "tutorial-basic.svg" comment. sp_help_open_tutorial(NULL, (gpointer)_("tutorial-advanced.svg")); break; + +#if HAVE_POTRACE case SP_VERB_TUTORIAL_TRACING: // TRANSLATORS: See "tutorial-basic.svg" comment. sp_help_open_tutorial(NULL, (gpointer)_("tutorial-tracing.svg")); break; +#endif + case SP_VERB_TUTORIAL_TRACING_PIXELART: sp_help_open_tutorial(NULL, (gpointer)_("tutorial-tracing-pixelart.svg")); break; @@ -2593,9 +2616,13 @@ Verb *Verb::_base_verbs[] = { N_("Simplify selected paths (remove extra nodes)"), INKSCAPE_ICON("path-simplify")), new SelectionVerb(SP_VERB_SELECTION_REVERSE, "SelectionReverse", N_("_Reverse"), N_("Reverse the direction of selected paths (useful for flipping markers)"), INKSCAPE_ICON("path-reverse")), + +#if HAVE_POTRACE // TRANSLATORS: "to trace" means "to convert a bitmap to vector graphics" (to vectorize) new SelectionVerb(SP_VERB_SELECTION_TRACE, "SelectionTrace", N_("_Trace Bitmap..."), N_("Create one or more paths from a bitmap by tracing it"), INKSCAPE_ICON("bitmap-trace")), +#endif + new SelectionVerb(SP_VERB_SELECTION_PIXEL_ART, "SelectionPixelArt", N_("Trace Pixel Art..."), N_("Create paths using Kopf-Lischinski algorithm to vectorize pixel art"), INKSCAPE_ICON("pixelart-trace")), new SelectionVerb(SP_VERB_SELECTION_CREATE_BITMAP, "SelectionCreateBitmap", N_("Make a _Bitmap Copy"), @@ -2733,8 +2760,12 @@ Verb *Verb::_base_verbs[] = { N_("Pick colors from image"), INKSCAPE_ICON("color-picker")), new ContextVerb(SP_VERB_CONTEXT_CONNECTOR, "ToolConnector", NC_("ContextVerb", "Connector"), N_("Create diagram connectors"), INKSCAPE_ICON("draw-connector")), + +#if HAVE_POTRACE new ContextVerb(SP_VERB_CONTEXT_PAINTBUCKET, "ToolPaintBucket", NC_("ContextVerb", "Paint Bucket"), N_("Fill bounded areas"), INKSCAPE_ICON("color-fill")), +#endif + new ContextVerb(SP_VERB_CONTEXT_LPE, "ToolLPE", NC_("ContextVerb", "LPE Edit"), N_("Edit Path Effect parameters"), NULL), new ContextVerb(SP_VERB_CONTEXT_ERASER, "ToolEraser", NC_("ContextVerb", "Eraser"), @@ -2780,8 +2811,12 @@ Verb *Verb::_base_verbs[] = { N_("Open Preferences for the Dropper tool"), NULL), new ContextVerb(SP_VERB_CONTEXT_CONNECTOR_PREFS, "ConnectorPrefs", N_("Connector Preferences"), N_("Open Preferences for the Connector tool"), NULL), + +#if HAVE_POTRACE new ContextVerb(SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "PaintBucketPrefs", N_("Paint Bucket Preferences"), N_("Open Preferences for the Paint Bucket tool"), NULL), +#endif + new ContextVerb(SP_VERB_CONTEXT_ERASER_PREFS, "EraserPrefs", N_("Eraser Preferences"), N_("Open Preferences for the Eraser tool"), NULL), new ContextVerb(SP_VERB_CONTEXT_LPETOOL_PREFS, "LPEToolPrefs", N_("LPE Tool Preferences"), @@ -2937,9 +2972,13 @@ Verb *Verb::_base_verbs[] = { N_("Using shape tools to create and edit shapes"), NULL), new TutorialVerb(SP_VERB_TUTORIAL_ADVANCED, "TutorialsAdvanced", N_("Inkscape: _Advanced"), N_("Advanced Inkscape topics"), NULL/*"tutorial_advanced"*/), + +#if HAVE_POTRACE // TRANSLATORS: "to trace" means "to convert a bitmap to vector graphics" (to vectorize) new TutorialVerb(SP_VERB_TUTORIAL_TRACING, "TutorialsTracing", N_("Inkscape: T_racing"), N_("Using bitmap tracing"), NULL/*"tutorial_tracing"*/), +#endif + new TutorialVerb(SP_VERB_TUTORIAL_TRACING_PIXELART, "TutorialsTracingPixelArt", N_("Inkscape: Tracing Pixel Art"), N_("Using Trace Pixel Art dialog"), NULL), new TutorialVerb(SP_VERB_TUTORIAL_CALLIGRAPHY, "TutorialsCalligraphy", N_("Inkscape: _Calligraphy"), diff --git a/src/verbs.h b/src/verbs.h index 06fc4fb05..27aecae64 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -136,7 +136,11 @@ enum { SP_VERB_SELECTION_OUTLINE, SP_VERB_SELECTION_SIMPLIFY, SP_VERB_SELECTION_REVERSE, + +#if HAVE_POTRACE SP_VERB_SELECTION_TRACE, +#endif + SP_VERB_SELECTION_PIXEL_ART, SP_VERB_SELECTION_CREATE_BITMAP, SP_VERB_SELECTION_COMBINE, @@ -203,7 +207,11 @@ enum { SP_VERB_CONTEXT_MEASURE, SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_CONNECTOR, + +#if HAVE_POTRACE SP_VERB_CONTEXT_PAINTBUCKET, +#endif + SP_VERB_CONTEXT_LPE, /* not really a tool but used for editing LPE parameters on-canvas for example */ SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_LPETOOL, /* note that this is very different from SP_VERB_CONTEXT_LPE above! */ @@ -227,7 +235,11 @@ enum { SP_VERB_CONTEXT_MEASURE_PREFS, SP_VERB_CONTEXT_DROPPER_PREFS, SP_VERB_CONTEXT_CONNECTOR_PREFS, + +#if HAVE_POTRACE SP_VERB_CONTEXT_PAINTBUCKET_PREFS, +#endif + SP_VERB_CONTEXT_ERASER_PREFS, SP_VERB_CONTEXT_LPETOOL_PREFS, /* Zooming and desktop settings */ @@ -311,7 +323,11 @@ enum { SP_VERB_TUTORIAL_BASIC, SP_VERB_TUTORIAL_SHAPES, SP_VERB_TUTORIAL_ADVANCED, + +#if HAVE_POTRACE SP_VERB_TUTORIAL_TRACING, +#endif + SP_VERB_TUTORIAL_TRACING_PIXELART, SP_VERB_TUTORIAL_CALLIGRAPHY, SP_VERB_TUTORIAL_INTERPOLATE, diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index a3e9e14d0..c38bde5cf 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -11,7 +11,6 @@ set(widgets_SRC measure-toolbar.cpp mesh-toolbar.cpp node-toolbar.cpp - paintbucket-toolbar.cpp pencil-toolbar.cpp rect-toolbar.cpp spiral-toolbar.cpp @@ -67,7 +66,6 @@ set(widgets_SRC measure-toolbar.h mesh-toolbar.h node-toolbar.h - paintbucket-toolbar.h pencil-toolbar.h rect-toolbar.h spiral-toolbar.h @@ -114,3 +112,12 @@ set(widgets_SRC # add_inkscape_lib(widgets_LIB "${widgets_SRC}") add_inkscape_source("${widgets_SRC}") + +set ( widgets_paintbucket_SRC + paintbucket-toolbar.cpp + paintbucket-toolbar.h +) + +if ("${HAVE_POTRACE}") + add_inkscape_source("${widgets_paintbucket_SRC}") +endif() diff --git a/src/widgets/Makefile_insert b/src/widgets/Makefile_insert index f66be66ed..6913f4a58 100644 --- a/src/widgets/Makefile_insert +++ b/src/widgets/Makefile_insert @@ -58,8 +58,6 @@ ink_common_sources += \ widgets/node-toolbar.h \ widgets/paint-selector.cpp \ widgets/paint-selector.h \ - widgets/paintbucket-toolbar.cpp \ - widgets/paintbucket-toolbar.h \ widgets/pencil-toolbar.cpp \ widgets/pencil-toolbar.h \ widgets/rect-toolbar.cpp \ @@ -109,5 +107,13 @@ ink_common_sources += \ widgets/zoom-toolbar.h \ widgets/widget-sizes.h +if HAVE_POTRACE + +ink_common_sources += \ + widgets/paintbucket-toolbar.cpp \ + widgets/paintbucket-toolbar.h + +endif + widgets/button.$(OBJEXT): helper/sp-marshal.h widgets/menu.$(OBJEXT): helper/sp-marshal.h diff --git a/src/widgets/fill-style.cpp b/src/widgets/fill-style.cpp index a57c891e5..a96776894 100644 --- a/src/widgets/fill-style.cpp +++ b/src/widgets/fill-style.cpp @@ -68,7 +68,7 @@ namespace Inkscape { class FillNStroke : public Gtk::VBox { public: - FillNStroke( FillOrStroke kind ); + FillNStroke( FillOrStroke k ); ~FillNStroke(); void setFillrule( SPPaintSelector::FillRule mode ); @@ -125,9 +125,9 @@ Gtk::Widget *Inkscape::Widgets::createStyleWidget( FillOrStroke kind ) return filler; } -FillNStroke::FillNStroke( FillOrStroke kind ) : +FillNStroke::FillNStroke( FillOrStroke k ) : Gtk::VBox(), - kind(kind), + kind(k), desktop(0), psel(0), lastDrag(0), diff --git a/src/widgets/mesh-toolbar.cpp b/src/widgets/mesh-toolbar.cpp index 4e0b6d68b..9937b23ed 100644 --- a/src/widgets/mesh-toolbar.cpp +++ b/src/widgets/mesh-toolbar.cpp @@ -292,6 +292,9 @@ static void ms_col_changed(GtkAdjustment *adj, GObject * /*tbl*/ ) blocked = FALSE; } +/** + * Sets mesh type: Coons, Bicubic + */ static void ms_type_changed(EgeSelectOneAction *act, GtkWidget *widget) { // std::cout << "ms_type_changed" << std::endl; @@ -311,7 +314,7 @@ static void ms_type_changed(EgeSelectOneAction *act, GtkWidget *widget) gradient->type_set = true; gradient->updateRepr(); - DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_GRADIENT, + DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_MESH, _("Set mesh type")); } } diff --git a/src/widgets/pencil-toolbar.cpp b/src/widgets/pencil-toolbar.cpp index 17c1d341d..aed80a66f 100644 --- a/src/widgets/pencil-toolbar.cpp +++ b/src/widgets/pencil-toolbar.cpp @@ -34,7 +34,6 @@ #include "pencil-toolbar.h" #include "desktop.h" -#include "document-undo.h" #include "widgets/ege-adjustment-action.h" #include "widgets/ege-select-one-action.h" #include "widgets/ink-action.h" @@ -57,7 +56,6 @@ #include "util/glib-list-iterators.h" using Inkscape::UI::UXManager; -using Inkscape::DocumentUndo; using Inkscape::UI::ToolboxFactory; using Inkscape::UI::PrefPusher; diff --git a/src/widgets/spray-toolbar.cpp b/src/widgets/spray-toolbar.cpp index 1774ba418..30e9c6418 100644 --- a/src/widgets/spray-toolbar.cpp +++ b/src/widgets/spray-toolbar.cpp @@ -34,7 +34,6 @@ #include "spray-toolbar.h" #include "desktop.h" #include "inkscape.h" -#include "document-undo.h" #include "widgets/ege-adjustment-action.h" #include "widgets/ege-select-one-action.h" #include "widgets/ink-action.h" @@ -47,7 +46,6 @@ #include <glibmm/i18n.h> -using Inkscape::DocumentUndo; using Inkscape::UI::ToolboxFactory; using Inkscape::UI::PrefPusher; diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp index c49fbccaa..c6cc7dc45 100644 --- a/src/widgets/text-toolbar.cpp +++ b/src/widgets/text-toolbar.cpp @@ -737,7 +737,7 @@ static void sp_text_rotation_value_changed( GtkAdjustment *adj, GObject *tbl ) g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); } -static void sp_text_orientation_mode_changed( EgeSelectOneAction *act, GObject *tbl ) +static void sp_writing_mode_changed( EgeSelectOneAction *act, GObject *tbl ) { // quit if run by the _changed callbacks if (g_object_get_data(G_OBJECT(tbl), "freeze")) { @@ -752,13 +752,73 @@ static void sp_text_orientation_mode_changed( EgeSelectOneAction *act, GObject * { case 0: { - sp_repr_css_set_property (css, "writing-mode", "lr"); + sp_repr_css_set_property (css, "writing-mode", "lr-tb"); break; } case 1: { - sp_repr_css_set_property (css, "writing-mode", "tb"); + sp_repr_css_set_property (css, "writing-mode", "tb-rl"); + break; + } + + case 2: + { + sp_repr_css_set_property (css, "writing-mode", "vertical-lr"); + break; + } +} + + SPStyle query(SP_ACTIVE_DOCUMENT); + int result_numbers = + sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTNUMBERS); + + // If querying returned nothing, update default style. + if (result_numbers == QUERY_STYLE_NOTHING) + { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->mergeStyle("/tools/text/style", css); + } + + sp_desktop_set_style (SP_ACTIVE_DESKTOP, css, true, true); + if(result_numbers != QUERY_STYLE_NOTHING) + { + DocumentUndo::done(SP_ACTIVE_DESKTOP->getDocument(), SP_VERB_CONTEXT_TEXT, + _("Text: Change writing mode")); + } + sp_repr_css_attr_unref (css); + + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); +} + +static void sp_text_orientation_changed( EgeSelectOneAction *act, GObject *tbl ) +{ + // quit if run by the _changed callbacks + if (g_object_get_data(G_OBJECT(tbl), "freeze")) { + return; + } + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); + + int mode = ege_select_one_action_get_active( act ); + + SPCSSAttr *css = sp_repr_css_attr_new (); + switch (mode) + { + case 0: + { + sp_repr_css_set_property (css, "text-orientation", "auto"); + break; + } + + case 1: + { + sp_repr_css_set_property (css, "text-orientation", "upright"); + break; + } + + case 2: + { + sp_repr_css_set_property (css, "text-orientation", "sideways"); break; } } @@ -893,13 +953,17 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ 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_baseline = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_BASELINES); + int result_wmode = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_WRITINGMODES); /* * If no text in selection (querying returned nothing), read the style from * the /tools/text preferencess (default style for new texts). Return if * tool bar already set to these preferences. */ - if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) { + if (result_family == QUERY_STYLE_NOTHING || + result_style == QUERY_STYLE_NOTHING || + result_numbers == QUERY_STYLE_NOTHING || + result_wmode == QUERY_STYLE_NOTHING ) { // There are no texts in selection, read from preferences. query.readFromPrefs("/tools/text"); #ifdef DEBUG_TEXT @@ -1047,13 +1111,42 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ gtk_adjustment_set_value( letterSpacingAdjustment, letterSpacing ); + // Writing mode + int activeButton2 = 0; + if (query.writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) activeButton2 = 0; + if (query.writing_mode.computed == SP_CSS_WRITING_MODE_TB_RL) activeButton2 = 1; + if (query.writing_mode.computed == SP_CSS_WRITING_MODE_TB_LR) activeButton2 = 2; + + EgeSelectOneAction* writingModeAction = + EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextWritingModeAction" ) ); + ege_select_one_action_set_active( writingModeAction, activeButton2 ); + // Orientation - int activeButton2 = (query.writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB ? 0 : 1); + int activeButton3 = 0; + if (query.text_orientation.computed == SP_CSS_TEXT_ORIENTATION_MIXED ) activeButton3 = 0; + if (query.text_orientation.computed == SP_CSS_TEXT_ORIENTATION_UPRIGHT ) activeButton3 = 1; + if (query.text_orientation.computed == SP_CSS_TEXT_ORIENTATION_SIDEWAYS) activeButton3 = 2; EgeSelectOneAction* textOrientationAction = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextOrientationAction" ) ); - ege_select_one_action_set_active( textOrientationAction, activeButton2 ); + ege_select_one_action_set_active( textOrientationAction, activeButton3 ); + + // Disable text orientation for horizontal text.. See above for why this nonsense + model = GTK_LIST_STORE( ege_select_one_action_get_model( textOrientationAction ) ); + path = gtk_tree_path_new_from_string("0"); + gtk_tree_model_get_iter( GTK_TREE_MODEL (model), &iter, path ); + gtk_list_store_set( model, &iter, /* column */ 3, activeButton2 != 0, -1 ); + + path = gtk_tree_path_new_from_string("1"); + gtk_tree_model_get_iter( GTK_TREE_MODEL (model), &iter, path ); + gtk_list_store_set( model, &iter, /* column */ 3, activeButton2 != 0, -1 ); + + path = gtk_tree_path_new_from_string("2"); + gtk_tree_model_get_iter( GTK_TREE_MODEL (model), &iter, path ); + gtk_list_store_set( model, &iter, /* column */ 3, activeButton2 != 0, -1 ); + + ege_select_one_action_update_sensitive( textOrientationAction ); } @@ -1387,7 +1480,7 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_align_mode_changed), holder ); } - /* Orientation (Left to Right, Top to Bottom */ + /* Writing mode (Horizontal, Vertical-LR, Vertical-RL) */ { GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING ); @@ -1402,14 +1495,73 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, - 0, _("Vertical"), - 1, _("Vertical text"), + 0, _("Vertical — RL"), + 1, _("Vertical text — lines: right to left"), 2, INKSCAPE_ICON("format-text-direction-vertical"), -1 ); + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("Vertical — LR"), + 1, _("Vertical text — lines: left to right"), // Mongolian! + 2, INKSCAPE_ICON("format-text-direction-vertical-lr"), + -1 ); + + EgeSelectOneAction* act = ege_select_one_action_new( "TextWritingModeAction", // Name + _("Writing mode"), // Label + _("Block progression"), // Tooltip + NULL, // Icon name + GTK_TREE_MODEL(model) ); // Model + + g_object_set( act, "short_label", "NotUsed", NULL ); + gtk_action_group_add_action( mainActions, GTK_ACTION(act) ); + g_object_set_data( holder, "TextWritingModeAction", act ); + + ege_select_one_action_set_appearance( act, "full" ); + ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE ); + g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL ); + ege_select_one_action_set_icon_column( act, 2 ); + ege_select_one_action_set_icon_size( act, secondarySize ); + ege_select_one_action_set_tooltip_column( act, 1 ); + + gint mode = prefs->getInt("/tools/text/writing_mode", 0); + ege_select_one_action_set_active( act, mode ); + g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_writing_mode_changed), holder ); + } + + /* Text (glyph) orientation (Auto (mixed), Upright, Sideways) */ + { + GtkListStore* model = gtk_list_store_new( 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN ); + + GtkTreeIter iter; + + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("Auto"), + 1, _("Auto glyph orientation"), + 2, INKSCAPE_ICON("text-orientation-auto"), + 3, true, + -1 ); + + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("Upright"), + 1, _("Upright glyph orientation"), + 2, INKSCAPE_ICON("text-orientation-upright"), + 3, true, + -1 ); + + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("Sideways"), + 1, _("Sideways glyph orientation"), + 2, INKSCAPE_ICON("text-orientation-sideways"), + 3, true, + -1 ); + EgeSelectOneAction* act = ege_select_one_action_new( "TextOrientationAction", // Name - _("Orientation"), // Label - _("Text orientation"), // Tooltip + _("Text orientation"), // Label + _("Text (glyph) orientation in vertical text."), // Tooltip NULL, // Icon name GTK_TREE_MODEL(model) ); // Model @@ -1423,10 +1575,11 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje ege_select_one_action_set_icon_column( act, 2 ); ege_select_one_action_set_icon_size( act, secondarySize ); ege_select_one_action_set_tooltip_column( act, 1 ); + ege_select_one_action_set_sensitive_column( act, 3 ); - gint mode = prefs->getInt("/tools/text/orientation", 0); + gint mode = prefs->getInt("/tools/text/text_orientation", 0); ege_select_one_action_set_active( act, mode ); - g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_orientation_mode_changed), holder ); + g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_orientation_changed), holder ); } /* Line height */ diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 3e79e038a..c3f301c52 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -79,7 +79,11 @@ #include "measure-toolbar.h" #include "node-toolbar.h" #include "rect-toolbar.h" -#include "paintbucket-toolbar.h" + +#if HAVE_POTRACE +# include "paintbucket-toolbar.h" +#endif + #include "pencil-toolbar.h" #include "select-toolbar.h" #include "spray-toolbar.h" @@ -151,7 +155,9 @@ static struct { { "/tools/calligraphic", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS }, { "/tools/lpetool", "lpetool_tool", SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS }, { "/tools/eraser", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS }, +#if HAVE_POTRACE { "/tools/paintbucket", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS }, +#endif { "/tools/text", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS }, { "/tools/connector","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS }, { "/tools/gradient", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS }, @@ -212,8 +218,10 @@ static struct { SP_VERB_INVALID, 0, 0}, { "/tools/mesh", "mesh_toolbox", 0, sp_mesh_toolbox_prep, "MeshToolbar", SP_VERB_INVALID, 0, 0}, +#if HAVE_POTRACE { "/tools/paintbucket", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar", SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")}, +#endif { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL } }; @@ -469,6 +477,7 @@ static gchar const * ui_descr = " <separator />" " </toolbar>" +#if HAVE_POTRACE " <toolbar name='PaintbucketToolbar'>" " <toolitem action='ChannelsAction' />" " <separator />" @@ -481,6 +490,7 @@ static gchar const * ui_descr = " <separator />" " <toolitem action='PaintbucketResetAction' />" " </toolbar>" +#endif " <toolbar name='EraserToolbar'>" " <toolitem action='EraserModeAction' />" @@ -507,6 +517,8 @@ static gchar const * ui_descr = " <toolitem action='TextDyAction' />" " <toolitem action='TextRotationAction' />" " <separator />" + " <toolitem action='TextWritingModeAction' />" + " <separator />" " <toolitem action='TextOrientationAction' />" " </toolbar>" @@ -1323,8 +1335,11 @@ void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop) " <toolitem action='ToolSpray' />" " <toolitem action='ToolEraser' />" +#if HAVE_POTRACE " <!-- Fill -->" " <toolitem action='ToolPaintBucket' />" +#endif + " <toolitem action='ToolGradient' />" #ifdef WITH_MESH " <toolitem action='ToolMesh' />" diff --git a/src/xml/quote.cpp b/src/xml/quote.cpp index 02c12dfb0..b889b890d 100644 --- a/src/xml/quote.cpp +++ b/src/xml/quote.cpp @@ -1,81 +1,77 @@ /** \file - * XML quoting routines. - */ - -/* Based on Lauris' repr_quote_write in repr-io.cpp. - * - * Copyright (C) 1999-2002 Lauris Kaplinski - * Copyright (C) 2004 Monash University - * - * May be modified and/or redistributed under the terms of version 2 - * of the GNU General Public License: see the file `COPYING'. + * @brief XML quoting routines + *//* + * Authors: + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * This file is in the public domain. */ +#include "xml/quote.h" #include <cstring> #include <glib.h> -#include "quote.h" - -/** \return strlen(xml_quote_strdup(\a val)) (without doing the malloc). - * \pre val != NULL - */ -size_t -xml_quoted_strlen(char const *val) +/// Returns the length of the string after quoting the characters <code>"&<></code>. +size_t xml_quoted_strlen(char const *val) { - size_t ret = 0; - if (val != NULL) { - for (; *val != '\0'; val++) { - switch (*val) { - case '"': ret += sizeof(""") - 1; break; - case '&': ret += sizeof("&") - 1; break; - case '<': ret += sizeof("<") - 1; break; - case '>': ret += sizeof(">") - 1; break; - default: ++ret; break; - } + if (!val) return 0; + size_t len = 0; + + for (char const *valp = val; *valp; ++valp) { + switch (*valp) { + case '"': + len += 6; // " + break; + case '&': + len += 5; // & + break; + case '<': + case '>': + len += 4; // < or > + break; + default: + ++len; + break; } } - return ret; + return len; } -/** Writes \a src (including the NUL byte) to \a dest, doing XML quoting as necessary. - * - * \pre \a src != NULL. - * \pre \a dest must have enough space for (xml_quoted_strlen(src) + 1) bytes. - */ -static void -xml_quote(char *dest, char const *src) +char *xml_quote_strdup(char const *src) { -#define COPY_LIT(_lit) do { \ - size_t cpylen = sizeof(_lit "") - 1; \ - memcpy(dest, _lit, cpylen); \ - dest += cpylen; \ - } while(0) + size_t len = xml_quoted_strlen(src); + char *result = static_cast<char*>(g_malloc(len + 1)); + char *resp = result; - for (; *src != '\0'; ++src) { - switch (*src) { - case '"': COPY_LIT("""); break; - case '&': COPY_LIT("&"); break; - case '<': COPY_LIT("<"); break; - case '>': COPY_LIT(">"); break; - default: *dest++ = *src; break; + for (char const *srcp = src; *srcp; ++srcp) { + switch(*srcp) { + case '"': + strcpy(resp, """); + resp += 6; + break; + case '&': + strcpy(resp, "&"); + resp += 5; + break; + case '<': + strcpy(resp, "<"); + resp += 4; + break; + case '>': + strcpy(resp, ">"); + resp += 4; + break; + default: + *resp++ = *srcp; + break; } } - *dest = '\0'; - -#undef COPY_LIT + *resp = 0; + return result; } -/** \return A g_malloc'd buffer containing an XML-quoted version of \a src. - * \pre src != NULL. - */ -char * -xml_quote_strdup(char const *src) -{ - size_t const quoted_size = xml_quoted_strlen(src) + 1; - char *ret = (char *) g_malloc(quoted_size); - xml_quote(ret, src); - return ret; -} +// quote: ", &, <, > + /* Local Variables: |
