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
12640 {
12641 // std::cout << "generateNewPoint(): command=" << mode._command
12642 // << std::hex << " hex=" << (int)mode._command << std::dec << '\n';
12643  auto nb = val.size();
12644 // priv::printVector( val,"generateNewPoint()" );
12645  HOMOG2D_LOG( "abs/rel=" << (mode.isAbsolute()?"ABS":"REL") );
12646 // some checking to lighten up the following code, maybe can be removed afterwards
12647  switch( mode._command )
12648  {
12649  case 'M':
12650  case 'L':
12651  assert( nb == 2 );
12652  break;
12653  case 'H':
12654  case 'V':
12655  assert( nb == 1 );
12656  break;
12657  case 'Z':
12658  assert( nb == 0 );
12659  break;
12660  default: assert(0);
12661  }
12662 
12663  Point2d_<double> out;
12664  switch( mode._command )
12665  {
12666  case 'M':
12667  case 'L':
12668  if( mode.isAbsolute() )
12669  out.set( val[0], val[1] );
12670  else
12671  out.set( prevPt.getX() + val[0], prevPt.getY() + val[1] );
12672  break;
12673 
12674  case 'H': // Horizontal
12675  if( mode.isAbsolute() )
12676  out.set( val[0], prevPt.getY() );
12677  else
12678  out.set( val[0] + prevPt.getX() , prevPt.getY() );
12679  break;
12680 
12681  case 'V': // Vertical
12682  if( mode.isAbsolute() )
12683  out.set( prevPt.getX(), val[0] );
12684  else
12685  out.set( prevPt.getX(), val[0] + prevPt.getY() );
12686  break;
12687 
12688  default: assert(0);
12689  }
12690  return out;
12691 }
#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?
12934 {
12935  const char *pts = e.Attribute( attribName );
12936  if( !pts )
12937  throw std::string("h2d::img::svg Error: unable to find attribute '") + attribName + "' in tag " + e.Name();
12938  return pts;
12939 }
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.

12919 {
12920  double value=0.;
12921  if( tinyxml2::XML_SUCCESS != e.QueryDoubleAttribute( str, &value ) )
12922  HOMOG2D_THROW_ERROR_1( "h2d::svg::import error, failed to read attribute '"
12923  << std::string{str} << "' while reading element '" << e_name << "'" );
12924  return value;
12925 }
#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
12696 {
12697  HOMOG2D_LOG( "search command for " << c );
12698 
12699  static std::string commands( "MLHVCSQTAZ" ); // allowed SVG path commands (and their counterparts relative)
12700  SvgPathCommand out;
12701  std::string str( 1, c );
12702  HOMOG2D_LOG( " str=" << str << " #=" << str.size() );
12703  auto pos = commands.find( str );
12704 // HOMOG2D_LOG( "pos=" << pos << " str=" << str );
12705  bool invalid = false;
12706  if( pos == std::string::npos )
12707  {
12708  if( c>='a' && c <= 'z' ) // check if lowercase
12709  {
12710  c = (char)((int)c+'A'-'a');
12711 // std::cout << "lowercase, new c=" << c << '\n';
12712  str[0] = c;
12713 // HOMOG2D_LOG( " str2=" << str << "#=" << str.size() );
12714  pos = commands.find( str );
12715  if( pos != std::string::npos )
12716  out._absRel = PathMode::Relative;
12717  else
12718  invalid = true;
12719  }
12720  else
12721  invalid = true;
12722  }
12723  if( invalid )
12725  "Illegal character in SVG path element:-" << str
12726  << "- ascii=" << std::hex << str[0] << std::dec
12727  );
12728 
12729  out.setCommand( commands[pos] );
12730 
12731 // std::cout << "pos=" << pos << " _command=" << out._command << " _nbValues=" << (int)out._nbValues << '\n';
12732  return out;
12733 }
#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.

