SvgLoader: Implement SVG Loader and SimpleXMLParser

Load svg using fstream and parse it using SimpleXMLparser.
And Make a document tree composed of SvgNodes using the parsed data.

Change-Id: I5715b466638195844798f7b66f54f6015e7c3ae6
This commit is contained in:
JunsuChoi 2020-06-24 09:55:25 +09:00 committed by Hermet Park
parent 4d6dee91e4
commit 3a8d6821ba
6 changed files with 2966 additions and 18 deletions

View file

@ -1,5 +1,8 @@
source_file = [
'tvgSimpleXmlParser.h',
'tvgSvgLoader.h',
'tvgSvgLoaderCommon.h',
'tvgSimpleXmlParser.cpp',
'tvgSvgLoader.cpp',
]

View file

@ -0,0 +1,346 @@
#include "tvgSimpleXmlParser.h"
static const char* _simpleXmlFindWhiteSpace(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if (isspace((unsigned char)*itr)) break;
}
return itr;
}
static const char* _simpleXmlSkipWhiteSpace(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if (!isspace((unsigned char)*itr)) break;
}
return itr;
}
static const char* _simpleXmlUnskipWhiteSpace(const char* itr, const char* itrStart)
{
for (itr--; itr > itrStart; itr--) {
if (!isspace((unsigned char)*itr)) break;
}
return itr + 1;
}
static const char* _simpleXmlFindStartTag(const char* itr, const char* itrEnd)
{
return (const char*)memchr(itr, '<', itrEnd - itr);
}
static const char* _simpleXmlFindEndTag(const char* itr, const char* itrEnd)
{
bool insideQuote = false;
for (; itr < itrEnd; itr++) {
if (*itr == '"') insideQuote = !insideQuote;
if (!insideQuote) {
if ((*itr == '>') || (*itr == '<'))
return itr;
}
}
return nullptr;
}
static const char* _simpleXmlFindEndCommentTag(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if ((*itr == '-') && ((itr + 1 < itrEnd) && (*(itr + 1) == '-')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
}
return nullptr;
}
static const char* _simpleXmlFindEndCdataTag(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if ((*itr == ']') && ((itr + 1 < itrEnd) && (*(itr + 1) == ']')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
}
return nullptr;
}
static const char* _simpleXmlFindDoctypeChildEndTag(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if (*itr == '>') return itr;
}
return nullptr;
}
bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data)
{
const char *itr = buf, *itrEnd = buf + bufLength;
char* tmpBuf = (char*)alloca(bufLength + 1);
if (!buf) return false;
if (!func) return false;
while (itr < itrEnd) {
const char* p = _simpleXmlSkipWhiteSpace(itr, itrEnd);
const char *key, *keyEnd, *value, *valueEnd;
char* tval;
if (p == itrEnd) return true;
key = p;
for (keyEnd = key; keyEnd < itrEnd; keyEnd++) {
if ((*keyEnd == '=') || (isspace((unsigned char)*keyEnd))) break;
}
if (keyEnd == itrEnd) return false;
if (keyEnd == key) continue;
if (*keyEnd == '=') value = keyEnd + 1;
else {
value = (const char*)memchr(keyEnd, '=', itrEnd - keyEnd);
if (!value) return false;
value++;
}
for (; value < itrEnd; value++) {
if (!isspace((unsigned char)*value)) break;
}
if (value == itrEnd) return false;
if ((*value == '"') || (*value == '\'')) {
valueEnd = (const char*)memchr(value + 1, *value, itrEnd - value);
if (!valueEnd) return false;
value++;
} else {
valueEnd = _simpleXmlFindWhiteSpace(value, itrEnd);
}
memcpy(tmpBuf, key, keyEnd - key);
tmpBuf[keyEnd - key] = '\0';
tval = tmpBuf + (keyEnd - key) + 1;
memcpy(tval, value, valueEnd - value);
tval[valueEnd - value] = '\0';
if (!func((void*)data, tmpBuf, tval)) return false;
itr = valueEnd + 1;
}
return true;
}
bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb func, const void* data)
{
const char *itr = buf, *itrEnd = buf + bufLength;
if (!buf) return false;
if (!func) return false;
#define CB(type, start, end) \
do { \
size_t _sz = end - start; \
bool _ret; \
_ret = func((void*)data, type, start, start - buf, _sz); \
if (!_ret) \
return false; \
} while (0)
while (itr < itrEnd) {
if (itr[0] == '<') {
if (itr + 1 >= itrEnd) {
CB(SimpleXMLType::Error, itr, itrEnd);
return false;
} else {
SimpleXMLType type;
size_t toff;
const char* p;
if (itr[1] == '/') {
type = SimpleXMLType::Close;
toff = 1;
} else if (itr[1] == '?') {
type = SimpleXMLType::Processing;
toff = 1;
} else if (itr[1] == '!') {
if ((itr + sizeof("<!DOCTYPE>") - 1 < itrEnd) && (!memcmp(itr + 2, "DOCTYPE", sizeof("DOCTYPE") - 1)) && ((itr[2 + sizeof("DOCTYPE") - 1] == '>') || (isspace((unsigned char)itr[2 + sizeof("DOCTYPE") - 1])))) {
type = SimpleXMLType::Doctype;
toff = sizeof("!DOCTYPE") - 1;
} else if ((itr + sizeof("<!---->") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) {
type = SimpleXMLType::Comment;
toff = sizeof("!--") - 1;
} else if ((itr + sizeof("<![CDATA[]]>") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) {
type = SimpleXMLType::CData;
toff = sizeof("![CDATA[") - 1;
} else if (itr + sizeof("<!>") - 1 < itrEnd) {
type = SimpleXMLType::DoctypeChild;
toff = sizeof("!") - 1;
} else {
type = SimpleXMLType::Open;
toff = 0;
}
} else {
type = SimpleXMLType::Open;
toff = 0;
}
if (type == SimpleXMLType::CData) p = _simpleXmlFindEndCdataTag(itr + 1 + toff, itrEnd);
else if (type == SimpleXMLType::DoctypeChild) p = _simpleXmlFindDoctypeChildEndTag(itr + 1 + toff, itrEnd);
else if (type == SimpleXMLType::Comment) p = _simpleXmlFindEndCommentTag(itr + 1 + toff, itrEnd);
else p = _simpleXmlFindEndTag(itr + 1 + toff, itrEnd);
if ((p) && (*p == '<')) {
type = SimpleXMLType::Error;
toff = 0;
}
if (p) {
const char *start, *end;
start = itr + 1 + toff;
end = p;
switch (type) {
case SimpleXMLType::Open: {
if (p[-1] == '/') {
type = SimpleXMLType::OpenEmpty;
end--;
}
break;
}
case SimpleXMLType::CData: {
if (!memcmp(p - 2, "]]", 2)) end -= 2;
break;
}
case SimpleXMLType::Processing: {
if (p[-1] == '?') end--;
break;
}
case SimpleXMLType::Comment: {
if (!memcmp(p - 2, "--", 2)) end -= 2;
break;
}
case SimpleXMLType::OpenEmpty:
case SimpleXMLType::Close:
case SimpleXMLType::Data:
case SimpleXMLType::Error:
case SimpleXMLType::Doctype:
case SimpleXMLType::DoctypeChild:
case SimpleXMLType::Ignored: {
break;
}
}
if ((strip) && (type != SimpleXMLType::Error) && (type != SimpleXMLType::CData)) {
start = _simpleXmlSkipWhiteSpace(start, end);
end = _simpleXmlUnskipWhiteSpace(end, start + 1);
}
CB(type, start, end);
if (type != SimpleXMLType::Error) itr = p + 1;
else itr = p;
} else {
CB(SimpleXMLType::Error, itr, itrEnd);
return false;
}
}
} else {
const char *p, *end;
if (strip) {
p = _simpleXmlSkipWhiteSpace(itr, itrEnd);
if (p) {
CB(SimpleXMLType::Ignored, itr, p);
itr = p;
}
}
p = _simpleXmlFindStartTag(itr, itrEnd);
if (!p) p = itrEnd;
end = p;
if (strip) end = _simpleXmlUnskipWhiteSpace(end, itr);
if (itr != end) CB(SimpleXMLType::Data, itr, end);
if ((strip) && (end < p)) CB(SimpleXMLType::Ignored, end, p);
itr = p;
}
}
#undef CB
return true;
}
bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, const void* data)
{
const char* end;
char* key;
char* val;
char* next;
if (!buf) return false;
end = buf + strlen(buf);
key = (char*)alloca(end - buf + 1);
val = (char*)alloca(end - buf + 1);
if (buf == end) return true;
do {
char* sep = (char*)strchr(buf, ':');
next = (char*)strchr(buf, ';');
key[0] = '\0';
val[0] = '\0';
if (next == nullptr && sep != nullptr) {
memcpy(key, buf, sep - buf);
key[sep - buf] = '\0';
memcpy(val, sep + 1, end - sep - 1);
val[end - sep - 1] = '\0';
} else if (sep < next && sep != nullptr) {
memcpy(key, buf, sep - buf);
key[sep - buf] = '\0';
memcpy(val, sep + 1, next - sep - 1);
val[next - sep - 1] = '\0';
} else if (next) {
memcpy(key, buf, next - buf);
key[next - buf] = '\0';
}
if (key[0]) {
if (!func((void*)data, key, val)) return false;
}
buf = next + 1;
} while (next != nullptr);
return true;
}
const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength)
{
const char *itr = buf, *itrEnd = buf + bufLength;
for (; itr < itrEnd; itr++) {
if (!isspace((unsigned char)*itr)) {
//User skip tagname and already gave it the attributes.
if (*itr == '=') return buf;
} else {
itr = _simpleXmlSkipWhiteSpace(itr + 1, itrEnd);
if (itr == itrEnd) return nullptr;
return itr;
}
}
return nullptr;
}

