diff --git a/src/main/java/tech/nevets/vcardgen/Main.java b/src/main/java/tech/nevets/vcardgen/Main.java index bc46384..b963628 100644 --- a/src/main/java/tech/nevets/vcardgen/Main.java +++ b/src/main/java/tech/nevets/vcardgen/Main.java @@ -12,25 +12,36 @@ public class Main { Location.loadLocations(); port(8080); - get("/heartbeat", (req, res) -> { - res.status(200); - res.type("application/json"); - res.header("Access-Control-Allow-Origin", "*"); - return "{ \"up\":true }"; - }); + path("/backend", () -> { + before("/*"); - get("/data/locations", (req, res) -> { - res.status(200); - res.type("application/json"); - res.header("Access-Control-Allow-Origin", "*"); - return new FileInputStream("locations.json"); - }); + get("/heartbeat", (req, res) -> { + res.status(200); + res.type("application/json"); + res.header("Access-Control-Allow-Origin", "*"); + return "{ \"up\":true }"; + }); - post("/", (req, res) -> { - res.type("image/png"); - res.header("Access-Control-Allow-Origin", "*"); - JsonObject data = new Gson().fromJson(req.body(), JsonObject.class); - return new VCard(data).toByteArray(); + get("/data/locations", (req, res) -> { + res.status(200); + res.type("application/json"); + res.header("Access-Control-Allow-Origin", "*"); + return new FileInputStream("locations.json"); + }); + + post("/generate", (req, res) -> { + res.type("image/png"); + res.header("Access-Control-Allow-Origin", "*"); + JsonObject data = new Gson().fromJson(req.body(), JsonObject.class); + return new VCard(data).toByteArray(); + }); + + post("/edit", (req, res) -> { + res.status(200); + res.type("application/json"); + res.header("Access-Control-Allow-Origin", "*"); + return VCard.getDataFromVCard(req.bodyAsBytes()); + }); }); } } diff --git a/src/main/java/tech/nevets/vcardgen/VCard.java b/src/main/java/tech/nevets/vcardgen/VCard.java index 9bf3185..4db65d2 100644 --- a/src/main/java/tech/nevets/vcardgen/VCard.java +++ b/src/main/java/tech/nevets/vcardgen/VCard.java @@ -1,12 +1,20 @@ package tech.nevets.vcardgen; +import com.google.gson.Gson; import com.google.gson.JsonObject; -import javax.imageio.ImageIO; +import javax.imageio.*; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.metadata.IIOMetadataNode; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.ImageOutputStream; import java.awt.*; import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; public class VCard { private static final Color WHITE = new Color(255, 255, 255); @@ -20,7 +28,11 @@ public class VCard { private static final Font ARIAL40I = new Font("Arial", Font.ITALIC, 40); private static final Font ARIAL38I = new Font("Arial", Font.ITALIC, 38); + private static final String[] KEYS = {"Name", "Title", "Email", "Location", "Address", "SchoolNumber", "Extension", "DirectNumber", "CellNumber"}; + + private BufferedImage rawImage = new BufferedImage(1080, 602, BufferedImage.TYPE_INT_ARGB); private BufferedImage workingImage = new BufferedImage(1080, 602, BufferedImage.TYPE_INT_ARGB); + private IIOImage finalImage; private final String[] data = new String[9]; private final boolean[] flags = new boolean[3]; // 0 - Long Address, 1 - Has Direct Number, 2 - Has Cell Phone @@ -34,7 +46,7 @@ public class VCard { Location location = Location.getLocation(json.get("locationId").getAsString()); this.data[3] = location.getName(); //locationName this.data[4] = location.getAddress(); //address - this.data[5] = location.getName(); //schoolNumber + this.data[5] = location.getNumber(); //schoolNumber background = location.getBackground(); this.data[6] = json.get("extension").getAsString(); //extension this.data[7] = json.get("directNumber").getAsString(); //directNumber @@ -44,6 +56,13 @@ public class VCard { size = json.get("size").getAsInt(); renderImage(); + resizeImage(); + try { + addMetadata(); + } catch (IOException e) { + System.out.println("Error adding metadata"); + e.printStackTrace(); + } } public VCard(String name, String title, String email, String locationId, String extension, String directNumber, String cellNumber, int size) { @@ -63,6 +82,13 @@ public class VCard { this.size = size; renderImage(); + resizeImage(); + try { + addMetadata(); + } catch (IOException e) { + System.out.println("Error adding metadata"); + e.printStackTrace(); + } } private void renderImage() { @@ -71,15 +97,12 @@ public class VCard { g.drawImage(background, 0, 0, null); - FontMetrics fm = g.getFontMetrics(ARIAL65); - g.setColor(WHITE); - if (fm.stringWidth(data[0]) <= 969) g.setFont(ARIAL65); + if (fitsDimensions(data[0], ARIAL65, 969)) g.setFont(ARIAL65); else g.setFont(ARIAL55); g.drawString(data[0], 85, 112); - fm = g.getFontMetrics(ARIAL45I); - if (fm.stringWidth(data[1]) <= 970) g.setFont(ARIAL45I); + if (fitsDimensions(data[1], ARIAL45I, 970)) g.setFont(ARIAL45I); else g.setFont(ARIAL40I); g.drawString(data[1], 89, 176); @@ -88,9 +111,7 @@ public class VCard { doubleNumOffset = 40; } - int longAddrOffset = 0; - fm = g.getFontMetrics(ARIAL38I); - if (fm.stringWidth(data[4]) > 694) longAddrOffset = 40; + int longAddrOffset = fitsDimensions(data[4], ARIAL38I, 694) ? 0 : 40; g.setFont(ARIAL45); g.drawString(data[2], 62, 380 - doubleNumOffset - longAddrOffset); @@ -99,14 +120,14 @@ public class VCard { g.setFont(ARIAL44I); g.drawString(data[3], 59, 447 - doubleNumOffset - longAddrOffset); - if (fm.stringWidth(data[4]) >= 694) { + if (!fitsDimensions(data[4], ARIAL38I, 694)) { StringBuilder addrLineOne = new StringBuilder(); StringBuilder addrLineTwo = new StringBuilder(); String[] splitAddr = data[4].split(","); int i = 0; for (int width = 0; width < 694; i++) { - int splitSize = fm.stringWidth(splitAddr[i]); + int splitSize = g.getFontMetrics().stringWidth(splitAddr[i]); if ((width + splitSize) < 694) { width += splitSize; addrLineOne.append(splitAddr[i]); @@ -132,15 +153,13 @@ public class VCard { String number; int numY = flags[2] ? 496 : 540; - if (!flags[1]) number = "W: " + data[5] + " x" + data[6]; else number = "W: " + data[7]; - g.drawString(number, 59, numY); - if (flags[2]) g.drawString("C: " + data[8], 59, 540); g.dispose(); + rawImage = workingImage; } public void resizeImage() { @@ -151,7 +170,7 @@ public class VCard { BufferedImage resizedImage; switch (size) { case 0 -> resizedImage = resizeImage(300, 167, false); - case 1 -> resizedImage = workingImage; + case 1 -> resizedImage = rawImage; default -> resizedImage = background; } workingImage = resizedImage; @@ -162,43 +181,63 @@ public class VCard { Graphics2D g = resizedImage.createGraphics(); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); if (antiAlias) g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.drawImage(workingImage, 0, 0, width, height, null); + g.drawImage(rawImage, 0, 0, width, height, null); g.dispose(); return resizedImage; } - public byte[] toByteArray() { - ByteArrayOutputStream baos; - try { - baos = new ByteArrayOutputStream(); - ImageIO.write(workingImage, "png", baos); - } catch (IOException e) { - e.printStackTrace(); - return new byte[0]; - } + public void addMetadata() throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(workingImage, "png", baos); + ImageReader reader = ImageIO.getImageReadersByMIMEType("image/png").next(); + reader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(baos.toByteArray()))); + finalImage = reader.readAll(0, null); + + IIOMetadataNode text = new IIOMetadataNode("tEXt"); + for (int i = 0; i < KEYS.length; i++) { + IIOMetadataNode textEntry = new IIOMetadataNode("tEXtEntry"); + textEntry.setAttribute("keyword", KEYS[i]); + textEntry.setAttribute("value", data[i]); + text.appendChild(textEntry); + } + IIOMetadataNode root = new IIOMetadataNode("javax_imageio_png_1.0"); + root.appendChild(text); + + finalImage.getMetadata().mergeTree("javax_imageio_png_1.0", root); + } + + public byte[] toByteArray() throws IOException { + ImageWriter writer = ImageIO.getImageWritersByMIMEType("image/png").next(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageOutputStream ios = ImageIO.createImageOutputStream(baos); + writer.setOutput(ios); + writer.write(finalImage); return baos.toByteArray(); } - private boolean[] bitFlags(int bits) { - boolean[] flags = new boolean[bits]; - for (int i = 0; i < flags.length; i++) { - flags[i] = ((bits) & (1 << ((flags.length - i) - 1))) != 0; - } - return flags; - } - - private int bitFlagsFromFlags(boolean[] flags) { - StringBuilder sb = new StringBuilder(); - for (boolean b : flags) { - sb.append(b ? "1" : "0"); - } - return Integer.parseInt(sb.toString()); - } - + private static final Canvas C = new Canvas(); private static boolean fitsDimensions(String text, Font font, int maxLength) { - Canvas c = new Canvas(); - FontMetrics fm = c.getFontMetrics(font); - return fm.stringWidth(text) < maxLength; + return C.getFontMetrics(font).stringWidth(text) < maxLength; + } + + public static String getDataFromVCard(byte[] rawImage) throws IOException { + ImageReader reader = ImageIO.getImageReadersByMIMEType("image/png").next(); + reader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(rawImage))); + + IIOMetadata metadata = reader.getImageMetadata(0); + IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree("javax_imageio_png_1.0"); + IIOMetadataNode text = (IIOMetadataNode) root.getElementsByTagName("tEXt").item(0); + int numTextEntries = text.getLength(); + + Map keyValueMap = new HashMap<>(); + for (int i = 0; i < numTextEntries; i++) { + IIOMetadataNode textEntry = (IIOMetadataNode) text.item(i); + String key = textEntry.getAttribute("keyword"); + String value = textEntry.getAttribute("value"); + keyValueMap.put(key, value); + } + + return new Gson().toJson(keyValueMap); } }