diff --git a/build.gradle b/build.gradle index 7db34b3..f7aa7ee 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { } group = 'com.labelzoom.api' -version = '1.0.10' +version = '1.0.11' repositories { mavenCentral() diff --git a/src/main/java/com/labelzoom/api/util/DpiUtility.java b/src/main/java/com/labelzoom/api/util/DpiUtility.java new file mode 100644 index 0000000..b07f15f --- /dev/null +++ b/src/main/java/com/labelzoom/api/util/DpiUtility.java @@ -0,0 +1,108 @@ +package com.labelzoom.api.util; + +/* Copyright (C) 2021, LabelZoom, a subsidiary of RJF Technology Solutions, Inc. + * All rights reserved. Proprietary and confidential. + * + * This file is subject to the license terms found at + * https://www.labelzoom.net/end-user-license-agreement/ + * + * The methods and techniques described herein are considered + * confidential and/or trade secrets. + * No part of this file may be copied, modified, propagated, + * or distributed except as authorized by the license. + */ + +import com.labelzoom.api.model.components.*; +import com.labelzoom.api.model.components.barcodes.ABarcode; +import com.labelzoom.api.model.components.barcodes.linear.ALinearBarcode; + +import java.util.List; + +public class DpiUtility +{ + public static final float POINTS_TO_INCHES = 1.0f / 72.0f; // 1 pt = 1/72 inch + public static final int INCHES_TO_POINTS = 72; + + private final double scalingFactor; + private final double fontScalingFactor; + + public DpiUtility(final int sourceDpi, final int targetDpi, final int sourceFontDpi, final int targetFontDpi) + { + this.scalingFactor = (double)targetDpi / (double)sourceDpi; + this.fontScalingFactor = (double)targetFontDpi / (double)sourceFontDpi; + } + + private float applyResize(final float sourceValue) + { + return (float)(((double)sourceValue) * scalingFactor); + } + + private int applyResize(final int sourceValue) + { + return Math.round(applyResize((float)sourceValue)); + } + + private float applyFontResize(final float sourceValue) + { + return (float)(((double)sourceValue) * fontScalingFactor); + } + + public CLabel resizeLabel(final CLabel label) + { + final CLabel clone = label.clone(true); + clone.setHeight(applyResize(label.getHeight())); + clone.setWidth(applyResize(label.getWidth())); + shiftElements(clone.getElements()); + return clone; + } + + private void shiftElements(final List elements) + { + for (final AComponent component : elements) + { + component.setTop(applyResize(component.getTop())); + component.setLeft(applyResize(component.getLeft())); + if (component instanceof AFontComponent) + { + final AFontComponent fontComponent = (AFontComponent)component; + fontComponent.setFontSize(applyFontResize(fontComponent.getFontSize())); + if (fontComponent instanceof CVariableText) + { + final CVariableText variableText = (CVariableText) fontComponent; + if (!variableText.isAutoSize()) + { + variableText.setWidth(applyResize(variableText.getWidth())); + variableText.setHeight(applyResize(variableText.getHeight())); + } + } + } + else if (component instanceof ABarcode) + { + if (component instanceof ALinearBarcode) + { + final ALinearBarcode linearBarcode = (ALinearBarcode)component; + linearBarcode.setHeight(applyResize(linearBarcode.getHeight())); + } + } + else if (component instanceof CLine) + { + final CLine line = (CLine)component; + line.setLength(applyResize(line.getLength())); + line.setThickness(Math.max(applyResize(line.getThickness()), 1)); // We don't ever want thickness to be less than 1, otherwise it won't show up in the design tab + } + else if (component instanceof CRectangle) + { + final CRectangle rectangle = (CRectangle) component; + rectangle.setWidth(applyResize(rectangle.getWidth())); + rectangle.setHeight(applyResize(rectangle.getHeight())); + rectangle.setThickness(applyResize(rectangle.getThickness())); + } + else if (component instanceof CImage) + { + final CImage image = (CImage)component; + image.setHorizontalScaling(applyResize(image.getHorizontalScaling())); + image.setVerticalScaling(applyResize(image.getVerticalScaling())); + } + } + } +} diff --git a/src/main/java/com/labelzoom/api/util/HSLColor.java b/src/main/java/com/labelzoom/api/util/HSLColor.java new file mode 100644 index 0000000..f8959a1 --- /dev/null +++ b/src/main/java/com/labelzoom/api/util/HSLColor.java @@ -0,0 +1,432 @@ +package com.labelzoom.api.util; + +/* Copyright (C) 2021, LabelZoom, a subsidiary of RJF Technology Solutions, Inc. + * All rights reserved. Proprietary and confidential. + * + * This file is subject to the license terms found at + * https://www.labelzoom.net/end-user-license-agreement/ + * + * The methods and techniques described herein are considered + * confidential and/or trade secrets. + * No part of this file may be copied, modified, propagated, + * or distributed except as authorized by the license. + */ + +import java.awt.*; + +/** + * The HSLColor class provides methods to manipulate HSL (Hue, Saturation + * Luminance) values to create a corresponding Color object using the RGB + * ColorSpace. + * + * The HUE is the color, the Saturation is the purity of the color (with + * respect to grey) and Luminance is the brightness of the color (with respect + * to black and white) + * + * The Hue is specified as an angel between 0 - 360 degrees where red is 0, + * green is 120 and blue is 240. In between you have the colors of the rainbow. + * Saturation is specified as a percentage between 0 - 100 where 100 is fully + * saturated and 0 approaches gray. Luminance is specified as a percentage + * between 0 - 100 where 0 is black and 100 is white. + * + * In particular the HSL color space makes it easier change the Tone or Shade + * of a color by adjusting the luminance value. + */ +public class HSLColor +{ + private Color rgb; + private float[] hsl; + private float alpha; + + /** + * Create a HSLColor object using an RGB Color object. + * + * @param rgb the RGB Color object + */ + public HSLColor(Color rgb) + { + this.rgb = rgb; + hsl = fromRGB( rgb ); + alpha = rgb.getAlpha() / 255.0f; + } + + /** + * Create a HSLColor object using individual HSL values and a default + * alpha value of 1.0. + * + * @param h is the Hue value in degrees between 0 - 360 + * @param s is the Saturation percentage between 0 - 100 + * @param l is the Lumanance percentage between 0 - 100 + */ + public HSLColor(float h, float s, float l) + { + this(h, s, l, 1.0f); + } + + /** + * Create a HSLColor object using individual HSL values. + * + * @param h the Hue value in degrees between 0 - 360 + * @param s the Saturation percentage between 0 - 100 + * @param l the Lumanance percentage between 0 - 100 + * @param alpha the alpha value between 0 - 1 + */ + public HSLColor(float h, float s, float l, float alpha) + { + hsl = new float[] {h, s, l}; + this.alpha = alpha; + rgb = toRGB(hsl, alpha); + } + + /** + * Create a HSLColor object using an an array containing the + * individual HSL values and with a default alpha value of 1. + * + * @param hsl array containing HSL values + */ + public HSLColor(float[] hsl) + { + this(hsl, 1.0f); + } + + /** + * Create a HSLColor object using an an array containing the + * individual HSL values. + * + * @param hsl array containing HSL values + * @param alpha the alpha value between 0 - 1 + */ + public HSLColor(float[] hsl, float alpha) + { + this.hsl = hsl; + this.alpha = alpha; + rgb = toRGB(hsl, alpha); + } + + /** + * Create a RGB Color object based on this HSLColor with a different + * Hue value. The degrees specified is an absolute value. + * + * @param degrees - the Hue value between 0 - 360 + * @return the RGB Color object + */ + public Color adjustHue(float degrees) + { + return toRGB(degrees, hsl[1], hsl[2], alpha); + } + + /** + * Create a RGB Color object based on this HSLColor with a different + * Luminance value. The percent specified is an absolute value. + * + * @param percent - the Luminance value between 0 - 100 + * @return the RGB Color object + */ + public Color adjustLuminance(float percent) + { + return toRGB(hsl[0], hsl[1], percent, alpha); + } + + /** + * Create a RGB Color object based on this HSLColor with a different + * Saturation value. The percent specified is an absolute value. + * + * @param percent - the Saturation value between 0 - 100 + * @return the RGB Color object + */ + public Color adjustSaturation(float percent) + { + return toRGB(hsl[0], percent, hsl[2], alpha); + } + + /** + * Create a RGB Color object based on this HSLColor with a different + * Shade. Changing the shade will return a darker color. The percent + * specified is a relative value. + * + * @param percent - the value between 0 - 100 + * @return the RGB Color object + */ + public Color adjustShade(float percent) + { + float multiplier = (100.0f - percent) / 100.0f; + float l = Math.max(0.0f, hsl[2] * multiplier); + + return toRGB(hsl[0], hsl[1], l, alpha); + } + + /** + * Create a RGB Color object based on this HSLColor with a different + * Tone. Changing the tone will return a lighter color. The percent + * specified is a relative value. + * + * @param percent - the value between 0 - 100 + * @return the RGB Color object + */ + public Color adjustTone(float percent) + { + float multiplier = (100.0f + percent) / 100.0f; + float l = Math.min(100.0f, hsl[2] * multiplier); + + return toRGB(hsl[0], hsl[1], l, alpha); + } + + /** + * Get the Alpha value. + * + * @return the Alpha value. + */ + public float getAlpha() + { + return alpha; + } + + /** + * Create a RGB Color object that is the complementary color of this + * HSLColor. This is a convenience method. The complementary color is + * determined by adding 180 degrees to the Hue value. + * @return the RGB Color object + */ + public Color getComplementary() + { + float hue = (hsl[0] + 180.0f) % 360.0f; + return toRGB(hue, hsl[1], hsl[2]); + } + + /** + * Get the Hue value. + * + * @return the Hue value. + */ + public float getHue() + { + return hsl[0]; + } + + /** + * Get the HSL values. + * + * @return the HSL values. + */ + public float[] getHSL() + { + return hsl; + } + + /** + * Get the Luminance value. + * + * @return the Luminance value. + */ + public float getLuminance() + { + return hsl[2]; + } + + /** + * Get the RGB Color object represented by this HDLColor. + * + * @return the RGB Color object. + */ + public Color getRGB() + { + return rgb; + } + + /** + * Get the Saturation value. + * + * @return the Saturation value. + */ + public float getSaturation() + { + return hsl[1]; + } + + public String toString() + { + String toString = + "HSLColor[h=" + hsl[0] + + ",s=" + hsl[1] + + ",l=" + hsl[2] + + ",alpha=" + alpha + "]"; + + return toString; + } + + /** + * Convert a RGB Color to it corresponding HSL values. + * + * @return an array containing the 3 HSL values. + */ + public static float[] fromRGB(Color color) + { + // Get RGB values in the range 0 - 1 + + float[] rgb = color.getRGBColorComponents( null ); + float r = rgb[0]; + float g = rgb[1]; + float b = rgb[2]; + + // Minimum and Maximum RGB values are used in the HSL calculations + + float min = Math.min(r, Math.min(g, b)); + float max = Math.max(r, Math.max(g, b)); + + // Calculate the Hue + + float h = 0; + + if (max == min) + h = 0; + else if (max == r) + h = ((60 * (g - b) / (max - min)) + 360) % 360; + else if (max == g) + h = (60 * (b - r) / (max - min)) + 120; + else if (max == b) + h = (60 * (r - g) / (max - min)) + 240; + + // Calculate the Luminance + + float l = (max + min) / 2; + + // Calculate the Saturation + + float s = 0; + + if (max == min) + s = 0; + else if (l <= .5f) + s = (max - min) / (max + min); + else + s = (max - min) / (2 - max - min); + + return new float[] {h, s * 100, l * 100}; + } + + /** + * Convert HSL values to a RGB Color with a default alpha value of 1. + * H (Hue) is specified as degrees in the range 0 - 360. + * S (Saturation) is specified as a percentage in the range 1 - 100. + * L (Lumanance) is specified as a percentage in the range 1 - 100. + * + * @param hsl an array containing the 3 HSL values + * + * @return the RGB Color object + */ + public static Color toRGB(float[] hsl) + { + return toRGB(hsl, 1.0f); + } + + /** + * Convert HSL values to a RGB Color. + * H (Hue) is specified as degrees in the range 0 - 360. + * S (Saturation) is specified as a percentage in the range 1 - 100. + * L (Lumanance) is specified as a percentage in the range 1 - 100. + * + * @param hsl an array containing the 3 HSL values + * @param alpha the alpha value between 0 - 1 + * + * @return the RGB Color object + */ + public static Color toRGB(float[] hsl, float alpha) + { + return toRGB(hsl[0], hsl[1], hsl[2], alpha); + } + + /** + * Convert HSL values to a RGB Color with a default alpha value of 1. + * + * @param h Hue is specified as degrees in the range 0 - 360. + * @param s Saturation is specified as a percentage in the range 1 - 100. + * @param l Lumanance is specified as a percentage in the range 1 - 100. + * + * @return the RGB Color object + */ + public static Color toRGB(float h, float s, float l) + { + return toRGB(h, s, l, 1.0f); + } + + /** + * Convert HSL values to a RGB Color. + * + * @param h Hue is specified as degrees in the range 0 - 360. + * @param s Saturation is specified as a percentage in the range 1 - 100. + * @param l Lumanance is specified as a percentage in the range 1 - 100. + * @param alpha the alpha value between 0 - 1 + * + * @return the RGB Color object + */ + public static Color toRGB(float h, float s, float l, float alpha) + { + if (s <0.0f || s > 100.0f) + { + String message = "Color parameter outside of expected range - Saturation"; + throw new IllegalArgumentException( message ); + } + + if (l <0.0f || l > 100.0f) + { + String message = "Color parameter outside of expected range - Luminance"; + throw new IllegalArgumentException( message ); + } + + if (alpha <0.0f || alpha > 1.0f) + { + String message = "Color parameter outside of expected range - Alpha"; + throw new IllegalArgumentException( message ); + } + + // Formula needs all values between 0 - 1. + + h = h % 360.0f; + h /= 360f; + s /= 100f; + l /= 100f; + + float q = 0; + + if (l < 0.5) + q = l * (1 + s); + else + q = (l + s) - (s * l); + + float p = 2 * l - q; + + float r = Math.max(0, HueToRGB(p, q, h + (1.0f / 3.0f))); + float g = Math.max(0, HueToRGB(p, q, h)); + float b = Math.max(0, HueToRGB(p, q, h - (1.0f / 3.0f))); + + r = Math.min(r, 1.0f); + g = Math.min(g, 1.0f); + b = Math.min(b, 1.0f); + + return new Color(r, g, b, alpha); + } + + private static float HueToRGB(float p, float q, float h) + { + if (h < 0) h += 1; + + if (h > 1 ) h -= 1; + + if (6 * h < 1) + { + return p + ((q - p) * 6 * h); + } + + if (2 * h < 1 ) + { + return q; + } + + if (3 * h < 2) + { + return p + ( (q - p) * 6 * ((2.0f / 3.0f) - h) ); + } + + return p; + } +}