/*
* 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.exporters;
import com.google.typography.font.sfntly.Font;
import com.google.typography.font.sfntly.FontFactory;
import com.google.typography.font.sfntly.data.WritableFontData;
import com.google.typography.font.tools.conversion.woff.WoffWriter;
import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler;
import com.jpexs.decompiler.flash.ApplicationInfo;
import com.jpexs.decompiler.flash.EventListener;
import com.jpexs.decompiler.flash.ReadOnlyTagList;
import com.jpexs.decompiler.flash.RetryTask;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.modes.FontExportMode;
import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings;
import com.jpexs.decompiler.flash.exporters.shape.PathExporter;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import fontastic.FGlyph;
import fontastic.FPoint;
import fontastic.Fontastic;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*/
public class FontExporter {
public List<File> exportFonts(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, final FontExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List<File> ret = new ArrayList<>();
if (tags.isEmpty()) {
return ret;
}
File foutdir = new File(outdir);
Path.createDirectorySafe(foutdir);
int count = 0;
for (Tag t : tags) {
if (t instanceof FontTag) {
count++;
}
}
if (count == 0) {
return ret;
}
int currentIndex = 1;
for (Tag t : tags) {
if (t instanceof FontTag) {
if (evl != null) {
evl.handleExportingEvent("font", currentIndex, count, t.getName());
}
final FontTag st = (FontTag) t;
String ext = ".ttf";
if (settings.mode == FontExportMode.WOFF) {
ext = ".woff";
}
final File file = new File(outdir + File.separator + Helper.makeFileName(st.getCharacterExportFileName() + ext));
new RetryTask(() -> {
exportFont(st, settings.mode, file);
}, handler).run();
ret.add(file);
if (evl != null) {
evl.handleExportedEvent("font", currentIndex, count, t.getName());
}
currentIndex++;
}
}
return ret;
}
public byte[] exportFont(final FontTag t, FontExportMode mode) {
try {
String ext = null;
switch (mode) {
case TTF:
ext = ".ttf";
break;
case WOFF:
ext = ".woff";
}
File f = File.createTempFile("temp", ext);
exportFont(t, mode, f);
return Helper.readFile(f.getPath());
} catch (IOException ex) {
Logger.getLogger(FontExporter.class.getName()).log(Level.SEVERE, null, ex);
}
return SWFInputStream.BYTE_ARRAY_EMPTY;
}
public void exportFont(FontTag ft, FontExportMode mode, File file) throws IOException {
final FontTag t = ft.toClassicFont();
List<SHAPE> shapes = t.getGlyphShapeTable();
final double divider = t.getDivider();
File ttfFile = file;
if (mode == FontExportMode.WOFF) {
ttfFile = File.createTempFile("ffdec_export", ".ttf");
}
String fontName = Helper.makeFileName(t.getFontNameIntag());
if (fontName.length() == 0) {
fontName = "noname";
}
Fontastic f = new Fontastic(fontName, ttfFile);
String cop = t.getCopyright();
f.getEngine().setCopyrightYear(cop == null ? "" : cop);
if (Configuration.setFFDecVersionInExportedFont.get()) {
f.setAuthor(ApplicationInfo.shortApplicationVerName);
} else {
f.setAuthor(ApplicationInfo.SHORT_APPLICATION_NAME);
}
f.setVersion("1.0");
SWF swf = t.getSwf();
if (swf != null) {
Date date = swf.getFileModificationDate();
f.setCreationDate(date);
f.setModificationDate(date);
}
int ascent = t.getAscent();
if (ascent != -1) {
float value = Math.round(ascent / divider);
value = Math.min(value, 1024);
f.setAscender(value);
}
int descent = t.getDescent();
if (descent != -1) {
float value = Math.round(descent / divider);
value = Math.min(value, 1024);
f.setDescender(value);
}
int glyphCount = 0;
for (int i = 0; i < shapes.size(); i++) {
//if there are more glyphs for one char (in some weird fonts), use the last glyph
char c = t.glyphToChar(i);
while (i + 1 < shapes.size() && t.glyphToChar(i + 1) == c) {
i++;
}
SHAPE s = shapes.get(i);
final List<FPoint[]> contours = new ArrayList<>();
PathExporter seb = new PathExporter(swf, s, null) {
private double transformX(double x) {
return Math.ceil((double) (x / divider));
}
private double transformY(double y) {
return -Math.ceil((double) (y / divider));
}
List<FPoint> path = new ArrayList<>();
@Override
protected void finalizePath() {
FPoint[] points = path.toArray(new FPoint[path.size()]);
if (points.length > 0) {
contours.add(points);
}
path.clear();
}
@Override
public void moveTo(double x, double y) {
finalizePath();
path.add(new FPoint(transformX(x), transformY(y)));
}
@Override
public void lineTo(double x, double y) {
path.add(new FPoint(transformX(x), transformY(y)));
}
@Override
public void curveTo(double controlX, double controlY, double anchorX, double anchorY) {
path.add(new FPoint(
new FPoint(transformX(anchorX), transformY(anchorY)),
new FPoint(transformX(controlX), transformY(controlY))
));
}
};
seb.export();
FGlyph g = f.addGlyph(c);
glyphCount++;
double adv = t.getGlyphAdvance(i);
if (adv != -1) {
g.setAdvanceWidth((int) Math.round(adv / divider));
} else {
g.setAdvanceWidth((int) Math.round(t.getGlyphWidth(i) / divider + 100));
}
for (FPoint[] cnt : contours) {
if (cnt.length == 0) {
continue;
}
g.addContour(cnt);
}
}
if (glyphCount == 0) {
return;
}
f.buildFont();
if (mode == FontExportMode.WOFF) {
FontFactory fontFactory = FontFactory.getInstance();
byte[] fontBytes;
try (FileInputStream fis = new FileInputStream(ttfFile)) {
fontBytes = new byte[(int) ttfFile.length()];
fis.read(fontBytes);
}
Font[] fontArray = null;
fontArray = fontFactory.loadFonts(fontBytes);
Font font = fontArray[0];
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) {
WoffWriter w = new WoffWriter();
WritableFontData woffData = w.convert(font);
woffData.copyTo(fos);
}
ttfFile.delete();
}
}
}