// FIXME: why is 'transform' argument not used? void PrintLatex::print_pathvector(SVGOStringStream &os, Geom::PathVector const &pathv_in, const Geom::Affine & /*transform*/) { if (pathv_in.empty()) return; // Geom::Affine tf=transform; // why was this here? Geom::Affine tf_stack=m_tr_stack.top(); // and why is transform argument not used? Geom::PathVector pathv = pathv_in * tf_stack; // generates new path, which is a bit slow, but this doesn't have to be performance optimized os << "\\newpath\n"; for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) { os << "\\moveto(" << it->initialPoint()[Geom::X] << "," << it->initialPoint()[Geom::Y] << ")\n"; for(Geom::Path::const_iterator cit = it->begin(); cit != it->end_open(); ++cit) { print_2geomcurve(os, *cit); } if (it->closed()) { os << "\\closepath\n"; } } }
/** Feeds path-creating calls to the cairo context translating them from the PathVector * One must have done cairo_new_path(ct); before calling this function. */ void feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv) { if (pathv.empty()) return; for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) { feed_path_to_cairo(ct, *it); } }
/** Feeds path-creating calls to the cairo context translating them from the PathVector, with the given transform and shift * One must have done cairo_new_path(ct); before calling this function. */ void feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv, Geom::Affine trans, Geom::OptRect area, bool optimize_stroke, double stroke_width) { if (!area) return; if (pathv.empty()) return; for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) { feed_path_to_cairo(ct, *it, trans, area, optimize_stroke, stroke_width); } }
int main(int argc, char **argv) { if (argc > 1) { SVGPathTestPrinter sink; Geom::parse_svg_path(&*argv[1], sink); std::cout << "Try real pathsink:" << std::endl; Geom::PathVector testpath = Geom::parse_svg_path(&*argv[1]); std::cout << "Geom::PathVector length: " << testpath.size() << std::endl; if ( !testpath.empty() ) std::cout << "Path curves: " << testpath.front().size() << std::endl; std::cout << "success!" << std::endl; } return 0; };
Geom::OptRect bounds_exact_transformed(Geom::PathVector const & pv, Geom::Affine const & t) { if (pv.empty()) return Geom::OptRect(); Geom::Point initial = pv.front().initialPoint() * t; Geom::Rect bbox(initial, initial); // obtain well defined bbox as starting point to unionWith for (Geom::PathVector::const_iterator it = pv.begin(); it != pv.end(); ++it) { bbox.expandTo(it->initialPoint() * t); // don't loop including closing segment, since that segment can never increase the bbox for (Geom::Path::const_iterator cit = it->begin(); cit != it->end_open(); ++cit) { Geom::Curve const &c = *cit; unsigned order = 0; if (Geom::BezierCurve const* b = dynamic_cast<Geom::BezierCurve const*>(&c)) { order = b->order(); } if (order == 1) { // line segment bbox.expandTo(c.finalPoint() * t); // TODO: we can make the case for quadratics faster by degree elevating them to // cubic and then taking the bbox of that. } else if (order == 3) { // cubic bezier Geom::CubicBezier const &cubic_bezier = static_cast<Geom::CubicBezier const&>(c); Geom::Point c0 = cubic_bezier[0] * t; Geom::Point c1 = cubic_bezier[1] * t; Geom::Point c2 = cubic_bezier[2] * t; Geom::Point c3 = cubic_bezier[3] * t; cubic_bbox(c0[0], c0[1], c1[0], c1[1], c2[0], c2[1], c3[0], c3[1], bbox); } else { // should handle all not-so-easy curves: Geom::Curve *ctemp = cit->transformed(t); bbox.unionWith( ctemp->boundsExact()); delete ctemp; } } } //return Geom::bounds_exact(pv * t); return bbox; }
Geom::OptRect bounds_exact_transformed(Geom::PathVector const & pv, Geom::Affine const & t) { if (pv.empty()) return Geom::OptRect(); Geom::Point initial = pv.front().initialPoint() * t; Geom::Rect bbox(initial, initial); // obtain well defined bbox as starting point to unionWith for (Geom::PathVector::const_iterator it = pv.begin(); it != pv.end(); ++it) { bbox.expandTo(it->initialPoint() * t); // don't loop including closing segment, since that segment can never increase the bbox for (Geom::Path::const_iterator cit = it->begin(); cit != it->end_open(); ++cit) { Geom::Curve const &c = *cit; if( is_straight_curve(c) ) { bbox.expandTo( c.finalPoint() * t ); } else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const *>(&c)) { Geom::Point c0 = (*cubic_bezier)[0] * t; Geom::Point c1 = (*cubic_bezier)[1] * t; Geom::Point c2 = (*cubic_bezier)[2] * t; Geom::Point c3 = (*cubic_bezier)[3] * t; cubic_bbox( c0[0], c0[1], c1[0], c1[1], c2[0], c2[1], c3[0], c3[1], bbox ); } else { // should handle all not-so-easy curves: Geom::Curve *ctemp = cit->transformed(t); bbox.unionWith( ctemp->boundsExact()); delete ctemp; } } } //return Geom::bounds_exact(pv * t); return bbox; }
/* Calculates... and returns ... in *wind and the distance to ... in *dist. Returns bounding box in *bbox if bbox!=NULL. */ void pathv_matrix_point_bbox_wind_distance (Geom::PathVector const & pathv, Geom::Affine const &m, Geom::Point const &pt, Geom::Rect *bbox, int *wind, Geom::Coord *dist, Geom::Coord tolerance, Geom::Rect const *viewbox) { if (pathv.empty()) { if (wind) *wind = 0; if (dist) *dist = Geom::infinity(); return; } // remember last point of last curve Geom::Point p0(0,0); // remembering the start of subpath Geom::Point p_start(0,0); bool start_set = false; for (Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) { if (start_set) { // this is a new subpath if (wind && (p0 != p_start)) // for correct fill picking, each subpath must be closed geom_line_wind_distance (p0[X], p0[Y], p_start[X], p_start[Y], pt, wind, dist); } p0 = it->initialPoint() * m; p_start = p0; start_set = true; if (bbox) { bbox->expandTo(p0); } // loop including closing segment if path is closed for (Geom::Path::const_iterator cit = it->begin(); cit != it->end_default(); ++cit) { geom_curve_bbox_wind_distance(*cit, m, pt, bbox, wind, dist, tolerance, viewbox, p0); } } if (start_set) { if (wind && (p0 != p_start)) // for correct picking, each subpath must be closed geom_line_wind_distance (p0[X], p0[Y], p_start[X], p_start[Y], pt, wind, dist); } }
void font_instance::LoadGlyph(int glyph_id) { if ( pFont == NULL ) { return; } InitTheFace(); #ifndef USE_PANGO_WIN32 if ( !FT_IS_SCALABLE(theFace) ) { return; // bitmap font } #endif if ( id_to_no.find(glyph_id) == id_to_no.end() ) { Geom::PathBuilder path_builder; if ( nbGlyph >= maxGlyph ) { maxGlyph=2*nbGlyph+1; glyphs=(font_glyph*)realloc(glyphs,maxGlyph*sizeof(font_glyph)); } font_glyph n_g; n_g.pathvector=NULL; n_g.bbox[0]=n_g.bbox[1]=n_g.bbox[2]=n_g.bbox[3]=0; n_g.h_advance = 0; n_g.v_advance = 0; n_g.h_width = 0; n_g.v_width = 0; bool doAdd=false; #ifdef USE_PANGO_WIN32 #ifndef GGO_UNHINTED // For compatibility with old SDKs. #define GGO_UNHINTED 0x0100 #endif MAT2 identity = {{0,1},{0,0},{0,0},{0,1}}; OUTLINETEXTMETRIC otm; GetOutlineTextMetrics(daddy->hScreenDC, sizeof(otm), &otm); GLYPHMETRICS metrics; DWORD bufferSize=GetGlyphOutline (daddy->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity); double scale=1.0/daddy->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; if ( bufferSize == GDI_ERROR) { // shit happened } else if ( bufferSize == 0) { // character has no visual representation, but is valid (eg whitespace) doAdd=true; } else { char *buffer = new char[bufferSize]; if ( GetGlyphOutline (daddy->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, bufferSize, buffer, &identity) <= 0 ) { // shit happened } else { // Platform SDK is rubbish, read KB87115 instead DWORD polyOffset=0; while ( polyOffset < bufferSize ) { TTPOLYGONHEADER const *polyHeader=(TTPOLYGONHEADER const *)(buffer+polyOffset); if (polyOffset+polyHeader->cb > bufferSize) break; if (polyHeader->dwType == TT_POLYGON_TYPE) { path_builder.moveTo(pointfx_to_nrpoint(polyHeader->pfxStart, scale)); DWORD curveOffset=polyOffset+sizeof(TTPOLYGONHEADER); while ( curveOffset < polyOffset+polyHeader->cb ) { TTPOLYCURVE const *polyCurve=(TTPOLYCURVE const *)(buffer+curveOffset); POINTFX const *p=polyCurve->apfx; POINTFX const *endp=p+polyCurve->cpfx; switch (polyCurve->wType) { case TT_PRIM_LINE: while ( p != endp ) path_builder.lineTo(pointfx_to_nrpoint(*p++, scale)); break; case TT_PRIM_QSPLINE: { g_assert(polyCurve->cpfx >= 2); // The list of points specifies one or more control points and ends with the end point. // The intermediate points (on the curve) are the points between the control points. Geom::Point this_control = pointfx_to_nrpoint(*p++, scale); while ( p+1 != endp ) { // Process all "midpoints" (all points except the last) Geom::Point new_control = pointfx_to_nrpoint(*p++, scale); path_builder.quadTo(this_control, (new_control+this_control)/2); this_control = new_control; } Geom::Point end = pointfx_to_nrpoint(*p++, scale); path_builder.quadTo(this_control, end); } break; case 3: // TT_PRIM_CSPLINE g_assert(polyCurve->cpfx % 3 == 0); while ( p != endp ) { path_builder.curveTo(pointfx_to_nrpoint(p[0], scale), pointfx_to_nrpoint(p[1], scale), pointfx_to_nrpoint(p[2], scale)); p += 3; } break; } curveOffset += sizeof(TTPOLYCURVE)+sizeof(POINTFX)*(polyCurve->cpfx-1); } } polyOffset += polyHeader->cb; } doAdd=true; } delete [] buffer; } #else if (FT_Load_Glyph (theFace, glyph_id, FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) { // shit happened } else { if ( FT_HAS_HORIZONTAL(theFace) ) { n_g.h_advance=((double)theFace->glyph->metrics.horiAdvance)/((double)theFace->units_per_EM); n_g.h_width=((double)theFace->glyph->metrics.width)/((double)theFace->units_per_EM); } else { n_g.h_width=n_g.h_advance=((double)(theFace->bbox.xMax-theFace->bbox.xMin))/((double)theFace->units_per_EM); } if ( FT_HAS_VERTICAL(theFace) ) { 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); } if ( theFace->glyph->format == ft_glyph_format_outline ) { FT_Outline_Funcs ft2_outline_funcs = { ft2_move_to, ft2_line_to, ft2_conic_to, ft2_cubic_to, 0, 0 }; FT2GeomData user(path_builder, 1.0/((double)theFace->units_per_EM)); FT_Outline_Decompose (&theFace->glyph->outline, &ft2_outline_funcs, &user); } doAdd=true; } #endif path_builder.finish(); if ( doAdd ) { Geom::PathVector pv = path_builder.peek(); // close all paths for (Geom::PathVector::iterator i = pv.begin(); i != pv.end(); ++i) { i->close(); } if ( !pv.empty() ) { n_g.pathvector = new Geom::PathVector(pv); Geom::OptRect bounds = bounds_exact(*n_g.pathvector); if (bounds) { n_g.bbox[0] = bounds->left(); n_g.bbox[1] = bounds->top(); n_g.bbox[2] = bounds->right(); n_g.bbox[3] = bounds->bottom(); } } glyphs[nbGlyph]=n_g; id_to_no[glyph_id]=nbGlyph; nbGlyph++; } } else { } }