homog2d library
Classes | Enumerations | Functions
h2d::svg::svgp Namespace Reference

Private functions related to the SVG import code. More...

Classes

struct  SvgPathCommand
 Holds the current SVG "path" command, and the number of required numerical values. More...
 
struct  SvgValuesBuffer
 This will hold the values read from the SVG Path parsing code, before they are converted to points. More...
 

Enumerations

enum  PathMode { PathMode::Absolute, PathMode::Relative }
 

Functions

Point2d_< double > generateNewPoint (SvgPathCommand mode, Point2d_< double > prevPt, const std::vector< double > &val)
 Generate new point from current mode and previous point, handles absolute/relative coordinates. More...
 
const char * getAttribString (const char *attribName, const tinyxml2::XMLElement &e)
 Helper function for SVG import. More...
 
double getAttribValue (const tinyxml2::XMLElement &e, const char *str, std::string e_name)
 Fetch attribute from XML element. Tag e_name is there just in case of trouble. More...
 
SvgPathCommand getCommand (char c)
 
std::pair< Point2d_< HOMOG2D_INUMTYPE >, HOMOG2D_INUMTYPEgetEllipseRotateAttr (const char *rot_str)
 Importing rotated ellipse from SVG data. More...
 
std::string getNextElem (const std::string &str, std::string::const_iterator &it)
 Get next element in svg path string. More...
 
std::vector< Point2dimportSvgPoints (const tinyxml2::XMLElement &e)
 Helper function called by Visitor::VisitExit() to process Polyline/Polygons. More...
 
bool isDigit (char c)
 
std::map< char, int > & numberValues ()
 Returns nb of expected values for a given SVG path command. Ref: https://www.w3.org/TR/SVG2/paths.html. More...
 
auto parsePath (const char *s)
 Parse a SVG "path" string and convert it to a vector holding a set (vector) of points. More...
 
std::vector< Point2dparsePoints (const char *pts)
 Svg import: Basic parsing of points that are in the format "10,20 30,40 50,60". More...
 
template<typename FPT >
std::vector< Point2d_< FPT > > purgeSetDupes (const std::vector< Point2d_< FPT >> &pts)
 Removes dupes in set of points. Needed when importing SVG files using a "path" command, because sometimes they hold duplicates points, and that can't be in polylines. More...
 
bool svgPathCommandIsAllowed (char c)
 
std::vector< std::string > tokenize (const std::string &s, char delim)
 General string tokenizer, taken from http://stackoverflow.com/a/236803/193789. More...
 

Detailed Description

Private functions related to the SVG import code.

Enumeration Type Documentation

◆ PathMode

Function Documentation

◆ generateNewPoint()

Point2d_<double> h2d::svg::svgp::generateNewPoint ( SvgPathCommand  mode,
Point2d_< double >  prevPt,
const std::vector< double > &  val 
)
inline

Generate new point from current mode and previous point, handles absolute/relative coordinates.

Parameters
modeSVG path command
prevPtprevious point, is needed if relative mode
valnumerical values that have been stored
12643 {
12644 // std::cout << "generateNewPoint(): command=" << mode._command
12645 // << std::hex << " hex=" << (int)mode._command << std::dec << '\n';
12646  auto nb = val.size();
12647 // priv::printVector( val,"generateNewPoint()" );
12648  HOMOG2D_LOG( "abs/rel=" << (mode.isAbsolute()?"ABS":"REL") );
12649 // some checking to lighten up the following code, maybe can be removed afterwards
12650  switch( mode._command )
12651  {
12652  case 'M':
12653  case 'L':
12654  assert( nb == 2 );
12655  break;
12656  case 'H':
12657  case 'V':
12658  assert( nb == 1 );
12659  break;
12660  case 'Z':
12661  assert( nb == 0 );
12662  break;
12663  default: assert(0);
12664  }
12665 
12666  Point2d_<double> out;
12667  switch( mode._command )
12668  {
12669  case 'M':
12670  case 'L':
12671  if( mode.isAbsolute() )
12672  out.set( val[0], val[1] );
12673  else
12674  out.set( prevPt.getX() + val[0], prevPt.getY() + val[1] );
12675  break;
12676 
12677  case 'H': // Horizontal
12678  if( mode.isAbsolute() )
12679  out.set( val[0], prevPt.getY() );
12680  else
12681  out.set( val[0] + prevPt.getX() , prevPt.getY() );
12682  break;
12683 
12684  case 'V': // Vertical
12685  if( mode.isAbsolute() )
12686  out.set( prevPt.getX(), val[0] );
12687  else
12688  out.set( prevPt.getX(), val[0] + prevPt.getY() );
12689  break;
12690 
12691  default: assert(0);
12692  }
12693  return out;
12694 }
#define HOMOG2D_LOG(a)
Definition: homog2d.hpp:115
Here is the call graph for this function:
Here is the caller graph for this function:

