#ifndef DOC_SVG_H
#define DOC_SVG_H
#include <vector>
//#include <ctype.h>
#include "tinyxml.h"
#include "DocStyle.h"
#include "DocTemplate.h"
#include "DocOutput.h"
#include "hpdf.h"
class SVGOperation
{
public:
enum PathOperation {
MoveToAbs,
LineToAbs,
HorizontalLineToAbs,
VerticalLineToAbs,
CurveToAbs,
SmoothCurveToAbs,
QuadraticBezierCurveToAbs,
SmoothQuadraticBezierCurveToAbs,
EllipticalArcToAbs,
ClosePath,
MoveToRel,
LineToRel,
HorizontalLineToRel,
VerticalLineToRel,
CurveToRel,
SmoothCurveToRel,
QuadraticBezierCurveToRel,
SmoothQuadraticBezierCurveToRel,
EllipticalArcToRel,
AltClosePath,
BadOperation,
BeginPathOperation,
EndPathOperation
};
static const char PathOperationChar[];
static const int PathOperationArgs[];
static PathOperation getOperationForChar(char a_ch) {
for (int i = 0; i < BadOperation; i++)
if (PathOperationChar[i] == a_ch)
return (PathOperation)i;
return BadOperation;
}
int getArgCount() {
return PathOperationArgs[m_type];
}
void dump() {
fprintf(stderr, " OP: -%c- ARGS: ", PathOperationChar[m_type]);
for (int i = 0; i < PathOperationArgs[m_type]; i++)
fprintf(stderr, " -%f- ", m_values[i]);
fprintf(stderr, "\n");
}
void scale(double a_scale) {
for (int i = 0; i < PathOperationArgs[m_type]; i++)
m_values[i] *= a_scale;
}
void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) {
float pageHeight = HPDF_Page_GetHeight(page);
float x = m_values[0];
float y = m_values[1];
fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y);
switch (m_type) {
case MoveToAbs:
curPos[0] = x; curPos[1] = y;
HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]);
fprintf(stderr, "move to: %f %f\n", curPos[0], curPos[1]);
break;
case LineToAbs:
curPos[0] = x; curPos[1] = y;
HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]);
fprintf(stderr, "line to: %f %f\n", curPos[0], curPos[1]);
break;
case HorizontalLineToAbs:
curPos[0] = x;
HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]);
break;
case VerticalLineToAbs:
curPos[1] = x;
HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]);
break;
case CurveToAbs:
HPDF_Page_CurveTo(page,
m_values[0], pageHeight - m_values[1],
m_values[2], pageHeight - m_values[3],
m_values[4], pageHeight - m_values[5]);
curPos[0] = m_values[4];
curPos[1] = m_values[5];
break;
case SmoothCurveToAbs:
HPDF_Page_CurveTo2(page,
m_values[0], pageHeight - m_values[1],
m_values[2], pageHeight - m_values[3]);
curPos[0] = m_values[2];
curPos[1] = m_values[3];
break;
case QuadraticBezierCurveToAbs:
//HPDF_Page_CurveTo2(HPDF_Page, HPDF_REAL x2, HPDF_REAL y2, HPDF_REAL x3, HPDF_REAL y3);
//HPDF_Page_CurveTo3(HPDF_Page, HPDF_REAL x1, HPDF_REAL y1, HPDF_REAL x3, HPDF_REAL y3);
// HPDF_Page_CurveTo3 // ??
HPDF_Page_CurveTo2(page,
m_values[0], pageHeight - m_values[1],
m_values[2], pageHeight - m_values[3]);
curPos[0] = m_values[2];
curPos[1] = m_values[3];
break;
case SmoothQuadraticBezierCurveToAbs:
// No idea, only has 2 args, I guess an x and y, but how does this make a curve?
break;
case EllipticalArcToAbs:
// SVG has 7 args, PDF has 5!
//HPDF_Page_Arc (HPDF_Page page, HPDF_REAL x, HPDF_REAL y,
// HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2);
break;
case MoveToRel:
if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed
curPos[0] = x; curPos[1] = y;
} else {
curPos[0] += x; curPos[1] += y;
}
//curPos[0] += x; curPos[1] += y;
HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]);
break;
case LineToRel:
if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed
curPos[0] = x; curPos[1] = y;
} else {
curPos[0] += x; curPos[1] += y;
}
HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]);
break;
case HorizontalLineToRel:
curPos[0] += x;
HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]);
break;
case VerticalLineToRel:
curPos[1] += x;
HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]);
break;
case CurveToRel:
// TODO: check if these are cumulatively relative, or all rel to current pos
HPDF_Page_CurveTo(page,
curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1],
curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3],
curPos[0] + m_values[4], pageHeight - curPos[1] - m_values[5]);
curPos[0] += m_values[4];
curPos[1] += m_values[5];
break;
case SmoothCurveToRel:
// TODO: check if these are cumulatively relative, or all rel to current pos
HPDF_Page_CurveTo2(page,
curPos[0] + m_values[0], pageHeight - curPos[1] - m_values[1],
curPos[0] + m_values[2], pageHeight - curPos[1] - m_values[3]);
curPos[0] += m_values[2];
curPos[1] += m_values[3];
break;
case QuadraticBezierCurveToRel:
fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__);
break;
case SmoothQuadraticBezierCurveToRel:
fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__);
break;
case EllipticalArcToRel:
fprintf(stderr, "TODO: %s %i\n", __FILE__, __LINE__);
break;
case ClosePath:
case AltClosePath:
// TODO: actually this means to draw a line back to the first point in the path
curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1];
//HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]);
/*
if (fill)
HPDF_Page_FillStroke(page);
else
HPDF_Page_Stroke(page);
*/
break;
default:
break;
}
}
PathOperation m_type;
double m_values[7];
};
class DocSVG : public TiXmlVisitor
{
public:
DocSVG(double scale);
virtual ~DocSVG();
/// Visit a document.
virtual bool VisitEnter( const TiXmlDocument& doc );
/// Visit a document.
virtual bool VisitExit( const TiXmlDocument& doc );
/// Visit a declaration
virtual bool Visit( const TiXmlDeclaration& declaration );
/// Visit a stylesheet reference
virtual bool Visit( const TiXmlStylesheetReference& stylesheet );
/// Visit a comment node
virtual bool Visit( const TiXmlComment& comment );
/// Visit an unknow node
virtual bool Visit( const TiXmlUnknown& unknown );
/// Visit an element.
virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute );
/// Visit an element.
virtual bool VisitExit( const TiXmlElement& element );
/// Visit a text node
virtual bool Visit( const TiXmlText& text );
void DumpOperations()
{
for (size_t p = 0; p < paths.size(); ++p)
for (size_t i = 0; i < paths[p].operations.size(); i++)
paths[p].operations[i].dump();
}
void WriteTo(DocOutputDevice* outputDoc)
{
DocOutputPage* outputPage = outputDoc->newPage();
HPDF_Page page = outputPage->page();
float curPos[2] = { 10.0, 10.0 };
//fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page));
/*
HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h);
HPDF_Page_Circle(page, x, pageHeight - y, radius);
*/
HPDF_Page_EndText(page);
for (size_t p = 0; p < paths.size(); ++p)
{
uint32_t fCol = paths[p].style.fillColor;
uint32_t sCol = paths[p].style.strokeColor;
std::vector<SVGOperation> operations = paths[p].operations;
HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale);
//if ( fCol )
HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0,
((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0);
//if ( sCol )
HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0,
((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0);
HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page));
float pathStartPos[2];
pathStartPos[0] = curPos[0];
pathStartPos[1] = curPos[1];
SVGOperation::PathOperation prevOp = SVGOperation::BadOperation;
for (size_t i = 0; i < operations.size(); i++) {
SVGOperation op = operations[i];
op.scale(m_scale);
op.output(page, curPos, false, prevOp, pathStartPos);
if (prevOp == SVGOperation::BeginPathOperation) {
pathStartPos[0] = curPos[0];
pathStartPos[1] = curPos[1];
}
prevOp = op.m_type;
}
if (fCol)
HPDF_Page_ClosePathFillStroke(page);
else
HPDF_Page_Stroke(page);
/*
HPDF_Page_Stroke (HPDF_Page page)
HPDF_Page_ClosePathStroke (HPDF_Page page)
HPDF_Page_Fill (HPDF_Page page)
HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill
HPDF_Page_FillStroke (HPDF_Page page)
HPDF_Page_EofillStroke (HPDF_Page page)
HPDF_Page_ClosePathFillStroke (HPDF_Page page)
HPDF_Page_ClosePathEofillStroke (HPDF_Page page)
HPDF_Page_EndPath (HPDF_Page page)
*/
}
HPDF_Page_BeginText(page);
delete outputPage;
}
private:
struct SVGStyle {
uint32_t fillColor;
uint32_t strokeColor;
float strokeWidth;
};
struct SVGPath {
std::vector<SVGOperation> operations;
SVGStyle style;
};
SVGStyle parseStyle(const char* a_str);
uint32_t parseColor(const char* a_str);
std::vector<SVGOperation> ParsePath(const char* a_pathData)
{
std::vector<SVGOperation> operations;
SVGOperation currentOp;
currentOp.m_type = SVGOperation::BeginPathOperation;
operations.push_back(currentOp);
currentOp.m_type = SVGOperation::MoveToAbs;
int val = 0;
const char* pathPtr = a_pathData;
while (*pathPtr) {
char* end = 0;
double v = strtod(pathPtr, &end);
if ( end != pathPtr ) {
currentOp.m_values[val] = v;
val++;
if (val == currentOp.getArgCount()) {
operations.push_back(currentOp);
// The first position in a move to op is where to go to, but after that it
// is where to draw a line to
if ( currentOp.m_type == SVGOperation::MoveToAbs )
currentOp.m_type = SVGOperation::LineToAbs;
if ( currentOp.m_type == SVGOperation::MoveToRel )
currentOp.m_type = SVGOperation::LineToRel;
val = 0;
}
pathPtr = end;
} else {
if (SVGOperation::getOperationForChar(*pathPtr) != SVGOperation::BadOperation)
{
if (val != 0) {
fprintf(stderr, "Broken SVG path data, expecting more numbers, got: -%c-\n", *pathPtr);
break;
}
if (currentOp.getArgCount() == 0) // eg: ClosePath
operations.push_back(currentOp);
currentOp.m_type = SVGOperation::getOperationForChar(*pathPtr);
}
pathPtr++;
}
}
if (val == currentOp.getArgCount()) {
operations.push_back(currentOp);
} else if (val) {
fprintf(stderr, "Broken SVG path data, expecting more numbers\n");
}
currentOp.m_type = SVGOperation::EndPathOperation;
operations.push_back(currentOp);
return operations;
}
std::vector<SVGPath> paths;
double m_scale;
};
#endif // DOC_SVG_H