Я пишу приложение, которое должно создать HTML-документ, содержащий несколько встроенных SVG.
Приложение имеет следующие характеристики:
Он должен работать без головы в серверной среде.
Он использует существующий код, который манипулирует DOM для создания SVG и других элементов в документе.
Существующий код использует метод
getBBox()
для определения размеров различных динамически создаваемых элементов, чтобы рассчитать макет SVG.В качестве дополнительной информации, хотя я не думаю, что это влияет на вопрос: код генерации 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)?