◆ getAttribString()

const char* h2d::svg::svgp::getAttribString ( const char *  attribName,
const tinyxml2::XMLElement &  e 
)
inline

Helper function for SVG import.

Todo:
Who owns the data? Should we return a string and/or release the memory?
12937 {
12938  const char *pts = e.Attribute( attribName );
12939  if( !pts )
12940  throw std::string("h2d::img::svg Error: unable to find attribute '") + attribName + "' in tag " + e.Name();
12941  return pts;
12942 }
Here is the caller graph for this function:

◆ getAttribValue()

double h2d::svg::svgp::getAttribValue ( const tinyxml2::XMLElement &  e,
const char *  str,
std::string  e_name 
)
inline

Fetch attribute from XML element. Tag e_name is there just in case of trouble.

12922 {
12923  double value=0.;
12924  if( tinyxml2::XML_SUCCESS != e.QueryDoubleAttribute( str, &value ) )
12925  HOMOG2D_THROW_ERROR_1( "h2d::svg::import error, failed to read attribute '"
12926  << std::string{str} << "' while reading element '" << e_name << "'" );
12927  return value;
12928 }
#define HOMOG2D_THROW_ERROR_1(msg)
Error throw wrapper macro.
Definition: homog2d.hpp:181
Here is the caller graph for this function:

◆ getCommand()

SvgPathCommand h2d::svg::svgp::getCommand ( char  c)
inline
12699 {
12700  HOMOG2D_LOG( "search command for " << c );
12701 
12702  static std::string commands( "MLHVCSQTAZ" ); // allowed SVG path commands (and their counterparts relative)
12703  SvgPathCommand out;
12704  std::string str( 1, c );
12705  HOMOG2D_LOG( " str=" << str << " #=" << str.size() );
12706  auto pos = commands.find( str );
12707 // HOMOG2D_LOG( "pos=" << pos << " str=" << str );
12708  bool invalid = false;
12709  if( pos == std::string::npos )
12710  {
12711  if( c>='a' && c <= 'z' ) // check if lowercase
12712  {
12713  c = (char)((int)c+'A'-'a');
12714 // std::cout << "lowercase, new c=" << c << '\n';
12715  str[0] = c;
12716 // HOMOG2D_LOG( " str2=" << str << "#=" << str.size() );
12717  pos = commands.find( str );
12718  if( pos != std::string::npos )
12719  out._absRel = PathMode::Relative;
12720  else
12721  invalid = true;
12722  }
12723  else
12724  invalid = true;
12725  }
12726  if( invalid )
12728  "Illegal character in SVG path element:-" << str
12729  << "- ascii=" << std::hex << str[0] << std::dec
12730  );
12731 
12732  out.setCommand( commands[pos] );
12733 
12734 // std::cout << "pos=" << pos << " _command=" << out._command << " _nbValues=" << (int)out._nbValues << '\n';
12735  return out;
12736 }
#define HOMOG2D_LOG(a)
Definition: homog2d.hpp:115
#define HOMOG2D_THROW_ERROR_1(msg)
Error throw wrapper macro.
Definition: homog2d.hpp:181
Here is the call graph for this function:
Here is the caller graph for this function:

◆ getEllipseRotateAttr()

std::pair<Point2d_<HOMOG2D_INUMTYPE>,HOMOG2D_INUMTYPE> h2d::svg::svgp::getEllipseRotateAttr ( const char *  rot_str)
inline

