Commit ed9e9569 authored by jan.koester's avatar jan.koester
Browse files

test

parent dbe87278
Loading
Loading
Loading
Loading
+79 −2
Original line number Diff line number Diff line
@@ -898,8 +898,7 @@ std::string blogi::htmlimport::extractCustomCss(

std::string blogi::htmlimport::serializeElement(const libhtmlpp::Element *el) {
    if (!el) return "";
    if (el->getType() == libhtmlpp::HtmlEl || el->getType() == libhtmlpp::SvgEL ||
        el->getType() == libhtmlpp::TextAreaEL) {
    if (el->getType() == libhtmlpp::HtmlEl || el->getType() == libhtmlpp::TextAreaEL) {
        const libhtmlpp::HtmlElement *hel = static_cast<const libhtmlpp::HtmlElement*>(el);
        std::string tag = hel->getTagname();
        std::string result = "<" + tag;
@@ -914,6 +913,33 @@ std::string blogi::htmlimport::serializeElement(const libhtmlpp::Element *el) {
        result += serializeInnerHtml(const_cast<libhtmlpp::HtmlElement*>(hel));
        result += "</" + tag + ">";
        return result;
    } else if (el->getType() == libhtmlpp::SvgEL) {
        // Unlike a plain HtmlElement, SvgElement stores its inner markup as
        // a raw blob (getSvg()) rather than a real child tree -- its own
        // insertChild/appendChild are deleted outright, and firstChild()
        // always returns null. Reusing the HtmlEl branch above (which walks
        // firstChild() via serializeInnerHtml) silently produced an empty
        // "<svg ...></svg>" shell for every icon on this class of
        // page-builder site (icons wrap inline <svg><path>/<polygon>...
        // markup, e.g. the header's location/email/phone icons) -- the
        // outer tag/attributes came through fine, only the actual icon
        // artwork vanished. Mirrors the ScriptEL branch below, which
        // already gets this right for the same reason (getScript()).
        const libhtmlpp::SvgElement *sel = static_cast<const libhtmlpp::SvgElement*>(el);
        std::string tag = sel->getTagname();
        std::string result = "<" + tag;
        for (const auto *attr = sel->firstAttribute(); attr; attr = attr->nextAttribute()) {
            result += " " + attr->getKey();
            std::string val = attr->getValue();
            if (!val.empty()) {
                result += "=\"" + val + "\"";
            }
        }
        result += ">";
        std::vector<char> svg = const_cast<libhtmlpp::SvgElement*>(sel)->getSvg();
        result.append(svg.begin(), svg.end());
        result += "</" + tag + ">";
        return result;
    } else if (el->getType() == libhtmlpp::TextEl) {
        return const_cast<libhtmlpp::TextElement*>(
            static_cast<const libhtmlpp::TextElement*>(el))->getText();
@@ -1804,6 +1830,57 @@ void blogi::htmlimport::htmlElementToWidgetXml(
                    if (child) childFrames.push_back({child, w, nullptr, elementEnv, childAncestorChain});
                }

            } else if (tag == "svg") {
                // No dedicated widget type for a raw <svg> icon -- it becomes
                // CustomHtml below, same as the generic fallback, EXCEPT: a
                // page-builder icon like this commonly has no width/height
                // attribute of its own (just a viewBox), sized purely by an
                // external class-based CSS rule such as
                // ".con-kit-component-icon svg{width:...;height:...}". That
                // rule can never survive import: htmlimport's own "global
                // CSS" extraction only ever keeps :root/html/body/*-scoped
                // rules, and even per-element CSS matching deliberately
                // rejects a bare-tag target after a combinator like this
                // (see compoundMatches's own doc comment -- a real, previously
                // fixed hazard, not an oversight). Confirmed on a real page:
                // every header icon (location/email/phone) rendered as an
                // empty circle, collapsed to 0x0, with its <path>/<polygon>
                // artwork otherwise intact (see serializeElement's SvgEL
                // branch). Falling back to the svg's own viewBox dimensions
                // when neither a width/height attribute nor a matched
                // "width"/"height" cssProps entry exists at least makes the
                // icon visible -- not pixel-identical to the source page,
                // but far closer than invisible.
                std::string svgHtml = serializeElement(cur);
                if (elemAttrs.count("width") == 0 && elemAttrs.count("height") == 0 &&
                    !cssProps.count("width") && !cssProps.count("height")) {
                    auto vbIt = elemAttrs.find("viewBox");
                    if (vbIt != elemAttrs.end()) {
                        std::istringstream vb(vbIt->second);
                        double minX, minY, vbWidth, vbHeight;
                        if ((vb >> minX >> minY >> vbWidth >> vbHeight) && vbWidth > 0 && vbHeight > 0) {
                            auto fmt = [](double v) {
                                long long i = static_cast<long long>(v);
                                return (v == static_cast<double>(i)) ? std::to_string(i) : std::to_string(v);
                            };
                            std::string sizeAttrs = " width=\"" + fmt(vbWidth) +
                                "\" height=\"" + fmt(vbHeight) + "\"";
                            size_t tagEnd = svgHtml.find("<svg");
                            if (tagEnd != std::string::npos) svgHtml.insert(tagEnd + 4, sizeAttrs);
                        }
                    }
                }
                tinyxml2::XMLElement *w = doc.NewElement("CustomHtml");
                tinyxml2::XMLElement *htmlContent = doc.NewElement("HtmlContent");
                htmlContent->SetText(svgHtml.c_str());
                w->InsertEndChild(htmlContent);
                if (!style.empty()) {
                    tinyxml2::XMLElement *cssEl = doc.NewElement("CssContent");
                    cssEl->SetText(style.c_str());
                    w->InsertEndChild(cssEl);
                }
                pXml->InsertEndChild(w);

            } else if (tag == "h1" || tag == "h2" || tag == "h3" || tag == "h4" ||
                       tag == "h5" || tag == "h6" || tag == "p" || tag == "span" ||
                       tag == "b" || tag == "i" || tag == "em" || tag == "strong" ||
+39 −0
Original line number Diff line number Diff line
@@ -509,6 +509,45 @@ int main(){
              "an attribute not referenced by any media rule is not preserved");
    }

    // --- serializeElement: <svg> icon content survives the CustomHtml
    // fallback ---
    // SvgElement stores its inner markup as a raw blob (getSvg()), not a
    // real child tree (its own insertChild/appendChild are deleted, and
    // firstChild() always returns null) -- serializeElement's generic
    // HtmlEl branch walked firstChild() via serializeInnerHtml and silently
    // produced an empty "<svg ...></svg>" shell, dropping every icon's
    // actual <path>/<polygon> artwork (real-world symptom: header
    // location/email/phone icons rendered as bare circles with nothing
    // inside).
    std::cout << "=== <svg> icon content preserved through CustomHtml fallback ===" << std::endl;
    {
        std::string html =
            "<div class=\"icon\"><svg viewBox=\"0 0 32 32\">"
            "<path d=\"M1,2\"></path><polygon points=\"1,2 3,4\"></polygon>"
            "</svg></div>";
        std::string xml = importXml(html);
        check(xml.find("path") != std::string::npos,
              "the <path> child survives import (was silently dropped -- empty <svg></svg> shell)");
        check(xml.find("polygon") != std::string::npos,
              "the <polygon> child survives import");
        check(xml.find("width=\"32\" height=\"32\"") != std::string::npos,
              "an <svg> with no width/height attribute of its own falls back to its viewBox "
              "dimensions (real-world symptom: icons sized purely by an external CSS rule that "
              "can't survive import collapsed to an invisible 0x0 box)");
    }
    {
        // An <svg> that already specifies its own width/height is left
        // alone -- the viewBox fallback only fires when neither is present.
        std::string html =
            "<div class=\"icon\"><svg viewBox=\"0 0 32 32\" width=\"18\" height=\"18\">"
            "<path d=\"M1,2\"></path></svg></div>";
        std::string xml = importXml(html);
        check(xml.find("width=\"18\" height=\"18\"") != std::string::npos,
              "an <svg> with its own explicit width/height keeps it, unmodified");
        check(xml.find("width=\"32\"") == std::string::npos,
              "the viewBox fallback does not also add its own width/height alongside the real one");
    }

    // --- Summary ---
    std::cout << "\n=== " << passCount << "/" << testCount << " tests passed ===" << std::endl;