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

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

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

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

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

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

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

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

◆ svgPathCommandIsAllowed()

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

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