Java Batik resize SVG to panel size keeping aspect ratio - java

I'm trying to display an SVG Image on my swing application,
since i don't have an SVG file but a path i'm converting the path to a valid svg document with:
private static Document buildSVGDocument(Color svgColor, /*double svgWidth, double svgHeight,*/ String svgPath) {
DOMImplementation svgDocumentImplementation = SVGDOMImplementation.getDOMImplementation();
Document svgDocument = svgDocumentImplementation.createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, "svg", null);
Element svgDocumentElement = svgDocument.getDocumentElement();
//svgDocumentElement.setAttribute("height", String.valueOf(svgHeight));
//svgDocumentElement.setAttribute("width", String.valueOf(svgWidth));
Element svgDocumentPath = svgDocument.createElementNS(SVGDOMImplementation.SVG_NAMESPACE_URI, "path");
svgDocumentPath.setAttribute("style", String.format("fill:rgb(%s, %s, %s);", svgColor.getRed(), svgColor.getGreen(), svgColor.getBlue()));
svgDocumentPath.setAttribute("d", svgPath);
svgDocumentElement.appendChild(svgDocumentPath);
return svgDocument;
}
Then I display the SVG Document on a Batik Canvas:
JSVGCanvas panel = new JSVGCanvas();
panel.setDocumentState(JSVGCanvas.ALWAYS_DYNAMIC);
panel.setDisableInteractions(true);
panel.setDocument(buildSVGDocument(/*etc*/));
container.add(panel, BorderLayout.WEST);
Now my question is: how do I resize the svg to the panel size keeping the aspect-ratio?

I figured out how to do it!,
Browsing the source I found the method used to calculate the image scale, it's:
calculateViewingTransform
Then I've implemented a simple class to scale the image to the container
public class SVGCanvas extends JSVGCanvas {
private static final long serialVersionUID = 1L;
/**
* The type of scale
*/
private short svgScale;
/**
* Image padding
*/
private int svgPadding;
public SVGCanvas() {
this.svgScale = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMAX;
this.svgPadding = 5;
}
#Override
protected AffineTransform calculateViewingTransform(String svgElementIdentifier, SVGSVGElement svgElement) {
SVGRect svgElementBounds = svgElement.getBBox();
float[] svgElementBoundsVector = new float[] {
svgElementBounds.getX(),
svgElementBounds.getY(),
svgElementBounds.getWidth(),
svgElementBounds.getHeight()
};
float svgEemenetScaleToHeight = getHeight() - svgPadding;
float svgElementScaleToWidth = getWidth() - svgPadding;
return ViewBox.getPreserveAspectRatioTransform(
svgElementBoundsVector, svgScale, true,
svgElementScaleToWidth,
svgEemenetScaleToHeight
);
}
public void setSvgScale(short svgScale) {
this.svgScale = svgScale;
}
public void setSvgPadding(int svgPadding) {
this.svgPadding = svgPadding;
}
}

Related

Trying to add a JavaFX shape from another class and create it in my main class. The Class cannot be converted to node

