How to fit width of a page when printing in java - java

I can print any component smartly with a footer by this code. Its working smartly.
public class MultiPagePrintable implements Printable {
private JComponent component;
private int lastPage = 0;
private double yOffset;
private Font footerFont;
public MultiPagePrintable(JComponent component) {
this.component = component;
footerFont = new Font("Arial", Font.BOLD, 24);
}
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
int result = NO_SUCH_PAGE;
String name = "I be mighty!";
String page = Integer.toString(pageIndex);
FontMetrics fm = graphics.getFontMetrics(footerFont);
double footerHeight = fm.getHeight() + 4;
double height = pageFormat.getImageableHeight() - footerHeight;
component.setSize(component.getPreferredSize());
if (lastPage != pageIndex) {
lastPage = pageIndex;
yOffset = height * pageIndex;
if (yOffset > component.getHeight()) {
yOffset = -1;
}
}
if (yOffset >= 0) {
Graphics2D g2d = (Graphics2D) graphics.create();
g2d.translate((int) pageFormat.getImageableX(),
(int) pageFormat.getImageableY());
g2d.translate(0, -yOffset);
component.printAll(g2d);
g2d.translate(0, +yOffset);
Shape footerArea = new Rectangle2D.Double(0, height, pageFormat.getImageableWidth(), footerHeight);
g2d.setColor(Color.WHITE);
g2d.fill(footerArea);
g2d.setColor(Color.RED);
g2d.draw(footerArea);
g2d.setColor(Color.BLACK);
g2d.translate(0, (pageFormat.getImageableHeight() - footerHeight));
float x = 2;
float y = (float)((footerHeight - fm.getHeight()) / 2d);
g2d.drawString(name, x, y + fm.getAscent());
x = (float)(pageFormat.getImageableWidth() - fm.stringWidth(page) - 2);
g2d.drawString(page, x, y + fm.getAscent());
g2d.dispose();
result = PAGE_EXISTS;
}
return result;
}
}
But problem is, it can't scale component width to fit page. However i don't want to fit height. Because this code can already print multiple page. and i need it.

You want to scale the Graphics2D:
g2d.translate(0, -yOffset);
double width = pageFormat.getImageableWidth();
double scale = Math.min(width / component.getWidth(),
height / component.getHeight());
if (scale < 1) {
AffineTransform oldTransform = g2d.getTransform();
g2d.scale(scale, scale);
component.printAll(g2d);
g2d.setTransform(oldTransform);
} else {
component.printAll(g2d);
}
g2d.translate(0, +yOffset);
I'm not sure what you mean by "I don't want to fit height" but you can always ignore the height, if you want:
g2d.translate(0, -yOffset);
double width = pageFormat.getImageableWidth();
double scale = width / component.getWidth();
if (scale < 1) {
AffineTransform oldTransform = g2d.getTransform();
g2d.scale(scale, scale);
component.printAll(g2d);
g2d.setTransform(oldTransform);
} else {
component.printAll(g2d);
}
g2d.translate(0, +yOffset);

Related

Java Graphics - Remove part of rounded rectangle under text?