Importing rotated ellipse from SVG data.

12490 {
12491  std::string s(rot_str);
12492 // std::cout << __FUNCTION__ << "(): " << s << "\n";
12493  auto v1 = tokenize( s, '(' );
12494  if( v1.size() == 2 )
12495  if( v1[0] == "rotate" )
12496  {
12497  auto v2 = v1[1].substr(0, v1[1].size() - 1 );
12498 // std::cout << __FUNCTION__ << "(): v2=" << v2 << "\n";
12499  auto v3 = tokenize( v2, ',' );
12500  if( v3.size() == 3 )
12501  {
12502  try // in case of an incorrect numerical string
12503  {
12504  auto angle = std::stod( v3[0] );
12505  auto x0 = std::stod( v3[1] );
12506  auto y0 = std::stod( v3[2] );
12507  return std::make_pair( Point2d_<HOMOG2D_INUMTYPE>(x0,y0),angle*M_PI/180. );
12508  }
12509  catch( std::exception& err )
12510  {
12511  HOMOG2D_THROW_ERROR_2( "invalid 'transform' attribute for svg ellipse import", err.what() );
12512  }
12513  }
12514  }
12515  HOMOG2D_THROW_ERROR_1( "invalid 'transform' attribute for svg ellipse import" );
12516 }
#define HOMOG2D_THROW_ERROR_2(func, msg)
Error throw wrapper macro, first arg is the function name.
Definition: homog2d.hpp:191
std::vector< std::string > tokenize(const std::string &s, char delim)
General string tokenizer, taken from http://stackoverflow.com/a/236803/193789.
Definition: homog2d.hpp:12474
#define M_PI
Definition: homog2d.hpp:235
HOMOG2D_INUMTYPE angle(const Ellipse_< FPT > &ell)
Return angle of ellipse (free function)
Definition: homog2d.hpp:10997
HOMOG2D_INUMTYPE size(const T &elem)
Returns size of element or variant (free function)
Definition: homog2d.hpp:10257
#define HOMOG2D_THROW_ERROR_1(msg)
Error throw wrapper macro.
Definition: homog2d.hpp:181
Here is the call graph for this function:
Here is the caller graph for this function:

◆ getNextElem()

std::string h2d::svg::svgp::getNextElem ( const std::string &  str,
std::string::const_iterator &  it 
)
inline

Get next element in svg path string.

12562 {
12563  std::string out;
12564  while( *it == ' ' || *it == ',' ) // skip spaces and commas
12565  it++;
12566 
12567  if( it >= str.cend() )
12568  return out; // return empty string
12569 
12570  if( !isDigit(*it) ) // alpha character
12571  {
12572  out.push_back( *it );
12573  it++;
12574  }
12575  else
12576  {
12577  while( isDigit(*it) && it < str.cend() )
12578  {
12579  out.push_back( *it );
12580  it++;
12581  }
12582 // std::cout << "getNextElem() DIGITS:-" << out << "- #=" << out.size() << '\n';
12583 // return out;
12584  }
12585  return out;
12586 }
bool isDigit(char c)
Definition: homog2d.hpp:12545
Here is the call graph for this function:
Here is the caller graph for this function:

◆ importSvgPoints()

std::vector<Point2d> h2d::svg::svgp::importSvgPoints ( const tinyxml2::XMLElement &  e)
inline

Helper function called by Visitor::VisitExit() to process Polyline/Polygons.

12948 {
12949  auto pts_str = svgp::getAttribString( "points", e );
12950  auto vec_pts = svgp::parsePoints( pts_str );
12951 
12952  if( vec_pts.front() == vec_pts.back() ) // if first point equal to last
12953  vec_pts.pop_back(); // point, remove last point
12954 
12955  return vec_pts;
12956 }
const char * getAttribString(const char *attribName, const tinyxml2::XMLElement &e)
Helper function for SVG import.
Definition: homog2d.hpp:12936
std::vector< Point2d > parsePoints(const char *pts)
Svg import: Basic parsing of points that are in the format "10,20 30,40 50,60".
Definition: homog2d.hpp:12524
Here is the call graph for this function:
Here is the caller graph for this function:

