lottie: fix & enhance parsing

The object type key does not need to be provided
first. Previously, content was skipped until the
"ty" type key was found. If the key was placed at
the end, the entire object was ignored.
Now, before parsing subsequent elements, finding
the "ty" key is enforced. Since in-situ parsing
does not support backtracking, the key is searched
with an internal function.

Introducing support for the "ty" key placed anywhere
highlighted the use of redundant logic for parsing
the list of shapes — this duplicate logic has been
removed.
This commit is contained in:
Mira Grudzinska 2025-02-07 12:53:01 +01:00
parent c1290a8205
commit 927fd537e5
4 changed files with 62 additions and 19 deletions

View file

@ -849,10 +849,12 @@ LottieOffsetPath* LottieParser::parseOffsetPath()
}
LottieObject* LottieParser::parseObject()
LottieObject* LottieParser::parseObject(const char* type)
{
auto type = getString();
if (!type) {
type = getString();
if (!type) return nullptr;
}
if (!strcmp(type, "gr")) return parseGroup();
else if (!strcmp(type, "rc")) return parseRect();
@ -879,6 +881,24 @@ LottieObject* LottieParser::parseObject()
void LottieParser::parseObject(Array<LottieObject*>& parent)
{
enterObject();
//object type key is not listed as the first one
auto value = peekValue();
if (value && strcmp(value->GetString(), "ty")) {
if (auto type = findObjectType()) {
if (auto child = parseObject(type)) {
if (child->hidden) delete (child);
else parent.push(child);
} else {
//skip unsupported type
while (nextObjectKey()) skip();
}
free(type);
return;
}
}
//object type key either listed as the first one or never - skip the entire object
while (auto key = nextObjectKey()) {
if (KEY_AS("ty")) {
if (auto child = parseObject()) {
@ -1080,21 +1100,8 @@ void LottieParser::parseTimeRemap(LottieLayer* layer)
void LottieParser::parseShapes(Array<LottieObject*>& parent)
{
enterArray();
while (nextArrayValue()) {
enterObject();
while (auto key = nextObjectKey()) {
if (KEY_AS("it")) {
enterArray();
while (nextArrayValue()) parseObject(parent);
} else if (KEY_AS("ty")) {
if (auto child = parseObject()) {
if (child->hidden) delete(child);
else parent.push(child);
}
} else skip();
}
}
}

View file

@ -75,7 +75,7 @@ private:
template<LottieProperty::Type type = LottieProperty::Type::Invalid, typename T> void parseProperty(T& prop, LottieObject* obj = nullptr);
template<LottieProperty::Type type = LottieProperty::Type::Invalid, typename T> void parseSlotProperty(T& prop);
LottieObject* parseObject();
LottieObject* parseObject(const char* type = nullptr);
LottieObject* parseAsset();
void parseImage(LottieImage* image, const char* data, const char* subPath, bool embedded, float width, float height);
LottieLayer* parseLayer(LottieLayer* precomp);

View file

@ -43,6 +43,7 @@
*/
#include "tvgLottieParserHandler.h"
#include "tvgStr.h"
/************************************************************************/
@ -184,6 +185,14 @@ int LookaheadParserHandler::peekType()
}
Value* LookaheadParserHandler::peekValue() {
if (state >= kHasNull && state <= kHasKey) {
return &val;
}
return nullptr;
}
void LookaheadParserHandler::skipOut(int depth)
{
do {
@ -233,6 +242,31 @@ void LookaheadParserHandler::skip()
}
char* LookaheadParserHandler::findObjectType()
{
auto level = 0;
for (auto p = iss.src_; *p != '\0'; ++p) {
if (*p == '{') level++;
else if (*p == '}') {
if (--level < 0) break;
} else if (level == 0) {
if (!strncmp(p, "\"ty\"", 4)) {
p += 4;
while (*p != '\0' && (isspace(*p) || *p == '\n')) ++p;
if (*p++ != ':') return nullptr;
while (*p != '\0' && (isspace(*p) || *p == '\n')) ++p;
if (*p++ != '\"') return nullptr;
const char* start = p;
while (*p != '\0' && *p != '\"') ++p;
if (*p == '\"') return strDuplicate(start, p - start);
return nullptr;
}
}
}
return nullptr;
}
char* LookaheadParserHandler::getPos()
{
return iss.src_;

View file

@ -195,6 +195,8 @@ struct LookaheadParserHandler
void skip();
void skipOut(int depth);
int peekType();
Value* peekValue();
char* findObjectType();
char* getPos();
};