diff --git a/inc/thorvg.h b/inc/thorvg.h index 4e7b68c3b..ec4a0fcb7 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -1529,7 +1529,7 @@ class TVG_API Text final : public Paint Result fill(std::unique_ptr f) noexcept; /** - * @brief Loads a scalable font data(ttf) from a file. + * @brief Loads a scalable font data (ttf) from a file. * * ThorVG efficiently caches the loaded data using the specified @p path as a key. * This means that loading the same file again will not result in duplicate operations; @@ -1544,16 +1544,42 @@ class TVG_API Text final : public Paint * * @note Experimental API * - * @see Text::unload(const std::string& path) + * @see Text::unload(const std::string& fontId) */ static Result load(const std::string& path) noexcept; + /** + * @brief Loads a scalable font data (ttf) from a memory block of a given size. + * + * ThorVG efficiently caches the loaded font data using the specified @p name as a key when @p copy + * is set to @c false. This means that loading the same fonts again will not result in duplicate + * operations for the shared @p data. Instead, ThorVG will reuse the previously loaded font data. + * + * @param[in] name The name under which the font will be stored and accessible. + * @param[in] data A pointer to a memory location where the content of the ttf data is stored. + * @param[in] size The size in bytes of the memory occupied by the @p data. + * @param[in] rpath A resource directory path, if the @p data needs to access any external resources. + * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not. + * + * @retval Result::Success When succeed. + * @retval Result::InvalidArguments In case no data are provided or the @p size is zero or less. + * @retval Result::NonSupport When trying to load a file with an unsupported extension. + * @retval Result::Unknown If an error occurs at a later stage. + * + * @warning: It's the user responsibility to release the @p data memory. + * + * @note Experimental API + * + * @see Text::unload(const std::string& fontId) + */ + static Result load(const std::string& name, const char* data, uint32_t size, const std::string& rpath = "", bool copy = false) noexcept; + /** * @brief Unloads the specified scalable font data (TTF) that was previously loaded. * * This function is used to release resources associated with a font file that has been loaded into memory. * - * @param[in] path The file path of the loaded font. + * @param[in] fontId The file path or the font name of the loaded font depending on which method was used for loading. * * @retval Result::Success Successfully unloads the font data. * @retval Result::InsufficientCondition Fails if the loader is not initialized. @@ -1562,8 +1588,9 @@ class TVG_API Text final : public Paint * @note Experimental API * * @see Text::load(const std::string& path) + * @see Text::load(const std::string& name, const char* data, uint32_t size, const std::string& rpath, bool copy) */ - static Result unload(const std::string& path) noexcept; + static Result unload(const std::string& fontId) noexcept; /** * @brief Creates a new Text object. diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 50f39356e..09e220d85 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -404,6 +404,28 @@ static char* _idFromUrl(const char* url) } +static char* _srcFromUrl(const char* url) +{ + auto open = strchr(url, '('); + auto close = strchr(url, ')'); + if (!open || !close || open >= close) return nullptr; + + open = strchr(open, '\''); + if (!open || open >= close) return nullptr; + ++open; + + close = strchr(open, '\''); + if (!close || close == open) return nullptr; + --close; + + //trim the rest of the spaces if any + while (open < close && *open == ' ') ++open; + while (open < close && *close == ' ') --close; + + return strDuplicate(open, (close - open + 1)); +} + + static unsigned char _parseColor(const char* value, char** end) { float r; @@ -1999,6 +2021,36 @@ static SvgNode* _createImageNode(SvgLoaderData* loader, SvgNode* parent, const c } +static bool _attrParseFontFace(void* data, const char* key, const char* value) +{ + if (!key || !value) return false; + + //Trim the white space + key = _skipSpace(key, nullptr); + value = _skipSpace(value, nullptr); + + auto loader = (SvgLoaderData*)data; + auto& embeddedFont = loader->fonts.last(); + + if (!strcmp(key, "font-family")) { + if (embeddedFont.name) free(embeddedFont.name); + embeddedFont.name = strdup(value); + } else if (!strcmp(key, "src")) { + if (embeddedFont.src) free(embeddedFont.src); + embeddedFont.src = _srcFromUrl(value); + } + + return true; +} + + +static void _createEmbeddedFont(SvgLoaderData* loader, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->fonts.push({}); + func(buf, bufLength, _attrParseFontFace, loader); +} + + static SvgNode* _getDefsNode(SvgNode* node) { if (!node) return nullptr; @@ -2119,7 +2171,71 @@ static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const cha } -//TODO: Implement 'text' primitive +static constexpr struct +{ + const char* tag; + SvgParserLengthType type; + int sz; + size_t offset; +} textTags[] = { + {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgTextNode, x)}, + {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgTextNode, y)}, + {"font-size", SvgParserLengthType::Vertical, sizeof("font-size"), offsetof(SvgTextNode, fontSize)} +}; + + +static bool _attrParseTextNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + SvgTextNode* text = &(node->node.text); + + unsigned char* array; + int sz = strlen(key); + + array = (unsigned char*)text; + for (unsigned int i = 0; i < sizeof(textTags) / sizeof(textTags[0]); i++) { + if (textTags[i].sz - 1 == sz && !strncmp(textTags[i].tag, key, sz)) { + *((float*)(array + textTags[i].offset)) = _toFloat(loader->svgParse, value, textTags[i].type); + return true; + } + } + + if (!strcmp(key, "font-family")) { + if (text->fontFamily && value) free(text->fontFamily); + text->fontFamily = strdup(value); + } else if (!strcmp(key, "style")) { + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); + } else if (!strcmp(key, "clip-path")) { + _handleClipPathAttr(loader, node, value); + } else if (!strcmp(key, "mask")) { + _handleMaskAttr(loader, node, value); + } else if (!strcmp(key, "id")) { + if (node->id && value) free(node->id); + node->id = _copyId(value); + } else if (!strcmp(key, "class")) { + _handleCssClassAttr(loader, node, value); + } else { + return _parseStyleAttr(loader, key, value, false); + } + return true; +} + + +static SvgNode* _createTextNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Text); + if (!loader->svgParse->node) return nullptr; + + loader->svgParse->node->node.text.fontSize = 10.0f; + //TODO: support the def font: loader->svgParse->node->node.text.fontFamily = ... + + func(buf, bufLength, _attrParseTextNode, loader); + + return loader->svgParse->node; +} + + static constexpr struct { const char* tag; @@ -2134,7 +2250,8 @@ static constexpr struct {"rect", sizeof("rect"), _createRectNode}, {"polyline", sizeof("polyline"), _createPolylineNode}, {"line", sizeof("line"), _createLineNode}, - {"image", sizeof("image"), _createImageNode} + {"image", sizeof("image"), _createImageNode}, + {"text", sizeof("text"), _createTextNode} }; @@ -3122,6 +3239,20 @@ static void _copyAttr(SvgNode* to, const SvgNode* from) to->node.use.symbol = from->node.use.symbol; break; } + case SvgNodeType::Text: { + to->node.text.x = from->node.text.x; + to->node.text.y = from->node.text.y; + to->node.text.fontSize = from->node.text.fontSize; + if (from->node.text.text) { + if (to->node.text.text) free(to->node.text.text); + to->node.text.text = strdup(from->node.text.text); + } + if (from->node.text.fontFamily) { + if (to->node.text.fontFamily) free(to->node.text.fontFamily); + to->node.text.fontFamily = strdup(from->node.text.fontFamily); + } + break; + } default: { break; } @@ -3244,7 +3375,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes); loader->cssStyle = node; loader->doc->node.doc.style = node; - loader->style = true; + loader->openedTag = OpenedTagType::Style; } } else { node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes); @@ -3260,9 +3391,12 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, else parent = loader->doc; node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes); if (node && !empty) { - auto defs = _createDefsNode(loader, nullptr, nullptr, 0, nullptr); - loader->stack.push(defs); - loader->currentGraphicsNode = node; + if (!strcmp(tagName, "text")) loader->openedTag = OpenedTagType::Text; + else { + auto defs = _createDefsNode(loader, nullptr, nullptr, 0, nullptr); + loader->stack.push(defs); + loader->currentGraphicsNode = node; + } } } else if ((gradientMethod = _findGradientFactory(tagName))) { SvgStyleGradient* gradient; @@ -3295,6 +3429,15 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, } +static void _svgLoaderParserText(SvgLoaderData* loader, const char* content, unsigned int length) +{ + auto text = &loader->svgParse->node->node.text; + if (text->text) free(text->text); + text->text = strDuplicate(content, length); + loader->openedTag = OpenedTagType::Other; +} + + static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* content, unsigned int length) { char* tag; @@ -3317,6 +3460,8 @@ static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* conte TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag); } else if (!strcmp(tag, "all")) { if ((node = _createCssStyleNode(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name); + } else if (!strcmp(tag, "@font-face")) { //css at-rule specifying font + _createEmbeddedFont(loader, attrs, attrsLength, simpleXmlParseW3CAttribute); } else if (!isIgnoreUnsupportedLogElements(tag)) { TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag); } @@ -3327,7 +3472,7 @@ static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* conte free(tag); free(name); } - loader->style = false; + loader->openedTag = OpenedTagType::Other; } @@ -3350,7 +3495,8 @@ static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content } case SimpleXMLType::Data: case SimpleXMLType::CData: { - if (loader->style) _svgLoaderParserXmlCssStyle(loader, content, length); + if (loader->openedTag == OpenedTagType::Style) _svgLoaderParserXmlCssStyle(loader, content, length); + else if (loader->openedTag == OpenedTagType::Text) _svgLoaderParserText(loader, content, length); break; } case SimpleXMLType::DoctypeChild: { @@ -3572,6 +3718,11 @@ static void _freeNode(SvgNode* node) free(node->node.image.href); break; } + case SvgNodeType::Text: { + free(node->node.text.text); + free(node->node.text.fontFamily); + break; + } default: { break; } @@ -3663,6 +3814,12 @@ void SvgLoader::clear(bool all) } loaderData.images.reset(); + for (auto font = loaderData.fonts.begin(); font < loaderData.fonts.end(); ++font) { + Text::unload(font->name); + free(font->name); + } + loaderData.fonts.reset(); + if (copy) free((char*)content); delete(root); diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index 9f2577346..ee6a4d15e 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -374,6 +374,14 @@ struct SvgCssStyleNode { }; +struct SvgTextNode +{ + char* text; + float x, y; + float fontSize; + char* fontFamily; +}; + struct SvgLinearGradient { float x1; @@ -518,6 +526,7 @@ struct SvgNode SvgClipNode clip; SvgCssStyleNode cssStyle; SvgSymbolNode symbol; + SvgTextNode text; } node; ~SvgNode(); }; @@ -545,6 +554,19 @@ struct SvgNodeIdPair char *id; }; +struct EmbeddedFont +{ + char* name; + char* src; +}; + +enum class OpenedTagType : uint8_t +{ + Other = 0, + Style, + Text +}; + struct SvgLoaderData { Array stack; @@ -556,10 +578,11 @@ struct SvgLoaderData SvgParser* svgParse = nullptr; Array cloneNodes; Array nodesToStyle; - Array images; //embedded images + Array images; //embedded images + Array fonts; int level = 0; bool result = false; - bool style = false; + OpenedTagType openedTag = OpenedTagType::Other; SvgNode* currentGraphicsNode = nullptr; }; diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index e8b13fafd..ba325904c 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -67,6 +67,14 @@ static Box _boundingBox(const Shape* shape) } +static Box _boundingBox(const Text* text) +{ + float x, y, w, h; + text->bounds(&x, &y, &w, &h, false); + return {x, y, w, h}; +} + + static void _transformMultiply(const Matrix* mBBox, Matrix* gradTransf) { gradTransf->e13 = gradTransf->e13 * mBBox->e11 + mBBox->e13; @@ -304,14 +312,9 @@ static void _applyComposition(SvgLoaderData& loaderData, Paint* paint, const Svg } -static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath, bool clip) +//TODO: unify with _applyFillProperty for text after the opacity to the text is introduced or after the shape's fill api doesn't change the alpha channel +static void _applyFillProperty(SvgStyleProperty* style, Shape* vg, const Box& vBox) { - SvgStyleProperty* style = node->style; - - //Clip transformation is applied directly to the path in the _appendClipShape function - if (node->transform && !clip) vg->transform(*node->transform); - if (node->type == SvgNodeType::Doc || !node->style->display) return; - //If fill property is nullptr then do nothing if (style->fill.paint.none) { //Do nothing @@ -320,11 +323,11 @@ static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg, if (!style->fill.paint.gradient->userSpace) bBox = _boundingBox(vg); if (style->fill.paint.gradient->type == SvgGradientType::Linear) { - auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity); - vg->fill(std::move(linear)); + auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity); + vg->fill(std::move(linear)); } else if (style->fill.paint.gradient->type == SvgGradientType::Radial) { - auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity); - vg->fill(std::move(radial)); + auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity); + vg->fill(std::move(radial)); } } else if (style->fill.paint.url) { //TODO: Apply the color pointed by url @@ -335,6 +338,46 @@ static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg, //Apply the fill color vg->fill(style->fill.paint.color.r, style->fill.paint.color.g, style->fill.paint.color.b, style->fill.opacity); } +} + + +static void _applyFillProperty(SvgStyleProperty* style, Text* vg, const Box& vBox) +{ + //If fill property is nullptr then do nothing + if (style->fill.paint.none) { + //Do nothing + } else if (style->fill.paint.gradient) { + Box bBox = vBox; + if (!style->fill.paint.gradient->userSpace) bBox = _boundingBox(vg); + + if (style->fill.paint.gradient->type == SvgGradientType::Linear) { + auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity); + vg->fill(std::move(linear)); + } else if (style->fill.paint.gradient->type == SvgGradientType::Radial) { + auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity); + vg->fill(std::move(radial)); + } + } else if (style->fill.paint.url) { + //TODO: Apply the color pointed by url + } else if (style->fill.paint.curColor) { + //Apply the current style color + vg->fill(style->color.r, style->color.g, style->color.b); + } else { + //Apply the fill color + vg->fill(style->fill.paint.color.r, style->fill.paint.color.g, style->fill.paint.color.b); + } +} + + +static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath, bool clip) +{ + SvgStyleProperty* style = node->style; + + //Clip transformation is applied directly to the path in the _appendClipShape function + if (node->transform && !clip) vg->transform(*node->transform); + if (node->type == SvgNodeType::Doc || !node->style->display) return; + + _applyFillProperty(style, vg, vBox); //Apply the fill rule vg->fill((tvg::FillRule)style->fill.fillRule); @@ -771,6 +814,32 @@ static unique_ptr _useBuildHelper(SvgLoaderData& loaderData, const SvgNod } +static unique_ptr _textBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath) +{ + auto textNode = &node->node.text; + auto text = Text::gen(); + + Matrix textTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + if (node->transform) textTransform = *node->transform; + if (!mathZero(node->node.text.x) || !mathZero(node->node.text.y)) { + Matrix translate = {1, 0, node->node.text.x, 0, 1, node->node.text.y - textNode->fontSize, 0, 0, 1}; + textTransform *= translate; + } + text->transform(textTransform); + + const float ptPerPx = 0.75f; //1 pt = 1/72; 1 in = 96 px; -> 72/96 = 0.75 + auto fontSizePt = textNode->fontSize * ptPerPx; + //TODO: handle default fonts + if (textNode->fontFamily) text->font(textNode->fontFamily, fontSizePt); + text->text(textNode->text); + + _applyFillProperty(node->style, text.get(), vBox); + _applyComposition(loaderData, text.get(), node, vBox, svgPath); + + return text; +} + + static unique_ptr _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite) { /* Exception handling: Prevent invalid SVG data input. @@ -799,6 +868,9 @@ static unique_ptr _sceneBuildHelper(SvgLoaderData& loaderData, const SvgN scene->push(std::move(image)); if (isMaskWhite) *isMaskWhite = false; } + } else if ((*child)->type == SvgNodeType::Text) { + auto text = _textBuildHelper(loaderData, *child, vBox, svgPath); + if (text) scene->push(std::move(text)); } else if ((*child)->type != SvgNodeType::Mask) { auto shape = _shapeBuildHelper(loaderData, *child, vBox, svgPath); if (shape) { @@ -843,6 +915,41 @@ static void _updateInvalidViewSize(const Scene* scene, Box& vBox, float& w, floa if (!validHeight) h *= vBox.h; } + +static void _loadFonts(Array& fonts) +{ + if (fonts.empty()) return; + + TaskScheduler::async(false); + for (auto font = fonts.begin(); font < fonts.end(); ++font) { + if (!font->src || !font->name) return; + + auto src = font->src; + if (!strncmp(src, "data:", sizeof("data:") - 1)) { + src += sizeof("data:") - 1; + + //Other possibilities? now handling: 1) data:font/ttf;base64, 2) data:application/font-ttf;base64, + src = strstr(src, "ttf;base64,"); + if (!src) { + TVGLOG("SVG", "The embedded font named \"%s\" not loaded properly.", font->name); + free(font->src); + font->src = nullptr; + continue; + } + src += sizeof("ttf;base64,") - 1; + + char *decoded = nullptr; + auto size = b64Decode(src, strlen(src), &decoded); + if (Text::load(font->name, decoded, size) != Result::Success) + TVGERR("SVG", "Error while loading the ttf font named %s.", font->name); + } + free(font->src); + font->src = nullptr; + } + + TaskScheduler::async(true); +} + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -853,6 +960,8 @@ Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, Aspe if (!loaderData.doc || (loaderData.doc->type != SvgNodeType::Doc)) return nullptr; + _loadFonts(loaderData.fonts); + auto docNode = _sceneBuildHelper(loaderData, loaderData.doc, vBox, svgPath, false, 0); if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode.get(), vBox, w, h, viewFlag); diff --git a/src/loaders/svg/tvgXmlParser.cpp b/src/loaders/svg/tvgXmlParser.cpp index 09fc8aaac..c11fc5b9f 100644 --- a/src/loaders/svg/tvgXmlParser.cpp +++ b/src/loaders/svg/tvgXmlParser.cpp @@ -483,6 +483,14 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAt do { char* sep = (char*)strchr(buf, ':'); next = (char*)strchr(buf, ';'); + + if (auto src = strstr(buf, "src")) {//src tag from css font-face contains extra semicolon + if (src && src < sep) { + if (next + 1 < end) next = (char*)strchr(next + 1, ';'); + else return true; + } + } + if (sep >= end) { next = nullptr; sep = nullptr; diff --git a/src/loaders/ttf/tvgTtfLoader.cpp b/src/loaders/ttf/tvgTtfLoader.cpp index bcff2a0f5..25be8b164 100644 --- a/src/loaders/ttf/tvgTtfLoader.cpp +++ b/src/loaders/ttf/tvgTtfLoader.cpp @@ -193,7 +193,13 @@ static uint32_t* _codepoints(const char* text, size_t n) void TtfLoader::clear() { - _unmap(this); + if (fromMemory) { + if (copy) free(reader.data); + reader.data = nullptr; + reader.size = 0; + copy = false; + } + else _unmap(this); shape = nullptr; } @@ -236,6 +242,25 @@ bool TtfLoader::open(const string& path) } +bool TtfLoader::open(const char* data, uint32_t size, TVG_UNUSED const string& rpath, bool copy) +{ + clear(); + if (!data || size == 0) return false; + + reader.size = size; + fromMemory = true; + + if (copy) { + reader.data = (uint8_t*)malloc(size); + if (!reader.data) return false; + memcpy((char*)reader.data, data, reader.size); + this->copy = true; + } else reader.data = (uint8_t*)data; + + return reader.header(); +} + + bool TtfLoader::request(Shape* shape, char* text, bool italic) { this->shape = shape; @@ -279,4 +304,4 @@ bool TtfLoader::read() free(code); return true; -} +} \ No newline at end of file diff --git a/src/loaders/ttf/tvgTtfLoader.h b/src/loaders/ttf/tvgTtfLoader.h index d45f86d56..de9190658 100644 --- a/src/loaders/ttf/tvgTtfLoader.h +++ b/src/loaders/ttf/tvgTtfLoader.h @@ -37,12 +37,15 @@ struct TtfLoader : public FontLoader char* text = nullptr; Shape* shape = nullptr; bool italic = false; + bool fromMemory = false; + bool copy = false; TtfLoader(); ~TtfLoader(); using FontLoader::open; bool open(const string& path) override; + bool open(const char *data, uint32_t size, const string& rpath, bool copy) override; bool resize(Paint* paint, float w, float h) override; bool request(Shape* shape, char* text, bool italic = false) override; bool read() override; diff --git a/src/renderer/tvgLoader.cpp b/src/renderer/tvgLoader.cpp index eb695b31d..088a282ef 100644 --- a/src/renderer/tvgLoader.cpp +++ b/src/renderer/tvgLoader.cpp @@ -431,5 +431,34 @@ LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool return loader; } delete(loader); + return nullptr; +} + + +LoadModule* LoaderMgr::loader(const string& name, const char* data, uint32_t size, const string& rpath, bool copy) +{ + //Note that users could use the same data pointer with the different content. + //Thus caching is only valid for shareable. + auto allowCache = !copy; + + if (allowCache) { + if (auto loader = _findFromCache(name)) return loader; + } + + if (auto loader = _findByType("ttf")) { + if (loader->open(data, size, rpath, copy)) { + if (allowCache) { + loader->hashpath = strdup(name.c_str()); + loader->pathcache = true; + ScopedLock lock(key); + _activeLoaders.back(loader); + } + return loader; + } else { + TVGLOG("LOADER", "Given data \"%s\" could not be loaded.", name.c_str()); + delete(loader); + } + } + return nullptr; } \ No newline at end of file diff --git a/src/renderer/tvgLoader.h b/src/renderer/tvgLoader.h index 86d36db10..ed1fdab89 100644 --- a/src/renderer/tvgLoader.h +++ b/src/renderer/tvgLoader.h @@ -32,6 +32,7 @@ struct LoaderMgr static LoadModule* loader(const string& path, bool* invalid); static LoadModule* loader(const char* data, uint32_t size, const string& mimeType, const string& rpath, bool copy); static LoadModule* loader(const uint32_t* data, uint32_t w, uint32_t h, bool premultiplied, bool copy); + static LoadModule* loader(const string& name, const char* data, uint32_t size, const string& rpath, bool copy); static LoadModule* loader(const char* key); static bool retrieve(const string& path); static bool retrieve(LoadModule* loader); diff --git a/src/renderer/tvgText.cpp b/src/renderer/tvgText.cpp index 1fe244c11..68539f30d 100644 --- a/src/renderer/tvgText.cpp +++ b/src/renderer/tvgText.cpp @@ -71,9 +71,17 @@ Result Text::load(const std::string& path) noexcept } -Result Text::unload(const std::string& path) noexcept +Result Text::load(const std::string& name, const char* data, uint32_t size, const string& rpath, bool copy) noexcept { - if (LoaderMgr::retrieve(path)) return Result::Success; + if (!data || size <= 0) return Result::InvalidArguments; + if (!LoaderMgr::loader(name, data, size, rpath, copy)) return Result::NonSupport; + return Result::Success; +} + + +Result Text::unload(const std::string& fontId) noexcept +{ + if (LoaderMgr::retrieve(fontId)) return Result::Success; return Result::InsufficientCondition; }