I'm trying to render text on top of a rounded rectangle, but I want the part of the rounded rectangle under the text to be cut out. Here's what I want it to look like:
The problem is that I can't find any easy way of doing this. I tried using clearRect, but that just creates a black rectangle, and I want to have an image underneath (for now it's just white).
I then had the idea that maybe I could just fill the area I want to remove of the rectangle with white, then filter out all the white pixels. This didn't work as well as I hoped, as there are still white pixels left over:
Here's the code I have currently:
public static void createRoundedRectImg(int width, int height)
{
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.getGraphics();
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
int padding = 50;
g.setColor(Color.BLUE);
g.drawRoundRect(padding, padding, width - (padding * 2), height - (padding * 2), 50, 50);
float textSize = 84f;
Font font = g.getFont().deriveFont(textSize).deriveFont(Font.BOLD);
g.setFont(font);
String text = "TEXT";
Rectangle2D stringBounds = g.getFontMetrics(font).getStringBounds(text, g);
int textWidth = (int) stringBounds.getWidth();
int textHeight = (int) (stringBounds.getHeight() + g.getFontMetrics(font).getDescent());
int textX = (width / 2) - (textWidth / 2);
int textY = g.getFontMetrics(font).getDescent() * 2 + padding;
//g.clearRect(textX, textY - textHeight, textWidth, textHeight);
g.setColor(Color.WHITE);
g.fillRect(textX, textY - textHeight, textWidth, textHeight);
g.setColor(Color.GREEN);
g.drawString(text, textX, textY);
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Color c = new Color(img.getRGB(x, y));
if (c.getRGB() == Color.WHITE.getRGB())
img.setRGB(x, y, new Color(0, 0, 0, 255).getRGB());
}
}
g.dispose();
}
Is there a simpler way of just clearing the part of the rounded rectangle under the text? After that is done I want to overlay the whole thing on top of an image, so I need the background to be transparent.
You could use the subtract method of the Area class to remove a rectangular section from a stroked RoundRectangle2D.
float strokeWidth = 1.5f;
RoundRectangle2D roundedRect = new RoundRectangle2D.Double(padding, padding, width - (padding * 2), height - (padding * 2), 50, 50);
Rectangle2D rectMask = new Rectangle2D.Double(textX, padding-strokeWidth, textWidth, 2*strokeWidth);
Stroke stroke = new BasicStroke(strokeWidth);
Area roundedRectArea = new Area(stroke.createStrokedShape(roundedRect));
roundedRectArea.subtract(new Area(rectMask));
g.setColor(Color.BLACK);
g.fill(roundedRectArea);
g.drawString(text, textX, textY);
Which produces:
Full code:
public static void createRoundedRectImg(int width, int height)
{
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = img.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
float textSize = 84f;
Font font = g.getFont().deriveFont(textSize).deriveFont(Font.BOLD);
g.setFont(font);
int padding = 50;
String text = "TEXT";
Rectangle2D stringBounds = g.getFontMetrics(font).getStringBounds(text, g);
int textWidth = (int) stringBounds.getWidth();
int textX = (width / 2) - (textWidth / 2);
int textY = g.getFontMetrics(font).getDescent() * 2 + padding;
float strokeWidth = 1.5f;
RoundRectangle2D roundedRect = new RoundRectangle2D.Double(padding, padding, width - (padding * 2),
height - (padding * 2), 50, 50);
Rectangle2D rectMask = new Rectangle2D.Double(textX, padding - strokeWidth, textWidth, 2 * strokeWidth);
Stroke stroke = new BasicStroke(strokeWidth);
Area roundedRectArea = new Area(stroke.createStrokedShape(roundedRect));
roundedRectArea.subtract(new Area(rectMask));
g.setColor(Color.BLACK);
g.fill(roundedRectArea);
g.drawString(text, textX, textY);
g.dispose();
try
{
ImageIO.write(img, "png", new File("round.png"));
} catch (IOException e)
{
e.printStackTrace();
}
}
Try this.
public static void createRoundedRectImg(int width, int height)
{
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D)img.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
int padding = 50;
g.setComposite(AlphaComposite.Clear);
g.fillRoundRect(padding, padding, width - (padding * 2), height - (padding * 2), 50, 50);
g.setComposite(AlphaComposite.SrcOver);
g.setColor(Color.BLUE);
g.drawRoundRect(padding, padding, width - (padding * 2), height - (padding * 2), 50, 50);
float textSize = 84f;
Font font = g.getFont().deriveFont(textSize).deriveFont(Font.BOLD);
g.setFont(font);
String text = "TEXT";
Rectangle2D stringBounds = g.getFontMetrics(font).getStringBounds(text, g);
int textWidth = (int) stringBounds.getWidth();
int textHeight = (int) (stringBounds.getHeight() + g.getFontMetrics(font).getDescent());
int textX = (width / 2) - (textWidth / 2);
int textY = g.getFontMetrics(font).getDescent() * 2 + padding;
g.setColor(Color.WHITE);
g.fillRect(textX, textY - textHeight, textWidth, textHeight);
g.setColor(Color.GREEN);
g.drawString(text, textX, textY);
g.dispose();
}
You could just use a TitledBorder. Since the curve is proportional to the line size I created a RoundedBorder class with most of the code from the paintBorder() method in the API to allow the size of the arc corners to be specified. It is now a simple pixel amount for both width and height of the arc radius.
first, create a RoundedBorder instance. Try 30 for the arc radius.
then, using the BorderFactor, create a TitledBorder instance and pass the rounded instance as the first agument.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
public class TitledBorderDemo extends JPanel {
JFrame frame = new JFrame();
public static void main(String[] args) {
SwingUtilities
.invokeLater(() -> new TitledBorderDemo().start());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
public void start() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Border b = new RoundedBorder(Color.black, 2, 30);
Border titled = BorderFactory.createTitledBorder(b, "Text",
TitledBorder.CENTER, TitledBorder.DEFAULT_POSITION,
new Font("Arial", Font.BOLD, 48));
setBorder(titled);
frame.add(this);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class RoundedBorder extends LineBorder {
private int arc;
public RoundedBorder(Color color, int lineThickness, int arc) {
super(color, lineThickness);
this.arc = arc;
}
#Override
public void paintBorder(Component c, Graphics g, int x, int y,
int width, int height) {
if ((this.thickness > 0) && (g instanceof Graphics2D)) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Color oldColor = g2d.getColor();
g2d.setColor(this.lineColor);
Shape outer;
Shape inner;
int offs = this.thickness;
int size = offs + offs;
outer = new RoundRectangle2D.Float(x, y, width, height,
arc, arc);
inner = new RoundRectangle2D.Float(x + offs, y + offs,
width - size, height - size, arc, arc);
Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD);
path.append(outer, false);
path.append(inner, false);
g2d.fill(path);
g2d.setColor(oldColor);
}
}
}
The above, when run, produces the following image.

