/* * Copyright 2014-15 Skynav, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY SKYNAV, INC. AND ITS CONTRIBUTORS “AS IS” AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL SKYNAV, INC. OR ITS CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.skynav.ttpe.fonts; import java.io.File; import java.io.FileFilter; import java.util.BitSet; import java.util.List; import java.util.Map; import java.util.Set; import com.skynav.ttpe.geometry.Axis; import com.skynav.ttpe.geometry.Extent; import com.skynav.ttv.util.Reporter; public class FontCache { private File fontSpecificationDirectory; private List<File> fontSpecificationFiles; private boolean loaded; private List<FontSpecification> fontSpecifications; private Map<FontKey,Font> instances; private Map<String,FontState> loadedState; private Reporter reporter; public FontCache(File fontSpecificationDirectory, List<File> fontSpecificationFiles, Reporter reporter) { this.fontSpecificationDirectory = fontSpecificationDirectory; if (fontSpecificationFiles != null) this.fontSpecificationFiles = new java.util.ArrayList<File>(fontSpecificationFiles); this.instances = new java.util.HashMap<FontKey,Font>(); this.loadedState = new java.util.HashMap<String,FontState>(); this.reporter = reporter; } public Font getDefaultFont(Axis axis, Extent size) { return get(new FontKey((axis == Axis.VERTICAL) ? FontKey.DEFAULT_VERTICAL : FontKey.DEFAULT_HORIZONTAL, size)); } public Font mapFont(List<String> families, FontStyle style, FontWeight weight, String language, Axis axis, Extent size, Set<FontFeature> features) { FontSpecification fs; if ((fs = findExactMatch(families, style, weight, language)) == null) fs = findBestMatch(families, style, weight, language, axis, size); if (fs != null) return get(new FontKey(fs.family, fs.style, fs.weight, fs.language, axis, size, features)); return null; } public Font get(FontKey key) { Font f = instances.get(key); if (f == null) { f = create(key); if (f != null) put(f); } return f; } public Font getScaledFont(Font font, double scale) { return get(font.getKey().getScaled(scale)); } public void put(Font f) { instances.put(f.getKey(), f); } public FontCache maybeLoad() { if (!loaded) load(); return this; } public void clear() { instances.clear(); loadedState.clear(); } public FontState getLoadedState(String source, BitSet forcePath, Reporter reporter) { FontState fs; if ((fs = loadedState.get(source)) == null) fs = createLoadedState(source, forcePath, reporter); return fs; } private FontSpecification findExactMatch(List<String> families, FontStyle style, FontWeight weight, String language) { for (String family : families) { if (fontSpecifications != null) { for (FontSpecification fs : fontSpecifications) { if (fs.family.compareToIgnoreCase(family) == 0) { if (fs.style != style) continue; if (fs.weight != weight) continue; if ((language != null) && (fs.language != null) && !fs.language.equals(language)) continue; return fs; } } } } return null; } private FontSpecification findBestMatch(List<String> families, FontStyle style, FontWeight weight, String language, Axis axis, Extent size) { if (fontSpecifications != null) { List<FontSpecification> matchesFamily = new java.util.ArrayList<FontSpecification>(); for (FontSpecification fs : fontSpecifications) { for (String family : families) { if (fs.family.compareToIgnoreCase(family) == 0) matchesFamily.add(fs); } } List<FontSpecification> matchesLanguage = new java.util.ArrayList<FontSpecification>(); for (FontSpecification fs : matchesFamily) { if ((language == null) || language.isEmpty()) matchesLanguage.add(fs); else if (fs.language.compareToIgnoreCase(language) == 0) matchesLanguage.add(fs); } List<FontSpecification> matchesStyle = new java.util.ArrayList<FontSpecification>(); for (FontSpecification fs : matchesLanguage) { if (style == null) matchesStyle.add(fs); else if (fs.style == style) matchesStyle.add(fs); } List<FontSpecification> matchesWeight = new java.util.ArrayList<FontSpecification>(); for (FontSpecification fs : matchesStyle) { if (weight == null) matchesWeight.add(fs); else if (fs.weight == weight) matchesWeight.add(fs); } if (!matchesWeight.isEmpty()) return matchesWeight.get(0); if (!matchesStyle.isEmpty()) return matchesStyle.get(0); if (!matchesLanguage.isEmpty()) return matchesLanguage.get(0); if (!matchesFamily.isEmpty()) return matchesFamily.get(0); } Font f = getDefaultFont(axis, size); return (f != null) ? f.getSpecification() : null; } private Font create(FontKey key) { FontSpecification fs = findSpecification(key); if (fs != null) return new Font(this, key, fs.source, fs.forcePath, reporter); else return null; } private FontSpecification findSpecification(FontKey key) { if (fontSpecifications != null) { for (FontSpecification fs : fontSpecifications) { if (fs.matches(key)) return fs; } } return null; } private void load() { if (fontSpecificationDirectory != null) { assert fontSpecificationDirectory.exists(); assert fontSpecificationDirectory.isDirectory(); FileFilter filter = new FileFilter() { public boolean accept(File f) { return maybeFontSpecificationFile(f); } }; File[] files = fontSpecificationDirectory.listFiles(filter); if (files != null) { for (File f : files) { if (fontSpecificationFiles == null) fontSpecificationFiles = new java.util.ArrayList<File>(); fontSpecificationFiles.add(f); } } } if (fontSpecificationFiles != null) { fontSpecifications = FontLoader.load(fontSpecificationFiles, reporter); } if ((fontSpecifications == null) || fontSpecifications.isEmpty()) reporter.logWarning(reporter.message("*KEY*", "No font specifications! No text will be rendered.")); loaded = true; } private static boolean maybeFontSpecificationFile(File f) { assert f != null; String n = f.getName(); assert n != null; return n.endsWith(".xml"); } public FontState createLoadedState(String source, BitSet forcePath, Reporter reporter) { FontState fs = new FontState(source, forcePath, reporter); assert loadedState != null; assert !loadedState.containsKey(source); loadedState.put(source, fs); return fs; } }