◆ isDigit()

bool h2d::svg::svgp::isDigit ( char  c)
inline
12546 {
12547  return ( (c >= '0' && c <= '9') || c == '.' || c == '-' );
12548 }
Here is the caller graph for this function:

◆ numberValues()

std::map<char,int>& h2d::svg::svgp::numberValues ( )
inline

Returns nb of expected values for a given SVG path command. Ref: https://www.w3.org/TR/SVG2/paths.html.

12593 {
12594  static std::map<char,int> nbval;
12595  nbval['M'] = 2; // M-m
12596  nbval['L'] = 2; // L-l
12597  nbval['H'] = 1; // H-h
12598  nbval['V'] = 1; // V-v
12599  nbval['C'] = 6; // C-c !NOT HANDLED!
12600  nbval['S'] = 4; // S-s !NOT HANDLED!
12601  nbval['Q'] = 4; // Q-q !NOT HANDLED!
12602  nbval['T'] = 2; // T t !NOT HANDLED!
12603  nbval['A'] = 7; // A-a !NOT HANDLED!
12604  nbval['Z'] = 0; // Z-z
12605  return nbval;
12606 }
Here is the caller graph for this function:

◆ parsePath()

auto h2d::svg::svgp::parsePath ( const char *  s)
inline

Parse a SVG "path" string and convert it to a vector holding a set (vector) of points.

Input string example:

m 261.68497,138.79393 2.57,3.15 -0.72,1.27 2.18,1.94 -0.7,4.93 1.88,0.9

The return value holds as 'second' a bool value, will be true if closed polyline

12801 {
12802  SvgPathCommand mode;
12803  std::vector<std::vector<Point2d>> vout(1);
12804  SvgValuesBuffer values;
12805  std::string str(s);
12806  size_t idx = 0;
12807  HOMOG2D_LOG( "parsing string -" << str << "- #=" << str.size() );
12808  if( str.size() == 0 )
12809  HOMOG2D_THROW_ERROR_1( "SVG path string is empty" );
12810 
12811  auto it = str.cbegin();
12812  Point2d previousPt;
12813  do
12814  {
12815  auto e = getNextElem( str, it );
12816 // HOMOG2D_LOG( "parsing element -" << e << "- #=" << e.size() );
12817 
12818  if( e.size() == 1 && !isDigit(e[0]) ) // we have a command !
12819  {
12820  if( values.size() != 0 ) // if we have some values stored,
12821  values.storeValues( vout[idx], mode ); // first process them and add new point
12822 
12823  mode = getCommand( e[0] );
12824  HOMOG2D_LOG( "command=" << e[0] );
12825  if( !svgPathCommandIsAllowed(mode._command) )
12826  HOMOG2D_THROW_ERROR_1( "SVG path command -" << mode._command << "- not handled" );
12827 
12828  if( mode.isNewPolyline() && !vout[idx].empty() )
12829  {
12830  vout.push_back( std::vector<Point2d>() );
12831  idx++;
12832  HOMOG2D_LOG( "NEW vector idx=" << idx );
12833  }
12834  }
12835  else // not a command, but a value
12836  {
12837 // HOMOG2D_LOG( "process value, values size=" << values.size() );
12838  if( values.size() == (size_t)mode._nbValues ) // already got enough values
12839  values.storeValues( vout[idx], mode );
12840  values.addValue( e );
12841  }
12842  }
12843  while( it < str.cend() );
12844 
12845  if( values.size() ) // process remaining values that have been stored
12846  values.storeValues( vout[idx], mode );
12847 
12848  HOMOG2D_LOG( "Nb vectors=" << vout.size() );
12849  for( auto& v: vout )
12850  if( v.size() )
12851  v = purgeSetDupes( v );
12852  if( vout.back().empty() )
12853  vout.pop_back();
12854 
12855  return std::make_pair(
12856  vout,
12857  mode._command == 'Z' ? true : false
12858  );
12859 }
SvgPathCommand getCommand(char c)
Definition: homog2d.hpp:12698
bool svgPathCommandIsAllowed(char c)
Definition: homog2d.hpp:12551
#define HOMOG2D_LOG(a)
Definition: homog2d.hpp:115
std::vector< Point2d_< FPT > > purgeSetDupes(const std::vector< Point2d_< FPT >> &pts)
Removes dupes in set of points. Needed when importing SVG files using a "path" command, because sometimes they hold duplicates points, and that can&#39;t be in polylines.
Definition: homog2d.hpp:12742
Point2d_< HOMOG2D_INUMTYPE > Point2d
Default point type, uses double as numerical type.
Definition: homog2d.hpp:12383
std::string getNextElem(const std::string &str, std::string::const_iterator &it)
Get next element in svg path string.
Definition: homog2d.hpp:12561
bool isDigit(char c)
Definition: homog2d.hpp:12545
#define HOMOG2D_THROW_ERROR_1(msg)
Error throw wrapper macro.
Definition: homog2d.hpp:181
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parsePoints()