View file

@ -0,0 +1,31 @@
#ifndef _TVG_SIMPLE_XML_PARSER_H_
#define _TVG_SIMPLE_XML_PARSER_H_
#include <ctype.h>
#include <cstring>
#include <alloca.h>
enum class SimpleXMLType
{
Open = 0, //!< \<tag attribute="value"\>
OpenEmpty, //!< \<tag attribute="value" /\>
Close, //!< \</tag\>
Data, //!< tag text data
CData, //!< \<![cdata[something]]\>
Error, //!< error contents
Processing, //!< \<?xml ... ?\> \<?php .. ?\>
Doctype, //!< \<!doctype html
Comment, //!< \<!-- something --\>
Ignored, //!< whatever is ignored by parser, like whitespace
DoctypeChild //!< \<!doctype_child
};
typedef bool (*simpleXMLCb)(void* data, SimpleXMLType type, const char* content, unsigned offset, unsigned length);
typedef bool (*simpleXMLAttributeCb)(void* data, const char* key, const char* value);
bool simpleXmlParseAttributes(const char* buf, unsigned buflen, simpleXMLAttributeCb func, const void* data);
bool simpleXmlParse(const char* buf, unsigned buflen, bool strip, simpleXMLCb func, const void* data);
bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, const void* data);
const char *simpleXmlFindAttributesTag(const char* buf, unsigned buflen);
#endif //_TVG_SIMPLE_XML_PARSER_H_

