Skip to content

Commit

Permalink
Fix #114: Add rotation to outputs
Browse files Browse the repository at this point in the history
  • Loading branch information
melloware committed Nov 13, 2024
1 parent 34c2efd commit 12fa710
Show file tree
Hide file tree
Showing 17 changed files with 818 additions and 50 deletions.
55 changes: 52 additions & 3 deletions src/main/java/uk/org/okapibarcode/output/Java2DRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.awt.Polygon;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
Expand Down Expand Up @@ -57,6 +58,9 @@ public class Java2DRenderer implements SymbolRenderer {
/** The ink (foreground) color. */
private final Color ink;

/** The clockwise rotation of the symbol in degrees (0, 90, 180, or 270). */
private final int rotation;

/**
* Creates a new Java 2D renderer. If the specified paper color is {@code null}, the symbol is drawn without clearing the
* existing {@code g2d} background.
Expand All @@ -67,18 +71,62 @@ public class Java2DRenderer implements SymbolRenderer {
* @param ink the ink (foreground) color
*/
public Java2DRenderer(Graphics2D g2d, double magnification, Color paper, Color ink) {
this(g2d, magnification, paper, ink, 0);
}

/**
* Creates a new Java 2D renderer. If the specified paper color is {@code null}, the symbol is drawn without clearing the
* existing {@code g2d} background.
*
* @param g2d the graphics to render to
* @param magnification the magnification factor to apply
* @param paper the paper (background) color (may be {@code null})
* @param ink the ink (foreground) color
* @param rotation the clockwise rotation of the symbol in degrees (0, 90, 180, or 270)
*/
public Java2DRenderer(Graphics2D g2d, double magnification, Color paper, Color ink, int rotation) {
this.g2d = g2d;
this.magnification = magnification;
this.paper = paper;
this.ink = ink;
this.rotation = SymbolRenderer.normalizeRotation(rotation);
}

/** {@inheritDoc} */
@Override
public void render(Symbol symbol) {

int width = (int) (symbol.getWidth() * magnification);
int height = (int) (symbol.getHeight() * magnification);
int marginX = (int) (symbol.getQuietZoneHorizontal() * magnification);
int marginY = (int) (symbol.getQuietZoneVertical() * magnification);
int rotateHeight = height;
int rotateWidth = width;

// render rotation clockwise
AffineTransform oldTransform = null;
if (rotation != 0) {
oldTransform = g2d.getTransform();
switch (rotation) {
case 90:
rotateHeight = width;
rotateWidth = height;
g2d.rotate(Math.PI / 2d);
g2d.translate(0, -rotateWidth);
break;
case 180:
g2d.rotate(-Math.PI);
g2d.translate(-width, -height);
break;
case 270:

rotateHeight = width;
rotateWidth = height;
g2d.rotate(Math.PI * 1.5d);
g2d.translate(-rotateHeight, 0);
break;
}
}

Font f = symbol.getFont();
if (f != null) {
Expand All @@ -92,10 +140,8 @@ public void render(Symbol symbol) {
java.awt.Color oldColor = g2d.getColor();

if (paper != null) {
int w = (int) (symbol.getWidth() * magnification);
int h = (int) (symbol.getHeight() * magnification);
g2d.setColor(new java.awt.Color(paper.red, paper.green, paper.blue));
g2d.fillRect(0, 0, w, h);
g2d.fillRect(0, 0, width, height);
}

g2d.setColor(new java.awt.Color(ink.red, ink.green, ink.blue));
Expand Down Expand Up @@ -154,6 +200,9 @@ public void render(Symbol symbol) {

g2d.setFont(oldFont);
g2d.setColor(oldColor);
if (oldTransform != null) {
g2d.setTransform(oldTransform);
}
}

private static Rectangle2D getBounds(TextBox text, Graphics2D g2d) {
Expand Down
57 changes: 56 additions & 1 deletion src/main/java/uk/org/okapibarcode/output/PostScriptRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ public class PostScriptRenderer implements SymbolRenderer {
/** The ink (foreground) color. */
private final Color ink;

/** The clockwise rotation of the symbol in degrees. */
private final int rotation;

/**
* Creates a new PostScript renderer.
*
Expand All @@ -61,10 +64,24 @@ public class PostScriptRenderer implements SymbolRenderer {
* @param ink the ink (foreground) color
*/
public PostScriptRenderer(OutputStream out, double magnification, Color paper, Color ink) {
this(out, magnification, paper, ink, 0);
}

/**
* Creates a new PostScript renderer.
*
* @param out the output stream to render to
* @param magnification the magnification factor to apply
* @param paper the paper (background) color
* @param ink the ink (foreground) color
* @param rotation the clockwise rotation of the symbol in degrees (0, 90, 180, or 270)
*/
public PostScriptRenderer(OutputStream out, double magnification, Color paper, Color ink, int rotation) {
this.out = out;
this.magnification = magnification;
this.paper = paper;
this.ink = ink;
this.rotation = SymbolRenderer.normalizeRotation(rotation);
}

/** {@inheritDoc} */
Expand All @@ -79,6 +96,20 @@ public void render(Symbol symbol) throws IOException {
int marginX = (int) (symbol.getQuietZoneHorizontal() * magnification);
int marginY = (int) (symbol.getQuietZoneVertical() * magnification);

// render rotation clockwise
int rotateHeight = height;
int rotateWidth = width;
String transform;
switch (rotation) {
case 90:
case 270:
rotateHeight = width;
rotateWidth = height;
break;
default:
break;
}

String title;
if (content.isEmpty()) {
title = "OkapiBarcode Generated Symbol";
Expand All @@ -93,7 +124,7 @@ public void render(Symbol symbol) throws IOException {
writer.append("%%Creator: OkapiBarcode\n");
writer.append("%%Title: ").append(title).append('\n');
writer.append("%%Pages: 0\n");
writer.append("%%BoundingBox: 0 0 ").appendInt(width).append(" ").appendInt(height).append("\n");
writer.append("%%BoundingBox: 0 0 ").appendInt(rotateWidth).append(" ").appendInt(rotateHeight).append("\n");
writer.append("%%EndComments\n");

// Definitions
Expand All @@ -104,6 +135,25 @@ public void render(Symbol symbol) throws IOException {
writer.append("/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def\n");
writer.append("/TE { pop pop } bind def\n");

// Set orientation
switch (rotation) {
case 90:
case 270:
writer.append("gsave\n");
writer.append(height / 2d).append(" ").append(width / 2d).append(" translate\n");
writer.append(rotation).append(" rotate\n");
writer.append(-height / 2d).append(" ").append(-width / 2d).append(" translate\n");
break;
case 180:
writer.append("gsave\n");
writer.append(width / 2d).append(" ").append(height / 2d).append(" translate\n");
writer.append(rotation).append(" rotate\n");
writer.append(-width / 2d).append(" ").append(-height / 2d).append(" translate\n");
break;
default:
break;
}

// Background
writer.append("newpath\n");
writer.append(ink.red / 255.0).append(" ")
Expand Down Expand Up @@ -235,6 +285,11 @@ public void render(Symbol symbol) throws IOException {
writer.append(" TH\n");
}

// Restore original transformation if rotated
if (rotation != 0) {
writer.append("grestore\n");
}

// Footer
writer.append("\nshowpage\n");
}
Expand Down
50 changes: 47 additions & 3 deletions src/main/java/uk/org/okapibarcode/output/SvgRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ public class SvgRenderer implements SymbolRenderer {

/** Whether or not to include the XML prolog in the output. */
private final boolean xmlProlog;

/** The clockwise rotation of the symbol in degrees. */
private final int rotation;

/**
* Creates a new SVG renderer.
Expand All @@ -79,11 +82,27 @@ public class SvgRenderer implements SymbolRenderer {
* standalone SVG documents, {@code false} for SVG content embedded directly in HTML documents)
*/
public SvgRenderer(OutputStream out, double magnification, Color paper, Color ink, boolean xmlProlog) {
this(out, magnification, paper, ink, xmlProlog, 0);
}

/**
* Creates a new SVG renderer.
*
* @param out the output stream to render to
* @param magnification the magnification factor to apply
* @param paper the paper (background) color
* @param ink the ink (foreground) color
* @param xmlProlog whether or not to include the XML prolog in the output (usually {@code true} for
* standalone SVG documents, {@code false} for SVG content embedded directly in HTML documents)
* @param rotation the clockwise rotation of the symbol in degrees (0, 90, 180, or 270)
*/
public SvgRenderer(OutputStream out, double magnification, Color paper, Color ink, boolean xmlProlog, int rotation) {
this.out = out;
this.magnification = magnification;
this.paper = paper;
this.ink = ink;
this.xmlProlog = xmlProlog;
this.rotation = SymbolRenderer.normalizeRotation(rotation);
}

/** {@inheritDoc} */
Expand Down Expand Up @@ -120,13 +139,38 @@ public void render(Symbol symbol) throws IOException {
writer.append(" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
}

// render rotation clockwise
int rotateHeight = height;
int rotateWidth = width;
String transform;
switch (rotation) {
case 90:
rotateHeight = width;
rotateWidth = height;
transform = " transform=\"rotate("+rotation+") translate(0,-" + rotateWidth + ")\"";
break;
case 180:
transform = " transform=\"rotate(" + rotation + "," + (width/2) + "," + (height/2) + ")\"";
break;
case 270:
rotateHeight = width;
rotateWidth = height;
transform = " transform=\"rotate("+rotation+") translate(-"+rotateHeight+",0)\"";
break;
default:
transform = "";
break;
}

// Header
writer.append("<svg width=\"").appendInt(width)
.append("\" height=\"").appendInt(height)
writer.append("<svg width=\"").appendInt(rotateWidth)
.append("\" height=\"").appendInt(rotateHeight)
.append("\" version=\"1.1")
.append("\" xmlns=\"http://www.w3.org/2000/svg\">\n");
writer.append(" <desc>").append(clean(title)).append("</desc>\n");
writer.append(" <g id=\"barcode\" fill=\"#").append(fgColour).append("\">\n");


writer.append(" <g id=\"barcode\" fill=\"#").append(fgColour).append("\"").append(transform).append(">\n");
writer.append(" <rect x=\"0\" y=\"0\" width=\"").appendInt(width)
.append("\" height=\"").appendInt(height)
.append("\" fill=\"#").append(bgColour).append("\" />\n");
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/uk/org/okapibarcode/output/SymbolRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,19 @@ public interface SymbolRenderer {
*/
void render(Symbol symbol) throws IOException;

/**
* Normalizes clockwise rotation values to 0, 90, 180, or 270 degrees.
*
* @param rotation the clockwise rotation to normalize
* @return the normalized rotation (0, 90, 180, or 270)
* @throws IllegalArgumentException if rotation is not a multiple of 90 degrees
*/
static int normalizeRotation(int rotation) {
int normalized = ((rotation % 360) + 360) % 360;
if (normalized % 90 != 0) {
throw new IllegalArgumentException("Rotation must be a multiple of 90 degrees");
}
return normalized;
}

}
Loading

0 comments on commit 12fa710

Please sign in to comment.