001/* 002 * AbstractNote -- Partial implementation of a note that holds a String located at a point in nD space. 003 * 004 * Copyright (C) 2014-2017, Joseph A. Huwaldt. 005 * All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public License 018 * along with this program; if not, write to the Free Software 019 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 020 * Or visit: http://www.gnu.org/licenses/lgpl.html 021 */ 022package geomss.geom; 023 024import java.awt.Font; 025import java.awt.FontFormatException; 026import java.awt.GraphicsEnvironment; 027import java.io.IOException; 028import java.io.InputStream; 029import java.net.URL; 030import static java.util.Objects.nonNull; 031import javolution.text.Text; 032import javolution.text.TextBuilder; 033import javolution.text.TypeFormat; 034 035/** 036 * Partial implementation of a textual note located at a point in nD space. 037 * 038 * <p> Modified by: Joseph A. Huwaldt </p> 039 * 040 * @author Joseph A. Huwaldt, Date: February 5, 2014 041 * @version January 30, 2017 042 * 043 * @param <T> The sub-type of this AbstractNote. 044 */ 045@SuppressWarnings({"serial", "CloneableImplementsClone"}) 046public abstract class AbstractNote<T extends AbstractNote> extends AbstractGeomElement<T> { 047 048 /** 049 * The default font used for displaying note objects. 050 */ 051 public static final Font DEFAULT_FONT; 052 053 /** 054 * The String encoding of the default font used for displaying note objects. 055 */ 056 protected static final String DEFAULT_FONT_CODE; 057 058 static { 059 // Load and register the list of fonts that should be loaded at run-time. 060 GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 061 String[] availFontNames = ge.getAvailableFontFamilyNames(); 062 String[] fontNames = RESOURCES.getString("appFontNames").split(","); 063 String[] fontPaths = RESOURCES.getString("appFontPaths").split(","); 064 int numFonts = fontNames.length; 065 for (int fontIdx = 0; fontIdx < numFonts; ++fontIdx) { 066 String name = fontNames[fontIdx]; 067 String path = fontPaths[fontIdx]; 068 069 // Is the font already provided by the system? 070 boolean found = false; 071 for (String availName : availFontNames) { 072 if (availName.equals(name)) { 073 found = true; 074 break; 075 } 076 } 077 if (!found) { 078 // Load the font from the program resources and register it. 079 try { 080 loadAndRegisterFont(path, Font.TRUETYPE_FONT); 081 } catch (Exception e) { 082 // Shouldn't happen after development. 083 e.printStackTrace(); 084 } 085 } 086 } // Next fontIdx 087 088 // Get the default font name, style and size. 089 String defName = RESOURCES.getString("defFontName"); 090 int defStyle = Font.BOLD; 091 int defSize = 12; 092 try { 093 defStyle = TypeFormat.parseInt(RESOURCES.getString("defFontStyle")); 094 } catch (Exception ignore) { /* Ignore, keep default */ } 095 try { 096 defSize = TypeFormat.parseInt(RESOURCES.getString("defFontSize")); 097 } catch (Exception ignore) { /* Ignore, keep default */ } 098 099 // Save the default font for use elsewhere. 100 DEFAULT_FONT = new Font(defName, defStyle, defSize); 101 102 // Store an encoding of the font name, style & size for use elsewhere. 103 DEFAULT_FONT_CODE = encodeFont(defName, defStyle, defSize); 104 } 105 106 /** 107 * Load the font at the specified path in the program's resources and register it with 108 * the Java font management system. 109 * 110 * @param fontPath The path to the font to be loaded in the program resources. 111 * @param fontType The type of the font: Font.TRUETYPE_FONT or Font.TYPE1_FONT. 112 * @throws FontFormatException if there is a problem with the font's format. 113 * @throws IOException 114 */ 115 @SuppressWarnings("null") 116 private static void loadAndRegisterFont(String fontPath, int fontType) 117 throws FontFormatException, IOException { 118 119 // Get a URL to the font file in the program's resources. 120 URL fontURL = ClassLoader.getSystemResource(fontPath); 121 122 Font baseFnt; 123 try (InputStream is = fontURL.openStream()) { 124 125 // Create a new font from the file as a 1-point, plain font. 126 baseFnt = Font.createFont(fontType, is); 127 128 } 129 130 // Register the font with the system. 131 GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 132 ge.registerFont(baseFnt); 133 } 134 135 /** 136 * Return the code string for the given font. For example, 12pt Arial with fontStyle 137 * == 1 is returned as: Arial-BOLD-12 138 * 139 * @param fontName The name of the font family. 140 * @param fontStyle The font style code such as Font.PLAIN. 141 * @param fontSize The point size of the font. 142 * @return The string encoding of the font. 143 */ 144 protected static String encodeFont(String fontName, int fontStyle, int fontSize) { 145 146 // Convert the font style code into a style String. 147 String styleStr = "PLAIN"; 148 switch (fontStyle) { 149 case Font.BOLD: 150 styleStr = "BOLD"; 151 break; 152 case Font.BOLD + Font.ITALIC: 153 styleStr = "BOLDITALIC"; 154 break; 155 case Font.ITALIC: 156 styleStr = "ITALIC"; 157 break; 158 default: 159 break; 160 } 161 162 // Put it all together. 163 StringBuilder buf = new StringBuilder(fontName); 164 buf.append("-"); 165 buf.append(styleStr); 166 buf.append("-"); 167 buf.append(fontSize); 168 return buf.toString(); 169 } 170 171 /** 172 * Return the text string associated with this note object. 173 * 174 * @return The text string associated with this note. 175 */ 176 public abstract String getNote(); 177 178 /** 179 * Return the geometric location of this note in space. 180 * 181 * @return The geometric location of this note in space. 182 */ 183 public abstract Point getLocation(); 184 185 /** 186 * Return the font used to display this note. 187 * 188 * @return The font used to display this note. 189 */ 190 public abstract Font getFont(); 191 192 /** 193 * Return a new note object identical to this one, but with the specified font. 194 * 195 * @param font The new font to change this note to display in. May not be null. 196 * @return A new note, identical tot his one, but using the specified font. 197 */ 198 public abstract T changeFont(Font font); 199 200 /** 201 * Return a new note object identical to this one, but with the specified location in 202 * model space. 203 * 204 * @param location The new location of this note. May not be null. 205 * @return A new note, identical tot his one, but using the specified location. 206 */ 207 public abstract T changeLocation(GeomPoint location); 208 209 /** 210 * Return the length of the text string associated with this geometry object. 211 * 212 * @return The length, in characters, of the text string associated with this note. 213 */ 214 public int length() { 215 return toString().length(); 216 } 217 218 /** 219 * Returns the number of parametric dimensions of the geometry element. This 220 * implementation always returns 0 as a GeneralNote is not parametric. 221 * 222 * @return Always returns 0. 223 */ 224 @Override 225 public int getParDimension() { 226 return 0; 227 } 228 229 /** 230 * Returns the text representation of this geometry element that consists of the text 231 * string followed by the coordinate position. For example: 232 * <pre> 233 * {aNote = {"A text string.",{10 ft, -3 ft, 4.56 ft}}} 234 * </pre> 235 * If there is no name, then the output looks like this: 236 * <pre> 237 * {"A text string.",{10 ft, -3 ft, 4.56 ft}} 238 * </pre> 239 * 240 * @return the text representation of this geometry element. 241 */ 242 @Override 243 public Text toText() { 244 TextBuilder tmp = TextBuilder.newInstance(); 245 tmp.append('{'); 246 String nameStr = getName(); 247 boolean hasName = nonNull(nameStr); 248 if (hasName) { 249 tmp.append(nameStr); 250 tmp.append(" = {"); 251 } 252 tmp.append("\""); 253 tmp.append(toString()); 254 tmp.append("\","); 255 tmp.append(getLocation().toText()); 256 if (hasName) 257 tmp.append('}'); 258 tmp.append('}'); 259 Text txt = tmp.toText(); 260 TextBuilder.recycle(tmp); 261 return txt; 262 } 263 264}