I'm very new to Java. I've been using it for the last two months. The end goal of my code is to try and create a shape and add it to the window without having to set all the properties of the shape in the main "view" class.
When I attempt to add my work in progress Castle class, Intellij is telling me I need to provide a node instead. Below I will provide the class where I'm setting the properties of the shape and the main class where I get halted trying to add it to my window.
public class Castle {
private int width = 1280; // width of the window
private int height = 720; // height of the window
private double x, y, size;
private int denizens;
private Color color;
private String name;
private Random rn = new Random();
// constructor
Castle() {
x = 50;
y = 50;
size = 10;
color = Color.GRAY;
name = "Anthony's Castle";
denizens = rn.nextInt();
}
// GraphicsContext method
protected void draw(GraphicsContext gc) {
Rectangle rec = new Rectangle(x, y);
rec.setX(100);
rec.setY(100);
rec.setWidth(100);
rec.setHeight(100);
rec.setFill(Color.RED);
}
// get methods
public int getDenizens() {
return denizens;
}
public double getSize() {
return size;
}
}
public class TwoDomains extends Application {
public void start(Stage primaryStage) {
Group root = new Group();
Scene scene = new Scene(root, 1280, 720, Color.BEIGE);
Castle castle = new Castle();
root.getChildren().add(castle);
primaryStage.setScene(scene);
primaryStage.setTitle("Village");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You can only add Nodes to the scene graph, and your Castle class is not a node.
Your draw() method looks strange. It creates a Rectangle (which is a Node), but doesn't do anything with it. It also takes a GraphicsContext2D as a parameter, which looks like you're going to draw on a canvas, but you don't have a canvas.
You just need to organize the code so that, one way or another, your Castle class provides some kind of Node.
One way is to refactor your draw method as
public Node draw() {
Rectangle rec = new Rectangle(x, y);
rec.setX(100);
rec.setY(100);
rec.setWidth(100);
rec.setHeight(100);
rec.setFill(Color.RED);
return rec ;
}
and then replace
root.getChildren().add(castle);
with
root.getChildren().add(castle.draw());
Note that with this setup, each time you call draw() on a single Castle instance will create a new Node, which may or may not be what you want, but it is a very simple task to refactor it if it's not what you want.
Maybe you want:
public class Castle extends Group {
private int width = 1280; // width of the window - does EACH castle need a separate copy of this info?
private int height = 720; // height of the window
private double x, y, size;
private int denizens;
private Color color;
private String name;
private Random rn = new Random(); // This probably should be static, or somewhere else.
// constructor
Castle() {
x = 50;
y = 50;
size = 10;
color = Color.GRAY;
name = "Anthony's Castle";
denizens = rn.nextInt();
makeShape();
}
protected void makeShape() {
Rectangle rec = new Rectangle(x, y);
rec.setX(100);
rec.setY(100);
rec.setWidth(100);
rec.setHeight(100);
rec.setFill(Color.RED);
getChildren().add(rec);
}
// get methods
public int getDenizens() {
return denizens;
}
public double getSize() {
return size;
}
}

ArrayList<ObjectLabel> labels = GoogleCloudVision.detectImageLabels(); Not working

I'm trying to call GoogleCloudVision to get the image labels into an array. I don't know what to do for the ObjectLabel method which seems to be the problem. The program is meant to take an image file and run it through googles cloud vision and get an array of labels for the image, then based on the array determine what is the image.
this is the error I'm getting.
java.nio.file.NoSuchFileException: C:\Users\DELL Laptop\Downloads\Exam3Starter\src
at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:85)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
at java.base/sun.nio.fs.WindowsDirectoryStream.<init>(WindowsDirectoryStream.java:86)
at java.base/sun.nio.fs.WindowsFileSystemProvider.newDirectoryStream(WindowsFileSystemProvider.java:533)
at java.base/java.nio.file.Files.newDirectoryStream(Files.java:544)
at GoogleCloudVision.findJsonCredentialsFile(GoogleCloudVision.java:51)
at GoogleCloudVision.detectImageLabels(GoogleCloudVision.java:80)
at SeeFood.labelImage(SeeFood.java:57)
at SeeFood.main(SeeFood.java:83)
Code:
public static void labelImage(String filename) throws IOException, InterruptedException {
// TODO: replace with your implementation
String userFile = filename;
JFrame img = new JFrame();
ImageIcon icon = new ImageIcon(userFile);
JLabel label = new JLabel(icon);
img.add(label);
img.pack();
img.setVisible(true);
ArrayList<ObjectLabel> labels = GoogleCloudVision.detectImageLabels(userFile);
JOptionPane.showMessageDialog(null, labels);
} // end labelImage
public class ObjectLabel {
String Label = "";
float confidence = 2.0F;
public ObjectLabel(String description, float score) {
}
}

How can I add background color and image at a fixed position in a pdf at the same time using iText in java?

I am new to itext library. I want to add both background colour and image at a fix position at each page in my pdf. I tried to make use the following code given by itext community for my work - https://kb.itextpdf.com/home/it7kb/faq/how-can-i-add-an-image-to-all-pages-of-my-pdf
public static class ImageEventHandler implements IEventHandler {
protected Image img;
public ImageEventHandler(Image img) {
this.img = img;
}
#Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdfDoc = docEvent.getDocument();
PdfPage page = docEvent.getPage();
Rectangle area = page.getPageSize();
PdfCanvas aboveCanvas = new PdfCanvas(page.newContentStreamAfter(),
page.getResources(), pdfDoc)
.setStrokeColor(ColorConstants.BLACK)
.setFillColor(new DeviceRgb(240, 230, 140))
.rectangle(area)
.fillStroke()
.restoreState();
new Canvas(aboveCanvas, pdfDoc, area)
.add(img);
}
}
With this image is coming on each page with background colour but other contents like paragraph and cells are hidden. Please help me understand how to use this concept?

How to make an animation/transition with the width of a node in javafx?