Resize Buffered Image without creating new instance (java)

I was wondering if there is a way to resize a BufferedImage without creating a new instance of another image. I am wondering this because I think that it will be inefficient to create a new image each time I want to resize a BufferedImage for my application. Here is some code I've seen that explains what I don't want:
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
Image tmp = img.getScaledInstance(newW, newH, Image.SCALE_SMOOTH);
BufferedImage dimg = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = dimg.createGraphics();
g2d.drawImage(tmp, 0, 0, null);
g2d.dispose();
return dimg;
}
public static BufferedImage scale(BufferedImage src, int w, int h)
{
BufferedImage img =
new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
int x, y;
int ww = src.getWidth();
int hh = src.getHeight();
int[] ys = new int[h];
for (y = 0; y < h; y++)
ys[y] = y * hh / h;
for (x = 0; x < w; x++) {
int newX = x * ww / w;
for (y = 0; y < h; y++) {
int col = src.getRGB(newX, ys[y]);
img.setRGB(x, y, col);
}
}
return img;
}
private BufferedImage resize(BufferedImage src, int targetSize) {
if (targetSize <= 0) {
return src; //this can't be resized
}
int targetWidth = targetSize;
int targetHeight = targetSize;
float ratio = ((float) src.getHeight() / (float) src.getWidth());
if (ratio <= 1) { //square or landscape-oriented image
targetHeight = (int) Math.ceil((float) targetWidth * ratio);
} else { //portrait image
targetWidth = Math.round((float) targetHeight / ratio);
}
BufferedImage bi = new BufferedImage(targetWidth, targetHeight, src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); //produces a balanced resizing (fast and decent quality)
g2d.drawImage(src, 0, 0, targetWidth, targetHeight, null);
g2d.dispose();
return bi;
}
Thank you for any responses!

