/* * Copyright (C) 2010-2016 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. */ package com.jpexs.decompiler.flash.tags.base; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.configuration.SwfSpecificConfiguration; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; import com.jpexs.decompiler.flash.helpers.FontHelper; import com.jpexs.decompiler.flash.tags.DefineFontNameTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.GLYPHENTRY; import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.SHAPE; import com.jpexs.decompiler.flash.types.TEXTRECORD; import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; import com.jpexs.helpers.ByteArrayRange; import com.jpexs.helpers.SerializableImage; import java.awt.Color; import java.awt.Font; import java.awt.Rectangle; import java.awt.Shape; import java.awt.font.FontRenderContext; import java.awt.font.GlyphMetrics; import java.awt.font.GlyphVector; import java.awt.geom.Area; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * * @author JPEXS */ public abstract class FontTag extends DrawableTag implements AloneTag { public static final int PREVIEWSIZE = 500; public FontTag(SWF swf, int id, String name, ByteArrayRange data) { super(swf, id, name, data); } public abstract List<SHAPE> getGlyphShapeTable(); public abstract void addCharacter(char character, Font font); public abstract boolean removeCharacter(char character); public abstract void setAdvanceValues(Font font); public abstract char glyphToChar(int glyphIndex); public abstract int charToGlyph(char c); public abstract double getGlyphAdvance(int glyphIndex); public abstract int getGlyphKerningAdjustment(int glyphIndex, int nextGlyphIndex); public abstract int getCharKerningAdjustment(char c1, char c2); public abstract int getGlyphWidth(int glyphIndex); public abstract String getFontNameIntag(); public abstract boolean isSmall(); public abstract boolean isBold(); public abstract boolean isItalic(); public abstract boolean isSmallEditable(); public abstract boolean isBoldEditable(); public abstract boolean isItalicEditable(); public abstract void setSmall(boolean value); public abstract void setBold(boolean value); public abstract void setItalic(boolean value); public abstract double getDivider(); public abstract int getAscent(); public abstract int getDescent(); public abstract int getLeading(); public String getFontName() { DefineFontNameTag fontNameTag = getFontNameTag(); if (fontNameTag == null) { return getFontNameIntag(); } return fontNameTag.fontName; } public String getFontCopyright() { DefineFontNameTag fontNameTag = getFontNameTag(); if (fontNameTag == null) { return ""; } return fontNameTag.fontCopyright; } public static Map<String, Map<String, Font>> installedFontsByFamily; public static Map<String, Font> installedFontsByName; public static String defaultFontName; static { reload(); } public int getFontId() { return getCharacterId(); } public boolean hasLayout() { return false; } public boolean containsChar(char character) { return charToGlyph(character) > -1; } public int getFontStyle() { int fontStyle = 0; if (isBold()) { fontStyle |= Font.BOLD; } if (isItalic()) { fontStyle |= Font.ITALIC; } return fontStyle; } public abstract int getCharacterCount(); public abstract String getCharacters(); @Override public String getName() { String nameAppend = ""; if (exportName != null) { nameAppend = ": " + exportName; } if (className != null) { nameAppend = ": " + className; } String fontName = getFontNameIntag(); if (fontName != null) { nameAppend = ": " + fontName; } return tagName + " (" + getCharacterId() + nameAppend + ")"; } @Override public String getExportFileName() { String result = super.getExportFileName(); String fontName = getFontNameIntag(); if (fontName != null) { fontName = fontName.replace(" ", "_"); } return result + (fontName != null ? "_" + fontName : ""); } public String getSystemFontName() { int fontId = getFontId(); String selectedFont = swf.sourceFontNamesMap.get(fontId); if (selectedFont == null) { SwfSpecificConfiguration swfConf = Configuration.getSwfSpecificConfiguration(swf.getShortFileName()); String key = fontId + "_" + getFontNameIntag(); if (swfConf != null) { selectedFont = swfConf.fontPairingMap.get(key); } } if (selectedFont == null) { selectedFont = Configuration.getFontToNameMap().get(getFontNameIntag()); } if (selectedFont != null && FontTag.installedFontsByName.containsKey(selectedFont)) { return selectedFont; } // findInstalledFontName always returns an available font name return FontTag.findInstalledFontName(getFontName()); } public Font getSystemFont() { return FontTag.installedFontsByName.get(getSystemFontName()); } protected void shiftGlyphIndices(int fontId, int startIndex, boolean increment) { for (Tag t : swf.getTags()) { List<TEXTRECORD> textRecords = null; if (t instanceof StaticTextTag) { textRecords = ((StaticTextTag) t).textRecords; } if (textRecords != null) { int curFontId = 0; for (TEXTRECORD tr : textRecords) { if (tr.styleFlagsHasFont) { curFontId = tr.fontId; } if (curFontId != fontId) { continue; } for (GLYPHENTRY en : tr.glyphEntries) { if (en == null) { //Currently edited continue; } if (en.glyphIndex >= startIndex) { if (increment) { en.glyphIndex++; } else { en.glyphIndex--; } } } t.setModified(true); } } } } public static float getSystemFontAdvance(String fontName, int fontStyle, int fontSize, Character character, Character nextCharacter) { return getSystemFontAdvance(new Font(fontName, fontStyle, fontSize), character, nextCharacter); } public static float getSystemFontAdvance(Font aFont, Character character, Character nextCharacter) { String chars = "" + character + (nextCharacter == null ? "" : nextCharacter); GlyphVector gv = aFont.layoutGlyphVector(new FontRenderContext(aFont.getTransform(), true, true), chars.toCharArray(), 0, chars.length(), Font.LAYOUT_LEFT_TO_RIGHT); GlyphMetrics gm = gv.getGlyphMetrics(0); return gm.getAdvanceX(); } public static void reload() { installedFontsByFamily = FontHelper.getInstalledFonts(); installedFontsByName = new HashMap<>(); for (String fam : installedFontsByFamily.keySet()) { for (String nam : installedFontsByFamily.get(fam).keySet()) { installedFontsByName.put(nam, installedFontsByFamily.get(fam).get(nam)); } } if (installedFontsByFamily.containsKey("Times New Roman")) { defaultFontName = "Times New Roman"; } else if (installedFontsByFamily.containsKey("Arial")) { defaultFontName = "Arial"; } else { defaultFontName = installedFontsByFamily.keySet().iterator().next(); } } public static String getFontNameWithFallback(String fontName) { if (installedFontsByFamily.containsKey(fontName)) { return fontName; } if (installedFontsByFamily.containsKey("Times New Roman")) { return "Times New Roman"; } if (installedFontsByFamily.containsKey("Arial")) { return "Arial"; } //First font return installedFontsByFamily.keySet().iterator().next(); } public static String isFontFamilyInstalled(String fontFamily) { if (installedFontsByFamily.containsKey(fontFamily)) { return fontFamily; } if (fontFamily.contains("_")) { String beforeUnderscore = fontFamily.substring(0, fontFamily.indexOf('_')); if (installedFontsByFamily.containsKey(beforeUnderscore)) { return beforeUnderscore; } } return null; } public static String findInstalledFontName(String fontName) { if (installedFontsByName.containsKey(fontName)) { return fontName; } if (fontName != null && fontName.contains("_")) { String beforeUnderscore = fontName.substring(0, fontName.indexOf('_')); if (installedFontsByName.containsKey(beforeUnderscore)) { return beforeUnderscore; } } return defaultFontName; } @Override public int getUsedParameters() { return PARAMETER_FRAME; } @Override public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) { RECT r = getRect(); return new Area(new Rectangle(r.Xmin, r.Ymin, r.getWidth(), r.getHeight())); } @Override public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) { SHAPERECORD.shapeListToImage(swf, getGlyphShapeTable(), image, frame, Color.black, colorTransform); } @Override public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { } @Override public void toHtmlCanvas(StringBuilder result, double unitDivisor) { List<SHAPE> shapes = getGlyphShapeTable(); result.append("\tdefaultFill = textColor;\r\n"); result.append("\tswitch(ch){\r\n"); for (int i = 0; i < shapes.size(); i++) { char c = glyphToChar(i); String cs = "" + c; cs = cs.replace("\\", "\\\\").replace("\"", "\\\""); result.append("\t\tcase \"").append(cs).append("\":\r\n"); CanvasShapeExporter exporter = new CanvasShapeExporter(null, unitDivisor, swf, shapes.get(i), null, 0, 0); exporter.export(); result.append("\t\t").append(exporter.getShapeData().replaceAll("\r\n", "\r\n\t\t")); result.append("\tbreak;\r\n"); } result.append("\t}\r\n"); } @Override public int getNumFrames() { int frameCount = (getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1; if (frameCount < 1) { frameCount = 1; } return frameCount; } @Override public boolean isSingleFrame() { return true; } @Override public RECT getRect() { return getRect(null); // parameter not used } @Override public RECT getRect(Set<BoundedTag> added) { return new RECT(0, (int) (PREVIEWSIZE * SWF.unitDivisor), 0, (int) (PREVIEWSIZE * SWF.unitDivisor)); } @Override public String getCharacterExportFileName() { return super.getCharacterExportFileName() + "_" + getFontNameIntag(); } public DefineFontNameTag getFontNameTag() { for (Tag t : swf.getTags()) { if (t instanceof DefineFontNameTag) { DefineFontNameTag dfn = (DefineFontNameTag) t; if (dfn.fontId == getFontId()) { return dfn; } } } return null; } public String getCopyright() { DefineFontNameTag dfn = getFontNameTag(); if (dfn == null) { return null; } return dfn.fontCopyright; } public RECT getGlyphBounds(int glyphIndex) { return getGlyphShapeTable().get(glyphIndex).getBounds(); } public FontTag toClassicFont() { return this; } }