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 5, 2024
1 parent 34c2efd commit 183f42f
Show file tree
Hide file tree
Showing 12 changed files with 565 additions and 38 deletions.
27 changes: 27 additions & 0 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,15 +71,35 @@ 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 with the specified clockwise rotation. 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 = normalizeRotation(rotation);
}

/** {@inheritDoc} */
@Override
public void render(Symbol symbol) {
AffineTransform oldTransform = null;
if (rotation != 0) {
oldTransform = g2d.getTransform();
g2d.rotate(Math.toRadians(rotation));
}

int marginX = (int) (symbol.getQuietZoneHorizontal() * magnification);
int marginY = (int) (symbol.getQuietZoneVertical() * magnification);
Expand Down Expand Up @@ -154,6 +178,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
30 changes: 30 additions & 0 deletions 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 with the specified clockwise rotation.
*
* @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 = normalizeRotation(rotation);
}

/** {@inheritDoc} */
Expand Down Expand Up @@ -104,6 +121,14 @@ 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
if (rotation != 0) {
writer.append("gsave\n");
writer.append(width/2).append(" ").append(height/2).append(" translate\n");
writer.append(rotation).append(" rotate\n");
writer.append(-width/2).append(" ").append(-height/2).append(" translate\n");
}

// Background
writer.append("newpath\n");
writer.append(ink.red / 255.0).append(" ")
Expand Down Expand Up @@ -235,6 +260,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
27 changes: 26 additions & 1 deletion 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 with the specified clockwise rotation.
*
* @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 = normalizeRotation(rotation);
}

/** {@inheritDoc} */
Expand Down Expand Up @@ -126,7 +145,13 @@ public void render(Symbol symbol) throws IOException {
.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");

String transform = "";
if (rotation != 0) {
transform = " transform=\"rotate(" + rotation + "," + (width/2) + "," + (height/2) + ")\"";
}

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
9 changes: 9 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,13 @@ 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)
*/
default int normalizeRotation(int rotation) {
return Math.abs(rotation) % 360;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void testCode93Basic() throws IOException {
code93.setQuietZoneHorizontal(5);
code93.setQuietZoneVertical(5);
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, "code93-basic.eps");
test(code93, 1, Color.WHITE, Color.BLACK, 0, "code93-basic.eps");
}

@Test
Expand All @@ -74,7 +74,7 @@ public void testCode93AlignmentLeft() throws IOException {
code93.setQuietZoneVertical(5);
code93.setHumanReadableAlignment(TextAlignment.LEFT);
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, "code93-alignment-left.eps");
test(code93, 1, Color.WHITE, Color.BLACK, 0, "code93-alignment-left.eps");
}

@Test
Expand All @@ -84,7 +84,7 @@ public void testCode93AlignmentRight() throws IOException {
code93.setQuietZoneVertical(5);
code93.setHumanReadableAlignment(TextAlignment.RIGHT);
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, "code93-alignment-right.eps");
test(code93, 1, Color.WHITE, Color.BLACK, 0, "code93-alignment-right.eps");
}

@Test
Expand All @@ -94,7 +94,7 @@ public void testCode93AlignmentJustify() throws IOException {
code93.setQuietZoneVertical(5);
code93.setHumanReadableAlignment(TextAlignment.JUSTIFY);
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, "code93-alignment-justify.eps");
test(code93, 1, Color.WHITE, Color.BLACK, 0, "code93-alignment-justify.eps");
}

@Test
Expand All @@ -105,7 +105,7 @@ public void testCode93AlignmentJustifyOneChar() throws IOException {
code93.setShowCheckDigits(false);
code93.setHumanReadableAlignment(TextAlignment.JUSTIFY);
code93.setContent("1");
test(code93, 1, Color.WHITE, Color.BLACK, "code93-alignment-justify-one-char.eps");
test(code93, 1, Color.WHITE, Color.BLACK, 0, "code93-alignment-justify-one-char.eps");
}

@Test
Expand All @@ -114,7 +114,7 @@ public void testCode93Margin() throws IOException {
code93.setQuietZoneHorizontal(20);
code93.setQuietZoneVertical(20);
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, "code93-margin-size-20.eps");
test(code93, 1, Color.WHITE, Color.BLACK, 0, "code93-margin-size-20.eps");
}

@Test
Expand All @@ -123,7 +123,7 @@ public void testCode93Magnification() throws IOException {
code93.setQuietZoneHorizontal(5);
code93.setQuietZoneVertical(5);
code93.setContent("123456789");
test(code93, 2, Color.WHITE, Color.BLACK, "code93-magnification-2.eps");
test(code93, 2, Color.WHITE, Color.BLACK, 0, "code93-magnification-2.eps");
}