I want to make an animation with the "width" of my node.
In this case my node is a "AnchorPane".
I try to make a navigation drawer in javafx.
there is no property "width Property ()"?
new Key Value (node.width Property (), 1, WEB_EASE)
node.widthProperty().getValue() not found
My code:
public void changeWidth(final Node node, double width) {
this.node = node;
this.timeline = TimelineBuilder.create()
.keyFrames(
new KeyFrame(Duration.millis(20),
new KeyValue( going here? , width, WEB_EASE)
)
)
.build();
setCycleDuration(Duration.seconds(5));
setDelay(Duration.seconds(0));
}
Example with "opacity" property:
new KeyValue(node.opacityProperty(), 1, WEB_EASE)
My class ConfigAnimationViewPane:
public class ConfigAnimationViewPane extends Transition {
protected static final Interpolator WEB_EASE = Interpolator.EASE_BOTH;
protected AnchorPane node;
protected Timeline timeline;
private boolean oldCache = false;
private CacheHint oldCacheHint = CacheHint.DEFAULT;
private final boolean useCache = true;
/**
* Called when the animation is starting
*/
protected void starting() {
if (useCache) {
oldCache = node.isCache();
oldCacheHint = node.getCacheHint();
node.setCache(true);
node.setCacheHint(CacheHint.SPEED);
}
}
/**
* Called when the animation is stopping
*/
protected void stopping() {
if (useCache) {
node.setCache(oldCache);
node.setCacheHint(oldCacheHint);
}
}
#Override protected void interpolate(double d) {
timeline.playFrom(Duration.seconds(d));
timeline.stop();
}
}
This is mi controller:
Move the menu to the left (the occult)
LeftTransition leftTransition = new LeftTransition();
leftTransition.OpenMenu(list1);
leftTransition.play();
Here I want to put my size "AnchorPane".
(Set the width of my "anchorpane")
/*ViewPaneTransition paneTransition = new ViewPaneTransition();
paneTransition.CloseMenu(viewPane, width );
paneTransition.play();*/
Here is a working example for java 9. It changes both the width and the height (just remove the height line if you don't need it)
The widthProperty is readOnly so yo have to set either maxWidth or minWidth switch the case you need.
the delay duration on timeline is 0 by default, no need to set it, and the cycleduration is computed from the keyframes durations.
public void changeSize(final Pane pane, double width, double height) {
Duration cycleDuration = Duration.millis(500);
Timeline timeline = new Timeline(
new KeyFrame(cycleDuration,
new KeyValue(pane.maxWidthProperty(),width,Interpolator.EASE_BOTH)),
new KeyFrame(cycleDuration,
new KeyValue(pane.maxHeightProperty(),height,Interpolator.EASE_BOTH))
);
timeline.play();
timeline.setOnFinished(event->{
/* insert code here if you need */
});
}
public void changeWidth(final Pane/*Region*/ node, double width) {//its a Pane or Regions
this.node = node;
this.timeline = TimelineBuilder.create()
.keyFrames(
new KeyFrame(Duration.millis(20),
new KeyValue( going here? , width, WEB_EASE)
)
)
.build();
setCycleDuration(Duration.seconds(5));
setDelay(Duration.seconds(0));
}
In this case my node is a "AnchorPane".
your AnchorPane is a subclass or Pane, let your wrappers or methods take in Pane or their respective class

LibGdx label background with 9patch

So i've come across this problem that i simply can't manage to sort out.
I'm making a game with the help of LibGdx and am trying to create a chat bubble functionality. The problem is, when i try to change the background of the label style to a 9patch drawable, it doesn't scale it well, or at all?
public class ChatBubble
{
private Label textLabel;
private BitmapFont font;
private Label.LabelStyle lStyle;
private int scaledWidth = 0;
private int scaledHeight = 0;
private Timer.Task currentTask;
private Texture bkg;
public ChatBubble()
{
font = new BitmapFont();
font.setColor(Color.BLACK);
bkg = new Texture("data/ui/chatb.9.png");
NinePatch np = new NinePatch(bkg,11,11,9,10);
NinePatchDrawable npd = new NinePatchDrawable(np);
lStyle = new Label.LabelStyle(font,font.getColor());
lStyle.background = npd;
textLabel = new Label("",lStyle);
textLabel.setVisible(false);
textLabel.setAlignment(Align.center);
currentTask = new Timer.Task() {
#Override
public void run() {
textLabel.setVisible(false);
}};
}
public void show(String text, float duration)
{
if(currentTask.isScheduled())currentTask.cancel();
textLabel.setText(text);
textLabel.setVisible(true);
scaledHeight = (int)textLabel.getPrefHeight();
scaledWidth = (int)textLabel.getWidth()/2;
Timer.schedule(currentTask,duration);
}
public void show(String text)
{
if(currentTask.isScheduled())currentTask.cancel();
textLabel.setText(text);
textLabel.setVisible(true);
scaledHeight = (int)textLabel.getPrefHeight();
scaledWidth = (int)textLabel.getWidth()/2;
Timer.schedule(currentTask,(float)(text.length()*0.1));
}
public void draw(SpriteBatch batch, float x, float y)
{
if(!textLabel.isVisible())return;
textLabel.setPosition(x - scaledWidth, y + scaledHeight);
batch.begin();
textLabel.draw(batch, 1);
batch.end();
}
}
How it looks ingame:
How the 9batch looks:
Any help would be appreciated!
Update:
I've found out that my 9patch scales ok, the problem being in label not updating it's size when setText() is called, thus having it width and height 0 since constructor was "".. calling layout() on label doesn't solve this either.
Call .pack() on the label after .setText() to tell it to size itself to its text (plus whatever padding there is in the background drawable). You don't need to call layout() since that's handled automatically.
I'm not sure the exact reason you have to manually call pack(), but this is generally the case with Widgets that you are not children of a WidgetGroup subclass (i.e. Table, VerticalGroup, etc.).

Categories