SignatureCardGenerator/src/main/java/tech/nevets/vcardgen/Card.java
2023-06-05 16:15:12 -04:00

381 lines
15 KiB
Java

package tech.nevets.vcardgen;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import javax.imageio.*;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
public class Card {
public static final Map<String, Card> CARD_SESSIONS = new HashMap<>();
// ---------------- COLORS ---------------- //
private static final Color WHITE = new Color(255, 255, 255);
private static final Color GREEN = new Color(101, 142, 61);
// ---------------- FONTS ---------------- //
private static final Font ARIAL65 = new Font("Arial", Font.PLAIN, 65);
private static final Font ARIAL55 = new Font("Arial", Font.PLAIN, 55);
private static final Font ARIAL45 = new Font("Arial", Font.PLAIN, 45);
private static final Font ARIAL45I = new Font("Arial", Font.ITALIC, 45);
private static final Font ARIAL44I = new Font("Arial", Font.ITALIC, 44);
private static final Font ARIAL40I = new Font("Arial", Font.ITALIC, 40);
private static final Font ARIAL38I = new Font("Arial", Font.ITALIC, 38);
// ---------------- LAYERS ---------------- //
private BufferedImage background;
private BufferedImage nameLayer = new BufferedImage(975, 90, BufferedImage.TYPE_INT_ARGB);
private BufferedImage titleLayer = new BufferedImage(975, 55, BufferedImage.TYPE_INT_ARGB);
private BufferedImage emailLayer = new BufferedImage(1000, 55, BufferedImage.TYPE_INT_ARGB);
private BufferedImage locationLayer = new BufferedImage(700, 134, BufferedImage.TYPE_INT_ARGB);
private BufferedImage phoneNumbersLayer = new BufferedImage(700, 90, BufferedImage.TYPE_INT_ARGB);
// ---------------- Working Images ---------------- //
private Graphics2D graphics;
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;
// ---------------- DATA ---------------- //
public String id;
private String name;
private String title;
private String email;
private Location location;
private String extension;
private String directNumber;
private String cellNumber;
private int size;
private boolean hasLongAddress;
private boolean hasDirectNumber;
private boolean hasCellNumber;
/**
* Constructs an empty card.<br>
* Calls:<br>{@link tech.nevets.vcardgen.Card#Card(String, String, String, String, String, String, String, int)} with empty strings and full size image
*/
public Card() {
this("", "", "", "", "", "", "", 1);
}
/**
* Constructor that fills out entire Card details.<br>
* Calls:<br>{@link tech.nevets.vcardgen.Card#Card(String, String, String, String, String, String, String, int)}
* @param json Json object from frontend
*/
public Card(JsonObject json) {
this(
json.get("name").getAsString(), //name
json.get("title").getAsString(), //title
json.get("email").getAsString(), //email
json.get("locationId").getAsString(), //locationId
json.get("extension").getAsString(), //extension
json.get("directNumber").getAsString(), //directNumber
json.get("cellNumber").getAsString(), //cellNumber
json.get("size").getAsInt() //size
);
}
/**
* Constructor that fills out entire Card details.
* @param name User's Name
* @param title User's Title
* @param email User's Email
* @param locationId User's Location ID
* @param extension User's Extension (can be empty <b>if</b> {@link tech.nevets.vcardgen.Card#directNumber} is populated)
* @param directNumber User's Direct Number (can be empty <b>if</b> {@link tech.nevets.vcardgen.Card#extension} is populated)
* @param cellNumber User's Cell Phone Number (can be empty)
* @param size Size to set the Card to (0 for Outlook 2016 size, 1 for full size)
*/
public Card(String name, String title, String email, String locationId, String extension, String directNumber, String cellNumber, int size) {
this.id = getUniqueId();
this.name = name;
this.title = title;
this.email = email;
Location location = Location.getLocation(locationId);
background = location.getBackground();
this.extension = extension;
this.directNumber = directNumber;
this.cellNumber = cellNumber;
this.hasDirectNumber = this.directNumber.length() > 0;
this.hasCellNumber = this.cellNumber.length() > 0;
this.size = size;
CARD_SESSIONS.put(id, this);
}
public void setName(String name) {
this.name = name;
}
public void setTitle(String title) {
this.title = title;
}
public void setEmail(String email) {
this.email = email;
}
public void setLocation(Location location) {
this.location = location;
}
public void setExtension(String extension) {
this.extension = extension;
hasDirectNumber = false;
this.directNumber = "";
}
public void setDirectNumber(String directNumber) {
this.directNumber = directNumber;
hasDirectNumber = directNumber.length() > 0;
this.extension = "";
}
public void setCellNumber(String cellNumber) {
this.cellNumber = cellNumber;
hasCellNumber = cellNumber.length() > 0;
}
public BufferedImage getBackground() {
return background;
}
private BufferedImage renderLayer(BufferedImage layer, String content, Color textColor, Font font) {
return renderLayer(layer, content, textColor, font, 0, 0);
}
private BufferedImage renderLayer(BufferedImage layer, String content, Color textColor, Font font, int x, int y) {
graphics = layer.createGraphics();
setAntiAlias(graphics);
graphics.setColor(textColor);
graphics.drawString(content, x, y);
graphics.dispose();
return layer;
}
private BufferedImage renderResizableLayer(BufferedImage layer, String content, Color textColor, int maxLength, Font defaultFont, Font smallFont) {
graphics = layer.createGraphics();
setAntiAlias(graphics);
graphics.setColor(textColor);
if (fitsDimensions(content, defaultFont, maxLength)) graphics.setFont(defaultFont);
else graphics.setFont(smallFont);
graphics.drawString(content, 0, 0);
graphics.dispose();
return layer;
}
public BufferedImage renderNameLayer() {
return renderResizableLayer(nameLayer, name, WHITE, 970, ARIAL65, ARIAL55);
}
public BufferedImage renderTitleLayer() {
return renderResizableLayer(titleLayer, title, WHITE, 970, ARIAL45I, ARIAL40I);
}
public BufferedImage renderEmailLayer() {
return renderLayer(emailLayer, email, WHITE, ARIAL45);
}
public BufferedImage renderLocationLayer() {
StringBuilder sb = new StringBuilder();
String address = location.getAddress();
String[] splitAddr = address.split(",");
if (fitsDimensions(address, ARIAL38I, 700)) {
sb.append(address);
} else {
int lineOneLength = 0;
int newLineIndex = 0;
for (String addrPart : splitAddr) {
if (fitsDimensions(addrPart, ARIAL38I, (700 - lineOneLength))) {
sb.append(addrPart).append(",");
lineOneLength += stringWidth(addrPart, ARIAL38I);
newLineIndex += addrPart.length();
} else {
sb.append(addrPart).append(",");
}
}
sb.delete(newLineIndex - 2, newLineIndex);
sb.insert(newLineIndex - 2, "\n");
sb.deleteCharAt(sb.length() - 1);
}
renderLayer(locationLayer, location.getName(), GREEN, ARIAL44I);
return renderLayer(locationLayer, sb.toString(), GREEN, ARIAL38I, 0, 44);
}
public BufferedImage renderPhoneNumbersLayer() {
return null;
}
private void renderImage() {
graphics = rawImage.createGraphics();
setAntiAlias(graphics);
graphics.drawImage(background, 0, 0, null);
graphics.drawImage(renderNameLayer(), 85, 112, null);
graphics.drawImage(renderTitleLayer(), 89, 176, null);
int doubleNumOffset = hasDirectNumber ? 40 : 0;
int longAddrOffset = fitsDimensions(location.getAddress(), ARIAL38I, 700) ? 0 : 40;
graphics.drawImage(renderEmailLayer(), 62, (380 - doubleNumOffset - longAddrOffset), null);
graphics.setColor(GREEN);
graphics.setFont(ARIAL44I);
graphics.drawString(location.getName(), 59, 447 - doubleNumOffset - longAddrOffset);
if (!fitsDimensions(location.getAddress(), ARIAL38I, 694)) {
StringBuilder addrLineOne = new StringBuilder();
StringBuilder addrLineTwo = new StringBuilder();
String[] splitAddr = location.getAddress().split(",");
int i = 0;
for (int width = 0; width < 694; i++) {
int splitSize = graphics.getFontMetrics().stringWidth(splitAddr[i]);
if ((width + splitSize) < 694) {
width += splitSize;
addrLineOne.append(splitAddr[i]);
addrLineOne.append(",");
} else {
addrLineOne.deleteCharAt(addrLineOne.length() - 1);
break;
}
}
for (; i < splitAddr.length; i++) {
addrLineTwo.append(splitAddr[i]);
addrLineTwo.append(",");
}
addrLineTwo.deleteCharAt(addrLineTwo.length() - 1);
graphics.setFont(ARIAL38I);
graphics.drawString(addrLineOne.toString().trim(), 59, 491 - doubleNumOffset - longAddrOffset);
graphics.drawString(addrLineTwo.toString().trim(), 59, 491 - doubleNumOffset);
} else {
graphics.drawString(location.getAddress(), 59, 491 - doubleNumOffset);
}
String number;
int numY = hasDirectNumber ? 496 : 540;
if (!hasDirectNumber) number = "W: " + location.getNumber() + " x" + extension;
else number = "W: " + directNumber;
graphics.drawString(number, 59, numY);
if (hasCellNumber) graphics.drawString("C: " + cellNumber, 59, 540);
graphics.dispose();
rawImage = workingImage;
}
public void resizeImage() {
resizeImage(this.size);
}
public void resizeImage(int size) {
BufferedImage resizedImage;
switch (size) {
case 0 -> resizedImage = resizeImage(300, 167, false);
case 1 -> resizedImage = rawImage;
default -> resizedImage = background;
}
workingImage = resizedImage;
}
public BufferedImage resizeImage(int width, int height, boolean antiAlias) {
BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
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(rawImage, 0, 0, width, height, null);
g.dispose();
return resizedImage;
}
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 void setAntiAlias(Graphics2D g) {
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
private static final Canvas C = new Canvas();
private static boolean fitsDimensions(String text, Font font, int maxLength) {
return C.getFontMetrics(font).stringWidth(text) < maxLength;
}
private static int stringWidth(String text, Font font) {
return C.getFontMetrics(font).stringWidth(text);
}
public static String getDataFromVCard(InputStream rawImage) throws IOException {
ImageReader reader = ImageIO.getImageReadersByMIMEType("image/png").next();
reader.setInput(ImageIO.createImageInputStream(rawImage));
IIOMetadata metadata = reader.getImageMetadata(0);
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree("javax_imageio_png_1.0");
IIOMetadataNode text = (IIOMetadataNode) root.getElementsByTagName("tEXt").item(0);
if (text == null) {
return "$null";
}
int numTextEntries = text.getLength();
Map<String, String> 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");
if (key.equals("Address") || key.equals("SchoolNumber")) {
continue;
}
if (key.equals("Location")) {
value = Location.getLocationId(value);
}
keyValueMap.put(key, value);
}
return new Gson().toJson(keyValueMap);
}
private static String getUniqueId() {
String uuid = UUID.randomUUID().toString().split("-")[4];
while (true) {
String finalUuid = uuid;
if (CARD_SESSIONS.entrySet().stream().noneMatch(card -> card.getKey().equals(finalUuid))) {
return uuid;
}
uuid = UUID.randomUUID().toString().split("-")[4];
}
}
}