/* * 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; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; import com.jpexs.decompiler.flash.tags.base.FontInfoTag; import com.jpexs.decompiler.flash.tags.base.FontTag; import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.SHAPE; import com.jpexs.decompiler.flash.types.annotations.Internal; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; import com.jpexs.helpers.ByteArrayRange; import java.awt.Font; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * * @author JPEXS */ @SWFVersion(from = 1) public class DefineFontTag extends FontTag { public static final int ID = 10; public static final String NAME = "DefineFont"; @SWFType(BasicType.UI16) public int fontId; public List<SHAPE> glyphShapeTable; @Internal private FontInfoTag fontInfoTag = null; /** * Constructor * * @param swf */ public DefineFontTag(SWF swf) { super(swf, ID, NAME, null); fontId = swf.getNextCharacterId(); glyphShapeTable = new ArrayList<>(); } /** * Constructor * * @param sis * @param data * @throws IOException */ public DefineFontTag(SWFInputStream sis, ByteArrayRange data) throws IOException { super(sis.getSwf(), ID, NAME, data); readData(sis, data, 0, false, false, false); } @Override public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { fontId = sis.readUI16("fontId"); glyphShapeTable = new ArrayList<>(); if (sis.available() > 0) { long pos = sis.getPos(); int firstOffset = sis.readUI16("firstOffset"); int nGlyphs = firstOffset / 2; long[] offsetTable = new long[nGlyphs]; offsetTable[0] = firstOffset; for (int i = 1; i < nGlyphs; i++) { offsetTable[i] = sis.readUI16("offset"); } for (int i = 0; i < nGlyphs; i++) { sis.seek(pos + offsetTable[i]); glyphShapeTable.add(sis.readSHAPE(1, false, "shape")); } } } /** * Gets data bytes * * @param sos SWF output stream * @throws java.io.IOException */ @Override public void getData(SWFOutputStream sos) throws IOException { sos.writeUI16(fontId); ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); List<Integer> offsetTable = new ArrayList<>(); SWFOutputStream sos2 = new SWFOutputStream(baos2, getVersion()); for (SHAPE shape : glyphShapeTable) { offsetTable.add(glyphShapeTable.size() * 2 + (int) sos2.getPos()); sos2.writeSHAPE(shape, 1); } for (int offset : offsetTable) { sos.writeUI16(offset); } sos.write(baos2.toByteArray()); } @Override public boolean isSmall() { return false; } @Override public double getGlyphAdvance(int glyphIndex) { return -1; } @Override public int getGlyphWidth(int glyphIndex) { return glyphShapeTable.get(glyphIndex).getBounds().getWidth(); } private void ensureFontInfo() { if (fontInfoTag == null) { List<CharacterIdTag> characterIdTags = swf.getCharacterIdTags(fontId); if (characterIdTags != null) { for (CharacterIdTag t : characterIdTags) { if (t instanceof FontInfoTag) { if (((FontInfoTag) t).fontID == fontId) { fontInfoTag = (FontInfoTag) t; break; } } } } } } @Override public char glyphToChar(int glyphIndex) { ensureFontInfo(); if (fontInfoTag != null) { return (char) (int) fontInfoTag.getCodeTable().get(glyphIndex); } else { return '?'; } } @Override public int charToGlyph(char c) { ensureFontInfo(); if (fontInfoTag != null) { return fontInfoTag.getCodeTable().indexOf((int) c); } return -1; } @Override public List<SHAPE> getGlyphShapeTable() { return glyphShapeTable; } @Override public int getCharacterId() { return fontId; } @Override public void setCharacterId(int characterId) { this.fontId = characterId; } @Override public String getFontNameIntag() { ensureFontInfo(); if (fontInfoTag != null) { return fontInfoTag.getFontName(); } return null; } @Override public boolean isBold() { if (fontInfoTag != null) { return fontInfoTag.getFontFlagsBold(); } return false; } @Override public boolean isItalic() { if (fontInfoTag != null) { return fontInfoTag.getFontFlagsItalic(); } return false; } @Override public boolean isSmallEditable() { return false; } @Override public boolean isBoldEditable() { return fontInfoTag != null; } @Override public boolean isItalicEditable() { return fontInfoTag != null; } @Override public void setSmall(boolean value) { } @Override public void setBold(boolean value) { if (fontInfoTag != null) { fontInfoTag.setFontFlagsBold(value); } } @Override public void setItalic(boolean value) { if (fontInfoTag != null) { fontInfoTag.setFontFlagsItalic(value); } } @Override public int getAscent() { return -1; } @Override public int getDescent() { return -1; } @Override public int getLeading() { return -1; } @Override public double getDivider() { return 1; } @Override public void addCharacter(char character, Font font) { SHAPE shp = SHAPERECORD.fontCharacterToSHAPE(font, (int) Math.round(getDivider() * 1024), character); ensureFontInfo(); int code = (int) character; int pos = -1; boolean exists = false; if (fontInfoTag != null) { List<Integer> codeTable = fontInfoTag.getCodeTable(); for (int i = 0; i < codeTable.size(); i++) { if (codeTable.get(i) >= code) { if (codeTable.get(i) == code) { exists = true; } pos = i; break; } } if (pos == -1) { pos = codeTable.size(); } } else { pos = 0; } if (!exists) { shiftGlyphIndices(fontId, pos, true); glyphShapeTable.add(pos, shp); if (fontInfoTag != null) { fontInfoTag.addFontCharacter(pos, (int) character); } } else { glyphShapeTable.set(pos, shp); } setModified(true); getSwf().clearImageCache(); } @Override public boolean removeCharacter(char character) { ensureFontInfo(); if (fontInfoTag == null) { return false; } int code = (int) character; int pos = -1; List<Integer> codeTable = fontInfoTag.getCodeTable(); for (int i = 0; i < codeTable.size(); i++) { if (codeTable.get(i) >= code) { if (codeTable.get(i) == code) { pos = i; break; } return false; } } if (pos == -1) { return false; } glyphShapeTable.remove(pos); fontInfoTag.removeFontCharacter(pos); shiftGlyphIndices(fontId, pos + 1, false); setModified(true); getSwf().clearImageCache(); return true; } @Override public void setAdvanceValues(Font font) { throw new UnsupportedOperationException("Setting the advance values for DefineFontTag is not supported."); } @Override public int getCharacterCount() { ensureFontInfo(); if (fontInfoTag != null) { List<Integer> codeTable = fontInfoTag.getCodeTable(); return codeTable.size(); } return 0; } @Override public String getCharacters() { ensureFontInfo(); if (fontInfoTag != null) { List<Integer> codeTable = fontInfoTag.getCodeTable(); StringBuilder ret = new StringBuilder(codeTable.size()); for (int i : codeTable) { ret.append((char) i); } return ret.toString(); } return ""; } @Override public int getGlyphKerningAdjustment(int glyphIndex, int nextGlyphIndex) { return 0; } @Override public int getCharKerningAdjustment(char c1, char c2) { return 0; } }