std::vector<Point2d> h2d::svg::svgp::parsePoints ( const char *  pts)
inline

Svg import: Basic parsing of points that are in the format "10,20 30,40 50,60".

Todo:
20240326: this is used to import SVG polygon type. Maybe this can be replaced by the "path" import code?
12525 {
12526  std::vector<Point2d> out;
12527  std::string s(pts);
12528 // std::cout << "processing " << s << '\n';
12529 // trimString( s );
12530  auto v1 = tokenize( s, ' ' );
12531  for( const auto& pt: v1 )
12532  {
12533  auto v2 = tokenize( pt, ',' );
12534  if( v2.size() != 2 )
12535  throw "h2d:img::svg: invalid point format in importing svg element: " + s;
12536  auto x = std::stod( v2[0] );
12537  auto y = std::stod( v2[1] );
12538  out.emplace_back( Point2d(x,y) );
12539  }
12540  return out;
12541 }
std::vector< std::string > tokenize(const std::string &s, char delim)
General string tokenizer, taken from http://stackoverflow.com/a/236803/193789.
Definition: homog2d.hpp:12474
Point2d_< HOMOG2D_INUMTYPE > Point2d
Default point type, uses double as numerical type.
Definition: homog2d.hpp:12383
Point2d pt
Definition: homog2d_test.cpp:4052
Here is the call graph for this function:
Here is the caller graph for this function:

◆ purgeSetDupes()

template<typename FPT >
std::vector<Point2d_<FPT> > h2d::svg::svgp::purgeSetDupes ( const std::vector< Point2d_< FPT >> &  pts)

Removes dupes in set of points. Needed when importing SVG files using a "path" command, because sometimes they hold duplicates points, and that can't be in polylines.

12743 {
12744  std::vector<Point2d_<FPT>> out;
12745  out.reserve( pts.size() );
12746  for( auto it=pts.begin(); it!=std::prev(pts.end()); it++ )
12747  {
12748  const auto& elem = *it;
12749  const auto& next = *std::next(it);
12750  if( elem != next )
12751  out.push_back( elem );
12752  }
12753  out.push_back( pts.back() ); // add last one
12754  return out;
12755 }
Here is the caller graph for this function:

◆ svgPathCommandIsAllowed()

bool h2d::svg::svgp::svgPathCommandIsAllowed ( char  c)
inline
12552 {
12553  if( c == 'M' || c == 'L' || c == 'H'|| c == 'V' || c == 'Z' )
12554  return true;
12555  return false;
12556 }
Here is the caller graph for this function:

◆ tokenize()

std::vector<std::string> h2d::svg::svgp::tokenize ( const std::string &  s,
char  delim 
)
inline

General string tokenizer, taken from http://stackoverflow.com/a/236803/193789.

12475 {
12476  std::vector<std::string> velems;
12477 // std::stringstream ss( TrimSpaces(s) );
12478  std::stringstream ss( s );
12479  std::string item;
12480  while( std::getline( ss, item, delim ) )
12481  velems.push_back(item);
12482 
12483  return velems;
12484 }
Here is the caller graph for this function: