/**
* This file is part of the ReTeX library - https://github.com/himamis/ReTeX
*
* Copyright (C) 2015 Balazs Bencze
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program 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
* General Public License for more details.
*
* A copy of the GNU General Public License can be found in the file
* LICENSE.txt provided with the source distribution of this program (see
* the META-INF directory in the source jar). This license can also be
* found on the GNU website at http://www.gnu.org/licenses/gpl.html.
*
* If you did not receive a copy of the GNU General Public License along
* with this program, contact the lead developer, or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* Linking this library statically or dynamically with other modules
* is making a combined work based on this library. Thus, the terms
* and conditions of the GNU General Public License cover the whole
* combination.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce
* an executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under terms
* of your choice, provided that you also meet, for each linked independent
* module, the terms and conditions of the license of that module.
* An independent module is a module which is not derived from or based
* on this library. If you modify this library, you may extend this exception
* to your version of the library, but you are not obliged to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
*/
package com.himamis.retex.renderer.web.font.opentype;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.ScriptInjector;
import com.himamis.retex.renderer.web.font.FontLoaderWrapper;
import com.himamis.retex.renderer.web.font.FontW;
import com.himamis.retex.renderer.web.resources.js.JsResources;
public class Opentype implements FontLoaderWrapper {
public static final Opentype INSTANCE = new Opentype();
private static class FontContainer {
public OpentypeFontWrapper font;
public boolean fontIsLoading;
public FontContainer(OpentypeFontWrapper font) {
this.font = font;
this.fontIsLoading = true;
}
}
private List<OpentypeFontStatusListener> listeners;
private Map<String, FontContainer> fonts;
private String fontBaseUrl = GWT.getModuleBaseURL();
private Opentype() {
ScriptInjector.fromString(JsResources.INSTANCE.opentypeJs().getText())
.setRemoveTag(false).inject();
listeners = new ArrayList<OpentypeFontStatusListener>();
fonts = new HashMap<String, FontContainer>();
}
public void addListener(OpentypeFontStatusListener listener) {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
public void removeListener(OpentypeFontStatusListener listener) {
listeners.remove(listener);
}
private void fireFontActiveEvent(String familyName) {
OpentypeFontWrapper fontWrapper = fonts.get(familyName).font;
// a copy of the listeners is needed, because listeners are being
// removed
// from the list throughout the iteration.
// see OpentypeFont::onFontLoaded(..)
List<OpentypeFontStatusListener> copyList = new ArrayList<OpentypeFontStatusListener>(
listeners);
for (OpentypeFontStatusListener listener : copyList) {
listener.onFontLoaded(fontWrapper, familyName);
}
}
private void fireFontInactiveEvent(Object error, String familyName) {
for (OpentypeFontStatusListener listener : listeners) {
listener.onFontError(error, familyName);
}
}
private boolean fontEntryExists(String familyName) {
return fonts.get(familyName) != null;
}
private void createFontEntry(String familyName) {
FontContainer fontContainer = new FontContainer(null);
fonts.put(familyName, fontContainer);
}
boolean fontIsLoading(String familyName) {
FontContainer fontContainer = fonts.get(familyName);
return fontContainer != null && fontContainer.fontIsLoading;
}
boolean fontIsLoaded(String familyName) {
FontContainer fontContainer = fonts.get(familyName);
return fontContainer != null && !fontContainer.fontIsLoading;
}
OpentypeFontWrapper getFont(String familyName) {
return fonts.get(familyName).font;
}
private void setFontIsLoaded(String familyName, JavaScriptObject font) {
FontContainer fontContainer = fonts.get(familyName);
fontContainer.font = new OpentypeFontWrapper(font);
fontContainer.fontIsLoading = false;
}
private void loadFont(String path, String familyName) {
// font does not exist
if (!fontEntryExists(familyName)) {
createFontEntry(familyName);
loadJavascriptFont(fontBaseUrl + path, familyName);
} else if (fontIsLoading(familyName)) {
// do nothing, wait for the font to be loaded
} else if (fontIsLoaded(familyName)) {
fireFontActiveEvent(familyName);
}
}
private void loadJavascriptFont(String path0, final String familyName) {
String path = path0.substring(0, path0.length() - 3);
path = path + "js";
ScriptInjector.fromUrl(path).setWindow(ScriptInjector.TOP_WINDOW)
.setRemoveTag(true)
.setCallback(new Callback<Void, Exception>() {
@Override
public void onFailure(Exception reason) {
fireFontInactiveEvent(reason, familyName);
}
@Override
public void onSuccess(Void result) {
nativeParseFont(familyName);
}
}).inject();
}
private native void nativeParseFont(String familyName) /*-{
var that = this;
var base64EncodedData = $wnd.__JLM_GWT_FONTS__[familyName];
var decodedArrayBuffer = that.@com.himamis.retex.renderer.web.font.opentype.Opentype::base64ToArrayBuffer(Ljava/lang/String;)(base64EncodedData)
var font = opentype.parse(decodedArrayBuffer);
if (!font.supported) {
that.@com.himamis.retex.renderer.web.font.opentype.Opentype::fireFontInactiveEvent(Ljava/lang/Object;Ljava/lang/String;)("Parse error: font not supported", familyName);
} else {
that.@com.himamis.retex.renderer.web.font.opentype.Opentype::setFontIsLoaded(Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)(familyName, font);
that.@com.himamis.retex.renderer.web.font.opentype.Opentype::fireFontActiveEvent(Ljava/lang/String;)(font.familyName);
}
}-*/;
private native JavaScriptObject base64ToArrayBuffer(String base64) /*-{
var binaryString = window.atob(base64);
var length = binaryString.length;
var bytes = new Uint8Array(length);
for (var i = 0; i < length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}-*/;
@Override
public FontW createNativeFont(String pathName, String fontName, int style,
int size) {
loadFont(pathName, fontName);
return new OpentypeFont(fontName, style, size);
}
/**
* Sets the base URL from where the fonts are loaded.
*
* @param url
* base URL
*/
public void setFontBaseUrl(String url) {
fontBaseUrl = url;
}
}