Drawing image on Graphics object does not work, but drawing lines work

For some reason painting an image does not appear to work on my Graphics object, please see the Triangle -> paintComponent() method.
Could I be missing something critical? Perhaps to do with not casting the Graphics object to Graphics2D? A transparency mask?
final JFrame jFrame = new JFrame();
jFrame.add(new Triangle());
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(500,500);
jFrame.repaint();
jFrame.setVisible(true);
Triangle class:
public class Triangle extends JPanel {
private int x = 50;
private int y = 50;
private int width = 100;
private int length = 300;
private int direction = 0;
private BufferedImage renderedImage = null;
private Image getRenderedImage() {
if (renderedImage == null) {
BufferedImage image = new BufferedImage(100, 300, BufferedImage.TYPE_3BYTE_BGR);
drawGraphics(image.getGraphics());
}
return renderedImage;
}
private void drawGraphics(Graphics graphics) {
graphics.setColor(Color.BLUE);
// left
graphics.drawLine(
(int) Math.round(x - (width / 2.0)), y,
(int) x, y + length);
// right
graphics.drawLine(
(int) x, y + length,
(int) Math.round(x + (width / 2.0)), y);
// bottom
graphics.drawLine(
(int) Math.round(x - (width / 2.0)), y,
(int) Math.round(x + (width / 2.0)), y);
}
protected void paintComponent(Graphics graphics) {
// this works
//drawGraphics(graphics);
// this doesn't work
graphics.drawImage(getRenderedImage(), 0, 0, null);
}
}

Using Bitmap to draw an image in Android

I need to put a random image inside a screen with given resolution (640x480, 1280x720, etc). I finished it in Java. In Android do not support the BufferedImage and Graphics2D, I wonder if there is a way to replace this code from Java to Android. Here is my code from Java:
public BufferedImage resizeImage(BufferedImage originalImage, int type){
BufferedImage resizedImage = new BufferedImage(screenWidth, screenHeight, type);
Graphics2D g = resizedImage.createGraphics();
int imgWidth = originalImage.getWidth();
int imgHeight = originalImage.getHeight();
int newImgWidth = 0;
int newImgHeight = 0;
int X = 0;
int Y = 0;
if (imgWidth > screenWidth){
// scale width to fit
newImgWidth = screenWidth;
//scale height to maintain aspect ratio
newImgHeight = (newImgWidth * imgHeight) / imgWidth;
}
if (newImgHeight > screenHeight) {
//scale height to fit instead
newImgHeight = screenHeight;
//scale width to maintain aspect ratio
newImgWidth = (newImgHeight * imgWidth) / imgHeight;
}
if (imgWidth < screenWidth && imgHeight < screenHeight) {
X = screenWidth/2 - imgWidth/2;
Y = screenHeight/2 - imgHeight/2;
g.drawImage(originalImage, X, Y, imgWidth, imgHeight, null);
g.dispose();
return resizedImage;
}
X = screenWidth/2 - newImgWidth/2;
Y = screenHeight/2 - newImgHeight/2;
g.drawImage(originalImage, X, Y, newImgWidth, newImgHeight, null);
g.dispose();
return resizedImage;
}
Thank you in advance!

Java2D Image Rotation Issue

