diff --git a/src/main/java/lc/captchas/FontFunCaptcha.java b/src/main/java/lc/captchas/FontFunCaptcha.java index 1007078..8dbea52 100644 --- a/src/main/java/lc/captchas/FontFunCaptcha.java +++ b/src/main/java/lc/captchas/FontFunCaptcha.java @@ -1,6 +1,5 @@ package lc.captchas; -import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; @@ -10,6 +9,7 @@ import java.util.Map; import java.util.List; import lc.captchas.interfaces.Challenge; import lc.captchas.interfaces.ChallengeProvider; +import lc.misc.PngImageWriter; import lc.misc.HelperFunctions; public class FontFunCaptcha implements ChallengeProvider { @@ -74,7 +74,7 @@ public class FontFunCaptcha implements ChallengeProvider { graphics2D.dispose(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { - ImageIO.write(img, "png", baos); + PngImageWriter.write(baos, img); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/lc/captchas/ShadowTextCaptcha.java b/src/main/java/lc/captchas/ShadowTextCaptcha.java index b510b9a..a797c50 100644 --- a/src/main/java/lc/captchas/ShadowTextCaptcha.java +++ b/src/main/java/lc/captchas/ShadowTextCaptcha.java @@ -1,6 +1,5 @@ package lc.captchas; -import javax.imageio.ImageIO; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Color; @@ -14,6 +13,7 @@ import java.util.Map; import java.util.List; import lc.misc.HelperFunctions; +import lc.misc.PngImageWriter; import lc.captchas.interfaces.Challenge; import lc.captchas.interfaces.ChallengeProvider; @@ -67,7 +67,7 @@ public class ShadowTextCaptcha implements ChallengeProvider { g2d.dispose(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { - ImageIO.write(img2, "png", baos); + PngImageWriter.write(baos, img2); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/lc/misc/HelperFunctions.java b/src/main/java/lc/misc/HelperFunctions.java index 4e77821..22b3a8a 100644 --- a/src/main/java/lc/misc/HelperFunctions.java +++ b/src/main/java/lc/misc/HelperFunctions.java @@ -7,9 +7,9 @@ public class HelperFunctions { private static Random random = new Random(); - synchronized public static void setSeed(long seed){ + public static synchronized void setSeed(long seed) { random.setSeed(seed); - } + } public static void setRenderingHints(Graphics2D g2d) { g2d.setRenderingHint( @@ -38,12 +38,11 @@ public class HelperFunctions { return stringBuilder.toString(); } - synchronized public static int randomNumber(int min, int max) { + public static synchronized int randomNumber(int min, int max) { return random.nextInt((max - min) + 1) + min; } - synchronized public static int randomNumber(int bound) { - return random.nextInt(bound); + public static synchronized int randomNumber(int bound) { + return random.nextInt(bound); } - } diff --git a/src/main/java/lc/misc/PngImageWriter.java b/src/main/java/lc/misc/PngImageWriter.java new file mode 100644 index 0000000..e13b783 --- /dev/null +++ b/src/main/java/lc/misc/PngImageWriter.java @@ -0,0 +1,68 @@ +package lc.misc; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Iterator; + +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.metadata.IIOInvalidTreeException; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.metadata.IIOMetadataNode; +import javax.imageio.stream.ImageOutputStream; +import java.awt.image.BufferedImage; + +public class PngImageWriter { + + static final int DPI = 245; + static final double INCH_2_CM = 2.54; + + public static void write(ByteArrayOutputStream boas, BufferedImage gridImage) throws IOException { + final String formatName = "png"; + for (Iterator iw = ImageIO.getImageWritersByFormatName(formatName); + iw.hasNext(); ) { + ImageWriter writer = iw.next(); + ImageWriteParam writeParam = writer.getDefaultWriteParam(); + ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB); + IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam); + if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) { + continue; + } + + setDPIMeta(metadata); + + final ImageOutputStream stream = ImageIO.createImageOutputStream(boas); + try { + writer.setOutput(stream); + writer.write(metadata, new IIOImage(gridImage, null, metadata), writeParam); + } finally { + stream.close(); + } + break; + } + } + + private static void setDPIMeta(IIOMetadata metadata) throws IIOInvalidTreeException { + + // for PNG, it's dots per millimeter + double dotsPerMilli = 1.0 * DPI / 10 / INCH_2_CM; + + IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize"); + horiz.setAttribute("value", Double.toString(dotsPerMilli)); + + IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize"); + vert.setAttribute("value", Double.toString(dotsPerMilli)); + + IIOMetadataNode dim = new IIOMetadataNode("Dimension"); + dim.appendChild(horiz); + dim.appendChild(vert); + + IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0"); + root.appendChild(dim); + + metadata.mergeTree("javax_imageio_1.0", root); + } +} diff --git a/src/main/scala/lc/captchas/DebugCaptcha.scala b/src/main/scala/lc/captchas/DebugCaptcha.scala index 787b6cf..f0add52 100644 --- a/src/main/scala/lc/captchas/DebugCaptcha.scala +++ b/src/main/scala/lc/captchas/DebugCaptcha.scala @@ -1,6 +1,5 @@ package lc.captchas -import javax.imageio.ImageIO import java.awt.Color import java.awt.Font import java.awt.font.TextLayout @@ -12,6 +11,7 @@ import java.util.List import lc.misc.HelperFunctions import lc.captchas.interfaces.Challenge import lc.captchas.interfaces.ChallengeProvider +import lc.misc.PngImageWriter /** This captcha is only for debugging purposes. It creates very simple captchas that are deliberately easy to solve with OCR engines */ class DebugCaptcha extends ChallengeProvider { @@ -53,7 +53,7 @@ class DebugCaptcha extends ChallengeProvider { graphics2D.dispose() val baos = new ByteArrayOutputStream() try { - ImageIO.write(img, "png", baos) + PngImageWriter.write(baos, img); } catch { case e: Exception => e.printStackTrace() diff --git a/src/main/scala/lc/captchas/FilterChallenge.scala b/src/main/scala/lc/captchas/FilterChallenge.scala index 8c21e78..c7f2b70 100644 --- a/src/main/scala/lc/captchas/FilterChallenge.scala +++ b/src/main/scala/lc/captchas/FilterChallenge.scala @@ -8,6 +8,8 @@ import java.awt.Color import lc.captchas.interfaces.ChallengeProvider import lc.captchas.interfaces.Challenge import java.util.{List => JavaList, Map => JavaMap} +import java.io.ByteArrayOutputStream +import lc.misc.PngImageWriter class FilterChallenge extends ChallengeProvider { def getId = "FilterChallenge" @@ -41,7 +43,15 @@ class FilterChallenge extends ChallengeProvider { var image = ImmutableImage.fromAwt(canvas) val s = scala.util.Random.nextInt(2) image = filterTypes(s).applyFilter(image) - new Challenge(image.bytes(new nio.PngWriter()), "image/png", secret) + val img = image.awt() + val baos = new ByteArrayOutputStream() + try { + PngImageWriter.write(baos, img); + } catch { + case e: Exception => + e.printStackTrace() + } + new Challenge(baos.toByteArray, "image/png", secret) } def checkAnswer(secret: String, answer: String): Boolean = { secret == answer diff --git a/src/main/scala/lc/captchas/LabelCaptcha.scala b/src/main/scala/lc/captchas/LabelCaptcha.scala index cd008e7..18048f8 100644 --- a/src/main/scala/lc/captchas/LabelCaptcha.scala +++ b/src/main/scala/lc/captchas/LabelCaptcha.scala @@ -10,6 +10,7 @@ import java.awt.Color import lc.captchas.interfaces.ChallengeProvider import lc.captchas.interfaces.Challenge import java.util.{List => JavaList, Map => JavaMap} +import lc.misc.PngImageWriter class LabelCaptcha extends ChallengeProvider { private var knownFiles = new File("known").list.toList @@ -49,7 +50,7 @@ class LabelCaptcha extends ChallengeProvider { val token = encrypt(knownImageFile + "," + unknownImageFile) val baos = new ByteArrayOutputStream() - ImageIO.write(mergedImage, "png", baos) + PngImageWriter.write(baos, mergedImage); new Challenge(baos.toByteArray(), "image/png", token) }