12487 {
12488  std::string s(rot_str);
12489 // std::cout << __FUNCTION__ << "(): " << s << "\n";
12490  auto v1 = tokenize( s, '(' );
12491  if( v1.size() == 2 )
12492  if( v1[0] == "rotate" )
12493  {
12494  auto v2 = v1[1].substr(0, v1[1].size() - 1 );
12495 // std::cout << __FUNCTION__ << "(): v2=" << v2 << "\n";
12496  auto v3 = tokenize( v2, ',' );
12497  if( v3.size() == 3 )
12498  {
12499  try // in case of an incorrect numerical string
12500  {
12501  auto angle = std::stod( v3[0] );
12502  auto x0 = std::stod( v3[1] );
12503  auto y0 = std::stod( v3[2] );
12504  return std::make_pair( Point2d_<HOMOG2D_INUMTYPE>(x0,y0),angle*M_PI/180. );
12505  }
12506  catch( std::exception& err )
12507  {
12508  HOMOG2D_THROW_ERROR_2( "invalid 'transform' attribute for svg ellipse import", err.what() );
12509  }
12510  }
12511  }
12512  HOMOG2D_THROW_ERROR_1( "invalid 'transform' attribute for svg ellipse import" );
12513 }
#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:12471
#define M_PI
Definition: homog2d.hpp:235
HOMOG2D_INUMTYPE angle(const Ellipse_< FPT > &ell)
Return angle of ellipse (free function)
Definition: homog2d.hpp:10994
HOMOG2D_INUMTYPE size(const T &elem)
Returns size of element or variant (free function)
Definition: homog2d.hpp:10254
#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.

12559 {
12560  std::string out;
12561  while( *it == ' ' || *it == ',' ) // skip spaces and commas
12562  it++;
12563 
12564  if( it >= str.cend() )
12565  return out; // return empty string
12566 
12567  if( !isDigit(*it) ) // alpha character
12568  {
12569  out.push_back( *it );
12570  it++;
12571  }
12572  else
12573  {
12574  while( isDigit(*it) && it < str.cend() )
12575  {
12576  out.push_back( *it );
12577  it++;
12578  }
12579 // std::cout << "getNextElem() DIGITS:-" << out << "- #=" << out.size() << '\n';
12580 // return out;
12581  }
12582  return out;
12583 }
bool isDigit(char c)
Definition: homog2d.hpp:12542
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.

12945 {
12946  auto pts_str = svgp::getAttribString( "points", e );
12947  auto vec_pts = svgp::parsePoints( pts_str );
12948 
12949  if( vec_pts.front() == vec_pts.back() ) // if first point equal to last
12950  vec_pts.pop_back(); // point, remove last point
12951 
12952  return vec_pts;
12953 }
const char * getAttribString(const char *attribName, const tinyxml2::XMLElement &e)
Helper function for SVG import.
Definition: homog2d.hpp:12933
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:12521
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
12543 {
12544  return ( (c >= '0' && c <= '9') || c == '.' || c == '-' );
12545 }
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.

12590 {
12591  static std::map<char,int> nbval;
12592  nbval['M'] = 2; // M-m
12593  nbval['L'] = 2; // L-l
12594  nbval['H'] = 1; // H-h
12595  nbval['V'] = 1; // V-v
12596  nbval['C'] = 6; // C-c !NOT HANDLED!
12597  nbval['S'] = 4; // S-s !NOT HANDLED!
12598  nbval['Q'] = 4; // Q-q !NOT HANDLED!
12599  nbval['T'] = 2; // T t !NOT HANDLED!
12600  nbval['A'] = 7; // A-a !NOT HANDLED!
12601  nbval['Z'] = 0; // Z-z
12602  return nbval;
12603 }
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