I am using following code to rotate an given image.
public int getImageWidth(BufferedImage img) {
if (rotate == Rotate.UPSIDE_DOWN || rotate == Rotate.ABOUT_CENTER)
return img.getWidth();
else
return img.getHeight();
}
public int getImageHeight(BufferedImage img) {
if (rotate == Rotate.UPSIDE_DOWN || rotate == Rotate.ABOUT_CENTER)
return img.getHeight();
else
return img.getWidth();
}
This is the Rotation enums
public enum Rotate {
DOWN, UP, UPSIDE_DOWN, ABOUT_CENTER;
}
Actual rotation method
public BufferedImage rotateImage(BufferedImage source, int x, int y,
float orientation) throws Exception {
int newWidth = getImageWidth(source);
int newHeight = getImageHeight(source);
int cWidth = newWidth / 2;
int cHeight = newHeight / 2;
int imgType = source.getType() == 0 ? 5 : source.getType();
BufferedImage result = new BufferedImage(getImageWidth(source),
getImageHeight(source),imgType);
Graphics2D g2 = result.createGraphics();
if (rotate == Rotate.DOWN) {
g2.translate(x + cHeight, y + cWidth);
g2.rotate(Math.toRadians(90));
g2.drawImage(source, 0, 0, newWidth, newHeight, null);
} else if (rotate == Rotate.UP) {
g2.translate(x + cHeight, y + cWidth);
g2.rotate(Math.toRadians(-90));
g2.drawImage(source, 0, 0, newWidth, newHeight, null);
} else if (rotate == Rotate.UPSIDE_DOWN) {
g2.translate(x + cWidth, y + cHeight);
g2.rotate(Math.toRadians(180));
g2.drawImage(source, 0, 0, newWidth, newHeight, null);
} else if (rotate == Rotate.ABOUT_CENTER) {
Rectangle r = new Rectangle(x, y, newWidth, newHeight);
g2.setClip(r);
AffineTransform original = g2.getTransform();
AffineTransform at = new AffineTransform();
at.concatenate(original);
at.rotate(orientation, x + cWidth, y + cHeight);
g2.setTransform(at);
g2.drawImage(source, 0, 0, newWidth, newHeight, null);
g2.setTransform(original);
}
g2.dispose();
g2 = null;
return result;
}
The client code
// rotate derived & filtered image to 90 degree
// using Affine transform
setRotate(Rotate.UP);
BufferedImage rSubImage = rotateImage(fSubImage, 0, 0, -90);
Now the following is an source image,
When i rotate this image using above code , the result is very strange
What did i do wrong ?
also the quality is lost after rotation , please notice it.
I think the solution is to use AffineTranform, translating the Image to reamin in the center. I modified your code and tested it, for me it works:
public BufferedImage rotateImage(BufferedImage source, int x, int y,
float orientation) throws Exception {
int newWidth = getImageWidth(source);
int newHeight = getImageHeight(source);
int imgType = source.getType() == 0 ? 5 : source.getType();
BufferedImage result = new BufferedImage(getImageWidth(source),
getImageHeight(source), imgType);
if (rotate == Rotate.DOWN) {
AffineTransform tranform = new AffineTransform();
tranform.translate(newWidth / 2, newHeight / 2);
tranform.rotate(Math.toRadians(90));
tranform.translate(-source.getWidth()/2, -source.getHeight()/2);
Graphics2D g2d = result.createGraphics();
g2d.drawImage(source, tranform, null);
} else if (rotate == Rotate.UP) {
AffineTransform tranform = new AffineTransform();
tranform.translate(newWidth / 2, newHeight / 2);
tranform.rotate(Math.toRadians(-90));
tranform.translate(-source.getWidth()/2, -source.getHeight()/2);
Graphics2D g2d = result.createGraphics();
g2d.drawImage(source, tranform, null);
} else if (rotate == Rotate.UPSIDE_DOWN) {
AffineTransform tranform = new AffineTransform();
tranform.translate(newWidth / 2, newHeight / 2);
tranform.rotate(Math.toRadians(180));
tranform.translate(-source.getWidth()/2, -source.getHeight()/2);
Graphics2D g2d = result.createGraphics();
g2d.drawImage(source, tranform, null);
} else if (rotate == Rotate.ABOUT_CENTER) {
//......
}
return result;
}
Hope it helps!

Categories