From 72cc9d25e8f4f96fde6073f3d18f63beb79c0499 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Fri, 7 Feb 2025 12:53:01 +0100 Subject: [PATCH] lottie: fix & enhance parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/loaders/lottie/tvgLottieParser.cpp | 41 +++++++++++-------- src/loaders/lottie/tvgLottieParser.h | 2 +- src/loaders/lottie/tvgLottieParserHandler.cpp | 35 +++++++++++++++- src/loaders/lottie/tvgLottieParserHandler.h | 2 + 4 files changed, 61 insertions(+), 19 deletions(-) diff --git a/src/loaders/lottie/tvgLottieParser.cpp b/src/loaders/lottie/tvgLottieParser.cpp index 9ef4a3bf..4e22cf8f 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -852,10 +852,12 @@ LottieOffsetPath* LottieParser::parseOffsetPath() } -LottieObject* LottieParser::parseObject() +LottieObject* LottieParser::parseObject(const char* type) { - auto type = getString(); - if (!type) return nullptr; + if (!type) { + type = getString(); + if (!type) return nullptr; + } if (!strcmp(type, "gr")) return parseGroup(); else if (!strcmp(type, "rc")) return parseRect(); @@ -882,6 +884,24 @@ LottieObject* LottieParser::parseObject() void LottieParser::parseObject(Array& 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()) { @@ -1096,20 +1116,7 @@ void LottieParser::parseTimeRemap(LottieLayer* layer) void LottieParser::parseShapes(Array& 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(); - } - } + while (nextArrayValue()) parseObject(parent); } diff --git a/src/loaders/lottie/tvgLottieParser.h b/src/loaders/lottie/tvgLottieParser.h index 66a96290..cceeb143 100644 --- a/src/loaders/lottie/tvgLottieParser.h +++ b/src/loaders/lottie/tvgLottieParser.h @@ -75,7 +75,7 @@ private: template void parseProperty(T& prop, LottieObject* obj = nullptr); template 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); diff --git a/src/loaders/lottie/tvgLottieParserHandler.cpp b/src/loaders/lottie/tvgLottieParserHandler.cpp index 6b1a5b9f..76898e52 100644 --- a/src/loaders/lottie/tvgLottieParserHandler.cpp +++ b/src/loaders/lottie/tvgLottieParserHandler.cpp @@ -1,4 +1,4 @@ -/* + /* * Copyright (c) 2023 - 2025 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -174,6 +174,14 @@ int LookaheadParserHandler::peekType() } +Value* LookaheadParserHandler::peekValue() { + if (state >= kHasNull && state <= kHasKey) { + return &val; + } + return nullptr; +} + + void LookaheadParserHandler::skipOut(int depth) { do { @@ -223,6 +231,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 duplicate(start, p - start); + return nullptr; + } + } + } + return nullptr; +} + + char* LookaheadParserHandler::getPos() { return iss.src_; diff --git a/src/loaders/lottie/tvgLottieParserHandler.h b/src/loaders/lottie/tvgLottieParserHandler.h index 507b830e..8a5b8369 100644 --- a/src/loaders/lottie/tvgLottieParserHandler.h +++ b/src/loaders/lottie/tvgLottieParserHandler.h @@ -197,6 +197,8 @@ struct LookaheadParserHandler void skip(); void skipOut(int depth); int peekType(); + Value* peekValue(); + char* findObjectType(); char* getPos(); };