@Test
Expand All @@ -132,7 +132,7 @@ public void testCode93Colors() throws IOException {
code93.setQuietZoneHorizontal(5);
code93.setQuietZoneVertical(5);
code93.setContent("123456789");
test(code93, 1, Color.GREEN, Color.RED, "code93-colors.eps");
test(code93, 1, Color.GREEN, Color.RED, 0, "code93-colors.eps");
}

@Test
Expand All @@ -143,7 +143,7 @@ public void testCode93CustomFont() throws IOException {
code93.setFontName("Arial");
code93.setFontSize(26);
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, "code93-custom-font.eps");
test(code93, 1, Color.WHITE, Color.BLACK, 0, "code93-custom-font.eps");
}

@Test
Expand All @@ -153,7 +153,7 @@ public void testCode93Empty() throws IOException {
code93.setQuietZoneVertical(5);
code93.setEmptyContentAllowed(true);
code93.setContent("");
test(code93, 1, Color.WHITE, Color.BLACK, "code93-empty.eps");
test(code93, 1, Color.WHITE, Color.BLACK, 0, "code93-empty.eps");
}

@Test
Expand All @@ -163,7 +163,7 @@ public void testCode93Null() throws IOException {
code93.setQuietZoneVertical(5);
code93.setEmptyContentAllowed(true);
code93.setContent(null);
test(code93, 1, Color.WHITE, Color.BLACK, "code93-empty.eps");
test(code93, 1, Color.WHITE, Color.BLACK, 0, "code93-empty.eps");
}

@Test
Expand All @@ -173,7 +173,7 @@ public void testMaxiCodeBasic() throws IOException {
maxicode.setQuietZoneVertical(5);
maxicode.setMode(4);
maxicode.setContent("123456789");
test(maxicode, 5, Color.WHITE, Color.BLACK, "maxicode-basic.eps");
test(maxicode, 5, Color.WHITE, Color.BLACK, 0, "maxicode-basic.eps");
}

@Test
Expand All @@ -182,7 +182,7 @@ public void testQrCodeBasic() throws IOException {
qr.setQuietZoneHorizontal(5);
qr.setQuietZoneVertical(5);
qr.setContent("123456789");
test(qr, 5, Color.WHITE, Color.BLACK, "qr-basic.eps");
test(qr, 5, Color.WHITE, Color.BLACK, 0, "qr-basic.eps");
}

@Test
Expand All @@ -192,20 +192,42 @@ public void testEan13WithAddOn() throws IOException {
ean.setQuietZoneHorizontal(5);
ean.setQuietZoneVertical(5);
ean.setContent("123456789012+12345");
test(ean, 2, Color.WHITE, Color.BLACK, "ean-13-with-add-on.eps");
test(ean, 2, Color.WHITE, Color.BLACK, 0, "ean-13-with-add-on.eps");
}

@Test
public void testCode93With1dot2Magnification() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 1.2, Color.WHITE, Color.BLACK, "code93-with-magnification-1.2.eps");
test(code93, 1.2, Color.WHITE, Color.BLACK, 0, "code93-with-magnification-1.2.eps");
}

private void test(Symbol symbol, double magnification, Color paper, Color ink, String expectationFile) throws IOException {
@Test
public void testCode93Orientation90() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 1.2, Color.WHITE, Color.BLACK, 90, "code93-with-orientation-90.eps");
}

@Test
public void testCode93Orientation180() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 1.2, Color.WHITE, Color.BLACK, 180, "code93-with-orientation-180.eps");
}

@Test
public void testCode93Orientation270() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 1.2, Color.WHITE, Color.BLACK, 270, "code93-with-orientation-270.eps");
}


private void test(Symbol symbol, double magnification, Color paper, Color ink, int orientation, String expectationFile) throws IOException {

ByteArrayOutputStream baos = new ByteArrayOutputStream();
PostScriptRenderer renderer = new PostScriptRenderer(baos, magnification, paper, ink);
PostScriptRenderer renderer = new PostScriptRenderer(baos, magnification, paper, ink, orientation);
renderer.render(symbol);
String actual = new String(baos.toByteArray(), StandardCharsets.UTF_8);
BufferedReader actualReader = new BufferedReader(new StringReader(actual));
Expand Down
Loading

0 comments on commit 183f42f

Please sign in to comment.