getBBox для встроенного элемента SVG с батиком

Я пишу приложение, которое должно создать HTML-документ, содержащий несколько встроенных SVG.

Приложение имеет следующие характеристики:

  1. Он должен работать без головы в серверной среде.

  2. Он использует существующий код, который манипулирует DOM для создания SVG и других элементов в документе.

  3. Существующий код использует метод getBBox() для определения размеров различных динамически создаваемых элементов, чтобы рассчитать макет SVG.

  4. В качестве дополнительной информации, хотя я не думаю, что это влияет на вопрос: код генерации SVG написан на Javascript и использует библиотеку D3. Для этого я использую node-java. Однако проблема, с которой я сейчас сталкиваюсь, легко воспроизводится на чистом Java SSCE (см. ниже).

Я пытаюсь использовать Apache Batik для реализации SVG DOM.

Моя проблема в том, что до сих пор мне не удалось заставить getBBox() возвращать ненулевое значение в случае, когда элемент SVG встроен в более крупный XML-документ.

Вот пример, иллюстрирующий проблему:

import org.w3c.dom.*;
import org.w3c.dom.svg.*;
import org.apache.batik.anim.dom.SVGDOMImplementation;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.GVTBuilder;

class BatikDomEmbedded {

    public static void main(String [ ] args) {

        DOMImplementation impl = SVGDOMImplementation.getDOMImplementation(); 
        SVGDocument doc = (SVGDocument) impl.createDocument(null, "html", null);

        Element html = doc.getDocumentElement();
        Element body = doc.createElement("body");
        html.appendChild(body);

        Element svg = doc.createElementNS(SVGDOMImplementation.SVG_NAMESPACE_URI, "svg"); 
        svg.setAttributeNS(null, "width", "500");
        svg.setAttributeNS(null, "height", "100");
        body.appendChild(svg);

        UserAgent userAgent = new UserAgentAdapter(); 
        DocumentLoader loader    = new DocumentLoader(userAgent); 
        BridgeContext ctx       = new BridgeContext(userAgent, loader); 
        ctx.setDynamicState(BridgeContext.DYNAMIC); 
        GVTBuilder builder   = new GVTBuilder(); 
        builder.build(ctx, svg); 

        Element rect = doc.createElementNS(SVGDOMImplementation.SVG_NAMESPACE_URI, "rect");
        rect.setAttributeNS(null, "x", "100"); 
        rect.setAttributeNS(null, "y", "100"); 
        rect.setAttributeNS(null, "width", "150"); 
        rect.setAttributeNS(null, "height", "200"); 
        svg.appendChild(rect); 

        SVGRect bbox = ((SVGLocatable)rect).getBBox(); 

        System.out.println("X: " + bbox.getX() + "\nY: " + bbox.getY() + 
            "\nHeight: " + bbox.getHeight() + "\nWidth: " + bbox.getWidth());     
    }       

}

Выполнение этого кода приводит к исключению нулевого указателя при разыменовании bbox.

Если я немного изменю код, чтобы сделать документ автономным документом SVG, он будет работать нормально.

Основное различие между этими сценариями (кроме того, что элемент SVG не является корневым элементом документа) заключается в том, что в рабочем случае вызов builder.build(ctx, ...); предоставляет SVGDocument в качестве второго аргумента, а в нерабочем случае это SVGElement (типа 'svg').

Есть ли способ заставить getBBox() работать в этом случае, когда элемент svg встроен в более крупный документ (и на самом деле в этом документе может быть несколько SVG)?


person harmic    schedule 03.12.2015    source источник


Ответы (1)


val impl: DOMImplementation = SVGDOMImplementation.getDOMImplementation()
val doc = impl.createDocument(SVGConstants.SVG_NAMESPACE_URI, "svg", null) as SVGDocument

val userAgent: UserAgent = UserAgentAdapter()
val loader = DocumentLoader(userAgent)
val ctx = BridgeContext(userAgent, loader)
ctx.setDynamicState(BridgeContext.DYNAMIC)
val builder = GVTBuilder()
builder.build(ctx, doc)

val rect: Element = doc.createElementNS(SVGDOMImplementation.SVG_NAMESPACE_URI, "rect")
rect.setAttributeNS(null, "x", "100")
rect.setAttributeNS(null, "y", "100")
rect.setAttributeNS(null, "width", "150")
rect.setAttributeNS(null, "height", "200")
doc.documentElement.appendChild(rect)

val bbox = (rect as SVGLocatable).bBox

println("X: " + bbox.x + "\nY: " + bbox.y +
        "\nHeight: " + bbox.height + "\nWidth: " + bbox.width)

попробуйте этот код.

во-первых, всегда вводите svg namespace_url при создании документа или элемента.

во-вторых, не устанавливайте в SvgDocument значение "html" для параметраqualName. если вы хотите создать html-документ, используйте HtmlDocument.

p.s. этот код kotlin.

person leeJB    schedule 10.04.2020