12798 {
12799  SvgPathCommand mode;
12800  std::vector<std::vector<Point2d>> vout(1);
12801  SvgValuesBuffer values;
12802  std::string str(s);
12803  size_t idx = 0;
12804  HOMOG2D_LOG( "parsing string -" << str << "- #=" << str.size() );
12805  if( str.size() == 0 )
12806  HOMOG2D_THROW_ERROR_1( "SVG path string is empty" );
12807 
12808  auto it = str.cbegin();
12809  Point2d previousPt;
12810  do
12811  {
12812  auto e = getNextElem( str, it );
12813 // HOMOG2D_LOG( "parsing element -" << e << "- #=" << e.size() );
12814 
12815  if( e.size() == 1 && !isDigit(e[0]) ) // we have a command !
12816  {
12817  if( values.size() != 0 ) // if we have some values stored,
12818  values.storeValues( vout[idx], mode ); // first process them and add new point
12819 
12820  mode = getCommand( e[0] );
12821  HOMOG2D_LOG( "command=" << e[0] );
12822  if( !svgPathCommandIsAllowed(mode._command) )
12823  HOMOG2D_THROW_ERROR_1( "SVG path command -" << mode._command << "- not handled" );
12824 
12825  if( mode.isNewPolyline() && !vout[idx].empty() )
12826  {
12827  vout.push_back( std::vector<Point2d>() );
12828  idx++;
12829  HOMOG2D_LOG( "NEW vector idx=" << idx );
12830  }
12831  }
12832  else // not a command, but a value
12833  {
12834 // HOMOG2D_LOG( "process value, values size=" << values.size() );
12835  if( values.size() == (size_t)mode._nbValues ) // already got enough values
12836  values.storeValues( vout[idx], mode );
12837  values.addValue( e );
12838  }
12839  }
12840  while( it < str.cend() );
12841 
12842  if( values.size() ) // process remaining values that have been stored
12843  values.storeValues( vout[idx], mode );
12844 
12845  HOMOG2D_LOG( "Nb vectors=" << vout.size() );
12846  for( auto& v: vout )
12847  if( v.size() )
12848  v = purgeSetDupes( v );
12849  if( vout.back().empty() )
12850  vout.pop_back();
12851 
12852  return std::make_pair(
12853  vout,
12854  mode._command == 'Z' ? true : false
12855  );
12856 }
SvgPathCommand getCommand(char c)
Definition: homog2d.hpp:12695
bool svgPathCommandIsAllowed(char c)
Definition: homog2d.hpp:12548
#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:12739
Point2d_< HOMOG2D_INUMTYPE > Point2d
Default point type, uses double as numerical type.
Definition: homog2d.hpp:12380
std::string getNextElem(const std::string &str, std::string::const_iterator &it)
Get next element in svg path string.
Definition: homog2d.hpp:12558
bool isDigit(char c)
Definition: homog2d.hpp:12542
#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?
12522 {
12523  std::vector<Point2d> out;
12524  std::string s(pts);
12525 // std::cout << "processing " << s << '\n';
12526 // trimString( s );
12527  auto v1 = tokenize( s, ' ' );
12528  for( const auto& pt: v1 )
12529  {
12530  auto v2 = tokenize( pt, ',' );
12531  if( v2.size() != 2 )
12532  throw "h2d:img::svg: invalid point format in importing svg element: " + s;
12533  auto x = std::stod( v2[0] );
12534  auto y = std::stod( v2[1] );
12535  out.emplace_back( Point2d(x,y) );
12536  }
12537  return out;
12538 }
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:12471
Point2d_< HOMOG2D_INUMTYPE > Point2d
Default point type, uses double as numerical type.
Definition: homog2d.hpp:12380
Point2d pt
Definition: homog2d_test.cpp:4034
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.

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

◆ svgPathCommandIsAllowed()

bool h2d::svg::svgp::svgPathCommandIsAllowed ( char  c)
inline
12549 {
12550  if( c == 'M' || c == 'L' || c == 'H'|| c == 'V' || c == 'Z' )
12551  return true;
12552  return false;
12553 }
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.

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