2

ダイアグラム作成アプリケーションでSVG図面を表示する際に問題が発生しました。キャンバスは、含まれているドキュメントではなく、親コンポーネントのサイズを取ります。このサイズより大きいドキュメントは完全にはレンダリングされません。

場合

たとえば、621x621ピクセルの図面があります。

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" contentScriptType="text/ecmascript" zoomAndPan="magnify" contentStyleType="text/css" preserveAspectRatio="xMaxYMax slice" version="1.0">
  <rect x="50" y="50" fill="white" width="20" height="20" stroke="black" stroke-width="1"/>
  <rect x="600" y="600" fill="blue" width="20" height="20" stroke="black" stroke-width="1"/>
</svg>

動的に作成されるため、viewBoxはありません。ドキュメントを作成しているとき、サイズがありません。

JSVGCanvas内部にJSVGScrollPane、スクロールペインの親がドキュメントよりも小さい、たとえば500x500ピクセルがあります。

期待される動作

アプリケーションを起動すると、500x500に収まる図面の部分が表示されます。基になるドキュメントが大きいので、121px左下にスクロールできるスクロールバーがあります。

フレームのサイズを大きくすると、より多くの図面が表示されます。620x620pxより大きくすると、スクロールバーが完全に消えることがあります。

パネルをドキュメントよりも小さくすると、スクロールバーが再び表示されます。

遭遇した行動

アプリケーションを起動すると、500x500に収まる図面の部分が表示されます。スクロールバーは表示されません。

パネルを大きくすると、キャンバス(背景色)は大きくなりますが、描画は最初の500x500pxの外側の部分には拡張されません。

パネルを小さくすると、たとえば400x400の場合、スクロールバーは表示されますが、100pxしかスクロールできません。したがって、キャンバスは、目的の621x621ではなく、最初の500x500のままです。

ScrollExample発生した動作は、Batikリポジトリと上記で追加したSVGファイルで再現できます。この例では、500x500pxの固定サイズを使用しています。

キャンバスとスクロールペインに背景色を追加すると、フレーム全体が青色になります。したがって、キャンバスのサイズは大きくなりますが、初期サイズを超えるものは描画されません。

canvas.setBackground(Color.blue);
scroller.setBackground(Color.red);

パネルのサイズを変更すると、ドキュメントをリセットできました。これにより、図面の表示部分が再描画されますが、キャンバスはドキュメントのサイズではなく表示部分のサイズのままであるため、スクロールは期待どおりに機能しません。このリスナーをフレーム(または私が推測する他のパネル)に追加することで、これをテストできます。

class ResizeListener implements ComponentListener {

    JSVGCanvas canvas;
    URI url;

    public ResizeListener(JSVGCanvas c, URI u) {
        canvas = c;
        url = u;
    }

    @Override
    public void componentResized(ComponentEvent arg0) {
        canvas.setURI(url.toString());
        // Calling invalidate on canvas or scroller does not work
    }

    // ...
};

viewBoxが必要であることを読みましたが、ドキュメントは頻繁に再作成され(modelchange-eventに基づいて)、ドキュメントを作成しているときに、viewBoxを取得する方法がわかりません。SVGDocument.getRootElement().getBBox();通常私に与えますnull。viewBoxが指定されていない場合、Batikコードにフォールバックがあるはずなので、これがオプションであることを願っています。また、どこかでパンした場合、ドキュメントを変更してもこのパンを維持する必要があります。

問題を明確にしたいと思います。JSVGクラスの多くが、箱から出して希望の動作を提供することを期待しているのでしょうか、それともここで何かが足りないのでしょうか。誰かが私を解決策に導いてくれませんか?

4

1 に答える 1

1

私は解決策を見つけたかもしれないと思います。viewBoxをいじる代わりに、ドキュメントの高さと幅を設定する必要があります。サイズを設定すると、viewBoxは不要になり、スクロールバーは期待どおりに機能します。ビューボックスを設定した場合、キャンバスは画像が収まるように拡大縮小し続けます。

さて、サイズを更新するために、サイズに影響を与える可能性のあるdomを変更するたびにこれを使用します。

static BridgeContext ctx = new BridgeContext(new UserAgentAdapter());
static GVTBuilder builder = new GVTBuilder();

private static void calculateSize(SVGDocument doc) {
    GraphicsNode gvtRoot = builder.build(ctx, doc);

    Rectangle2D rect = gvtRoot.getSensitiveBounds();

    doc.getRootElement().setAttributeNS(null,
            SVGConstants.SVG_WIDTH_ATTRIBUTE, rect.getMaxX() + "");
    doc.getRootElement().setAttributeNS(null,
            SVGConstants.SVG_HEIGHT_ATTRIBUTE, rect.getMaxY() + "");
}

私はこれをスレッドので行いUpdateManager、domの変更後に使用しますJSVGCanvas.setSVGDocument()。私がこれを介してこれを行おうとしたとき、UpdateManagerそれは最初の変更のみを更新しました。おそらく、updatequeueを正しく初期化することでこれを修正することができますが、ドキュメントの多くを変更するので、毎回新しく始めることもできます。

小さな問題が1つ残っています。新しいドキュメントを設定するたびに、スクロール位置がリセットされます。操作の前に変換を取得し、後で再適用しようとしましたが、これは機能していないようです。

Edit +:ドキュメント全体を置き換えるのではなく、ドキュメントのルートノードを置き換えることで、この小さな問題を修正することができました。キャンバスはdocumentstateALWAYS_DYNAMICで設定されます。ルートノードの新しいサイズ(上記のように設定)を含め、ドキュメントの変更はすぐに適用されます。ただし、ドキュメントが完全に置き換えられていないため、スクロール状態は保持されます。

キャンバスは、ドキュメントのレンダリングが終了した後でのみ、DOMを変更する準備ができています。これを検出するためにSVGLoad-listenerを使用しました。

class Canvas extends JSVGCanvas implements SVGLoadEventDispatcherListener {

    public Canvas() {
        super();
        setDocumentState(ALWAYS_DYNAMIC);
        addSVGLoadEventDispatcherListener(this);
    }

    /**
     * Indicatates whether the canvas has finished its first render, the
     * canvas is now ready for modification of the dom
     */
    private boolean isReadyForModification = false;

    /**
     * Renew the document by replacing the root node with the one of the new
     * document
     * 
     * @param doc The new document
     */
    public void renewDocument(final SVGDocument doc) {

        if (isReadyForModification) {
            getUpdateManager().getUpdateRunnableQueue().invokeLater(
                    new Runnable() {
                        @Override
                        public void run() {
                            // Get the root tags of the documents
                            Node oldRoot = getSVGDocument().getFirstChild();
                            Node newRoot = doc.getFirstChild();

                            // Make the new node suitable for the old
                            // document
                            newRoot = getSVGDocument().importNode(newRoot,
                                    true);

                            // Replace the nodes
                            getSVGDocument().replaceChild(newRoot, oldRoot);
                        }
                    });
        } else {
            setSVGDocument(doc);
        }

    }

    @Override
    public void svgLoadEventDispatchCompleted(SVGLoadEventDispatcherEvent e) {
        isReadyForModification = true;
    }

    // ...
}
于 2012-06-01T16:30:20.347 に答える