4

Batik を使用して SVG 画像を操作しています。具体的には、多数の形状を持つシーンがあり、各形状を個別の BufferedImage に変換できる必要があります。これを行うには、次のコードを使用します。

SVGDocument document = null;

// Load the document
String parser = XMLResourceDescriptor.getXMLParserClassName();
SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);

File file = new File(inPath);
try {
    document = (SVGDocument) f.createDocument(file.toURL().toString());
} catch (MalformedURLException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

// Build the tree and get the document dimensions
UserAgentAdapter userAgentAdapter = new UserAgentAdapter();
BridgeContext bridgeContext = new BridgeContext(userAgentAdapter);

GVTBuilder builder = new GVTBuilder();

GraphicsNode graphicsNode = builder.build(bridgeContext, document);
CanvasGraphicsNode canvasGraphicsNode = (CanvasGraphicsNode)
        graphicsNode.getRoot().getChildren().get(0);

if(canvasGraphicsNode.getChildren().get(i) instanceof ShapeNode) {
   currentNode = (ShapeNode) canvasGraphicsNode.getChildren().get(i);
    convertNodeToImage (currentNode);
}

これはかなり標準的です。Batik を起動し、SVG ファイルを解析します。ノードを画像に変換する関数は次のとおりです。

Rectangle2D bounds;
BufferedImage bufferedImage;
Graphics2D g2d;

// This is supposed to get the bounds of the svg node. i.e. the rectangle which would
// fit perfectly around the shape   
bounds = sn.getSensitiveBounds();

// Transform the shape so it's in the top left hand corner based on the bounds
sn.setTransform(AffineTransform.getTranslateInstance(-bounds.getX(), -bounds.getY()));

// Create a buffered image of the same size as the svg node         
bufferedImage = new BufferedImage((int) bounds.getWidth(), (int) bounds.getHeight(),
                BufferedImage.TYPE_INT_ARGB);

// Paint the node to the buffered image and convert the buffered image to an input       
// stream           
g2d = (Graphics2D) bufferedImage.getGraphics();
sn.paint(g2d);

ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", os);
InputStream is = new ByteArrayInputStream(os.toByteArray());
return is;

これは、長方形と直線形状ではうまく機能しますが、スプラインでは機能しません。スプラインの場合、境界はレンダリングされたスプラインよりも大きくなります。これは、getBounds 関数が境界計算に制御点を含めているためだと思います。スプラインだけの境界を見つける必要があります。つまり、スプラインがストロークされた場合、そのストロークの境界を見つけたいと思います。すべての getBounds() 関数 (getSensativeBounds、getGeometryBounds...) を試しましたが、すべて同じ結果が得られました。それで、私は何かを逃したのではないかと思っていますか?これはBatikのバグですか?または回避策がある場合は?

私が考えた回避策は、形状の頂点のリストを取得し、境界を手動で計算することです。ただし、アウトライン頂点のリストを取得する方法を見つけることができませんでした。

どんな助けでも大歓迎です。

4

1 に答える 1

4

この問題を抱えている人のために、私は解決策を見つけました。ドキュメントによると、 get bounds は、形状を完全に含む長方形だけで最小の境界を提供することが保証されていません。これは、境界を手動で計算する必要があることを意味します。スプラインは、形状の数学的な定義、つまり区分的連続関数です。これは、スプラインを特定の精度で計算する必要があることを意味します。これは、精度を倍増させるパス反復子を使用することによって実現されます。このパス反復子は LINE_TO コマンドのみを返します。つまり、形状の実際の境界を計算するために使用できます。

BufferedImage bufferedImage;
Graphics2D g2d;

// Manually calculate the bounds
double [] vals = new double[7];

double minX = Double.MAX_VALUE;
double maxX = 0;

double minY = Double.MAX_VALUE;
double maxY = 0;

// Get a path iterator iterating to a certain level of accuracy
PathIterator pi = sn.getOutline().getPathIterator(null, 0.01);

while(!pi.isDone()) {
    pi.currentSegment(vals);

    if(vals[0] < minX ) {
        minX = vals[0];
    }
    if(vals[0] > maxX ) {
        maxX = vals[0];
    }
    if(vals[1] < minY ) {
        minY = vals[1];
    }
    if(vals[1] > maxY ) {
        maxY = vals[1];
    }

    pi.next();
}

sn.setTransform(AffineTransform.getTranslateInstance(-minX, -minY));

bufferedImage = new BufferedImage((int) (maxX - minX), (int) (maxY - minY),
                BufferedImage.TYPE_INT_ARGB);

g2d = (Graphics2D) bufferedImage.getGraphics();

sn.paint(g2d);

ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", os);
InputStream is = new ByteArrayInputStream(os.toByteArray());
于 2012-05-16T12:21:13.627 に答える