File diff suppressed because it is too large Load diff

View file

@ -17,12 +17,13 @@
#ifndef _TVG_SVG_LOADER_H_
#define _TVG_SVG_LOADER_H_
#include "tvgCommon.h"
#include "tvgSvgLoaderCommon.h"
class SvgLoader : public Loader
{
private:
//TODO:
string content;
SvgLoaderData loaderData;
public:
SvgLoader();
@ -35,4 +36,4 @@ public:
};
#endif //_TVG_SVG_LOADER_H_
#endif //_TVG_SVG_LOADER_H_

View file

@ -0,0 +1,337 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef _TVG_SVG_LOADER_COMMON_H_
#define _TVG_SVG_LOADER_COMMON_H_
#include "tvgCommon.h"
#include "tvgSimpleXmlParser.h"
enum class SvgNodeType
{
Doc,
G,
Defs,
//Switch, //Not support
Animation,
Arc,
Circle,
Ellipse,
Image,
Line,
Path,
Polygon,
Polyline,
Rect,
Text,
TextArea,
Tspan,
Use,
Video,
//Custome_command, //Not support
Unknown
};
enum class SvgLengthType
{
Percent,
Px,
Pc,
Pt,
Mm,
Cm,
In,
};
enum class SvgFillFlags
{
Paint = 0x1,
Opacity = 0x2,
Gradient = 0x4,
FillRule = 0x8
};
enum class SvgStrokeFlags
{
Paint = 0x1,
Opacity = 0x2,
Gradient = 0x4,
Scale = 0x8,
Width = 0x10,
Cap = 0x20,
Join = 0x40,
Dash = 0x80,
};
enum class SvgGradientType
{
Linear,
Radial
};
enum class SvgStyleType
{
Quality,
Fill,
ViewportFill,
Font,
Stroke,
SolidColor,
Gradient,
Transform,
Opacity,
CompOp
};
enum class SvgGradientSpread
{
Pad = 0,
Reflect,
Repeat,
Last
};
enum class SvgFillRule
{
Winding = 0,
OddEven = 1
};
//Length type to recalculate %, pt, pc, mm, cm etc
enum class SvgParserLengthType
{
Vertical,
Horizontal,
//In case of, for example, radius of radial gradient
Other
};
typedef struct _SvgNode SvgNode;
typedef struct _SvgStyleGradient SvgStyleGradient;
struct SvgDocNode
{
float w;
float h;
float vx;
float vy;
float vw;
float vh;
SvgNode* defs;
bool preserveAspect;
};
struct SvgGNode
{
};
struct SvgDefsNode
{
vector<SvgStyleGradient *> gradients;
};
struct SvgArcNode
{
};
struct SvgEllipseNode
{
float cx;
float cy;
float rx;
float ry;
};
struct SvgCircleNode
{
float cx;
float cy;
float r;
};
struct SvgRectNode
{
float x;
float y;
float w;
float h;
float rx;
float ry;
};
struct SvgLineNode
{
float x1;
float y1;
float x2;
float y2;
};
struct SvgPathNode
{
string* path;
};
struct SvgPolygonNode
{
int pointsCount;
float* points;
};
struct SvgLinearGradient
{
float x1;
float y1;
float x2;
float y2;
};
struct SvgRadialGradient
{
float cx;
float cy;
float fx;
float fy;
float r;
};
struct SvgGradientStop
{
float offset;
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
struct SvgPaint
{
SvgStyleGradient* gradient;
string* url;
uint8_t r;
uint8_t g;
uint8_t b;
bool none;
bool curColor;
};
struct SvgDash
{
float length;
float gap;
};
struct _SvgStyleGradient
{
SvgGradientType type;
string* id;
string* ref;
SvgGradientSpread spread;
vector<SvgGradientStop *> stops;
SvgRadialGradient* radial;
SvgLinearGradient* linear;
Matrix* transform;
bool userSpace;
bool usePercentage;
};
struct SvgStyleFill
{
SvgFillFlags flags;
SvgPaint paint;
int opacity;
SvgFillRule fillRule;
};
struct SvgStyleStroke
{
SvgStrokeFlags flags;
SvgPaint paint;
int opacity;
float scale;
float width;
float centered;
StrokeCap cap;
StrokeJoin join;
SvgDash* dash;
int dashCount;
};
struct SvgStyleProperty
{
SvgStyleFill fill;
SvgStyleStroke stroke;
int opacity;
uint8_t r;
uint8_t g;
uint8_t b;
};
struct _SvgNode
{
SvgNodeType type;
SvgNode* parent;
vector<SvgNode*> child;
string* id;
SvgStyleProperty* style;
Matrix* transform;
union {
SvgGNode g;
SvgDocNode doc;
SvgDefsNode defs;
SvgArcNode arc;
SvgCircleNode circle;
SvgEllipseNode ellipse;
SvgPolygonNode polygon;
SvgPolygonNode polyline;
SvgRectNode rect;
SvgPathNode path;
SvgLineNode line;
} node;
bool display;
};
struct SvgParser
{
SvgNode* node;
SvgStyleGradient* styleGrad;
SvgGradientStop* gradStop;
struct
{
int x, y;
uint32_t w, h;
} global;
struct
{
bool parsedFx;
bool parsedFy;
} gradient;
};
struct SvgLoaderData
{
vector<SvgNode *> stack;
SvgNode* doc;
SvgNode* def;
vector<SvgStyleGradient*> gradients;
SvgStyleGradient* latestGradient; //For stops
SvgParser* svgParse;
int level;
bool result;
};
#endif