/*
* Copyright (c) 2014. Matthew Campbell <matthew.campbell@mq.edu.au>, David R. Damerell <david@nixbioinf.org>.
*
* This file is part of GlycanBuilder Vaadin Release and its affliated projects EUROCarbDB, UniCarb-DB and UniCarbKB.
*
* This program is free software 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 3 of the License, or
* (at your option) any later version.
*
* GlycanBuilder Vaadin Release 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 (LICENSE.txt) for more details.
*
* You should have received a copy of the GNU General Public License
* along with GlycanBuilder Vaadin Release. If not, see <http ://www.gnu.org/licenses/>.
*/
package ac.uk.icl.dell.vaadin.canvas.basiccanvas.font;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;
/**
* Base class defining a Font for GWTCanvas.<br><br>
*
* A Font is an extended ArrayList of FontCharacter.<br>
* A FontCharacter is an extended ArrayList of FontSegment.<br>
* A FontSegment is an extended ArrayList of FontPoint.<br>
* A FontPoint is the x,y coordinate of a point.<br><br>
*
* Note: Only printable characters are supported,
* i.e. integer value from 32 to 126.
*
*/
public class Font extends HashMap<Character,FontCharacter> implements Serializable{
private static final long serialVersionUID=5024626343913116045L;
private String fontName;
public float characterSetMaxHeight=-1f;
public float characterSetMinHeight=1000f;
public ArrayList<FontCharacter> orderedFontList=new ArrayList<FontCharacter>();
enum FontFormat {
HERSHEY(),HERSHEY_CORD()
}
/**
* Constructor
*/
public Font() {
super();
this.fontName = "";
}
/**
* Constructor with field
*
* @param fontName name of the font
*/
public Font(String fontName) {
super();
this.fontName = fontName;
}
/**
* Constructor with field and initial capacity
*
* @param fontName name of the font
* @param initialCapacity initial capacity
*/
public Font(String fontName, int initialCapacity) {
super(initialCapacity);
this.fontName = fontName;
}
/**
* Constructor with field and initial Collection
*
* @param fontName name of the font
* @param c initial collection
*/
public Font(String fontName, Collection<? extends FontCharacter> c) {
super();
this.fontName = fontName;
}
/**
* Get the font name
* @return the font name
*/
public String getFontName() {
return fontName;
}
/**
* Set the font name
* @param fontName the font name
*/
public void setFontName(String fontName) {
this.fontName = fontName;
}
/**
* @author David R. Damerell
* @param stream
* @return
* @throws IOException
* @throws NumberFormatException
* @throws FontCharacterException
*/
public static Font parseFontDefinition(InputStream stream) throws IOException, NumberFormatException, FontCharacterException{
return parseFontDefinition(new InputStreamReader(stream));
}
/**
* @author David R. Damerell
* @param simplexFileName
* @return
* @throws IOException
* @throws NumberFormatException
* @throws FontCharacterException
*/
public static Font parseFontDefinition(String simplexFileName) throws IOException, NumberFormatException, FontCharacterException{
return parseFontDefinition(new FileReader(new File(simplexFileName)));
}
/**
* @author David R. Damerell
* @param stream
* @param utf8CharacterList
* @param format
* @return
* @throws NumberFormatException
* @throws IOException
* @throws FontCharacterException
*/
public static Font parseFontDefinition(InputStream stream,InputStream utf8CharacterList,FontFormat format) throws NumberFormatException, IOException, FontCharacterException{
if(format==FontFormat.HERSHEY_CORD){
return parseFontDefinition(stream);
}else if(format==FontFormat.HERSHEY){
System.err.println("here");
BufferedReader reader=new BufferedReader(new InputStreamReader(stream,"UTF8"));
List<HersheyFontCharacter> fontCharacters=new ArrayList<HersheyFontCharacter>();
String characterDefinition=null;
float minimumY=Float.MAX_VALUE;
Pattern startOfEntry=Pattern.compile("^\\s*[0-9]+");
Pattern emptyLine=Pattern.compile("^\\s*$");
String line;
while((line=reader.readLine())!=null){
if(startOfEntry.matcher(line).find()){
if(characterDefinition!=null){
HersheyFontCharacter character=parseHesheyCode(characterDefinition);
fontCharacters.add(character);
minimumY=updateMinimumY(character, minimumY);
characterDefinition=null;
}
characterDefinition=line;
}else{
if(!emptyLine.matcher(line).find()){
characterDefinition=characterDefinition.concat(line);
}
}
}
reader.close();
System.err.println("Number of characters: "+fontCharacters.size());
if(characterDefinition!=null){
HersheyFontCharacter character=parseHesheyCode(characterDefinition);
fontCharacters.add(character);
minimumY=updateMinimumY(character, minimumY);
}
for(HersheyFontCharacter hersheyChar:fontCharacters){
for(Float vertice[]:hersheyChar.vertices){
if(vertice!=null && vertice.length!=0){
vertice[1]=vertice[1]-minimumY;
}
}
}
reader=new BufferedReader(new InputStreamReader(utf8CharacterList,"UTF8"));
line=null;
int charPos=-1;
int numberOfCharacters=fontCharacters.size();
System.err.println("Number of characters: "+numberOfCharacters);
while((line=reader.readLine())!=null){
charPos++;
if(charPos>numberOfCharacters-1){
break;
}else{
if(line.length()>0){
fontCharacters.get(charPos).character=line.charAt(0);
}else{
//fontCharacters.get(charPos).character=line.charAt(0);
}
}
}
return createFontFromHersheyFontCharacters(fontCharacters);
}else{
return null;
}
}
/**
* @author David R. Damerell
*/
public void printCharactersInFont(){
for(char character:keySet()){
System.out.println(">"+character);
}
}
/**
* @author David R. Damerell
* @param hersheyFontCharacterList
* @return
*/
private static Font createFontFromHersheyFontCharacters(List<HersheyFontCharacter> hersheyFontCharacterList){
Font font=new Font();
for(HersheyFontCharacter hersheyCharacter:hersheyFontCharacterList){
float maxHeight=0f;
float minHeight=1000f;
FontCharacter fontCharacter=new FontCharacter();
FontSegment segment=new FontSegment();
fontCharacter.add(segment);
for(Float vertice[]:hersheyCharacter.vertices){
if(vertice==null || vertice.length==0){
segment=new FontSegment();
fontCharacter.add(segment);
}else{
FontPoint fontPoint=new FontPoint(vertice[0],vertice[1]);
segment.add(fontPoint);
if(maxHeight < fontPoint.getY()){
maxHeight=(float)fontPoint.getY();
}
if(minHeight > fontPoint.getY()){
minHeight=(float)fontPoint.getY();
}
}
}
fontCharacter.height=maxHeight-minHeight;
fontCharacter.setCharacter(hersheyCharacter.character);
if(font.characterSetMinHeight > fontCharacter.height){
font.characterSetMinHeight=fontCharacter.height;
}
if(font.characterSetMaxHeight < fontCharacter.height){
font.characterSetMaxHeight=fontCharacter.height;
}
fontCharacter.setWidth(hersheyCharacter.width);
font.put(hersheyCharacter.character, fontCharacter);
font.orderedFontList.add(fontCharacter);
}
return font;
}
/**
* @author David R. Damerell
* @param character
* @param minimumY
* @return
*/
private static float updateMinimumY(HersheyFontCharacter character,float minimumY){
for(Float vertice[]:character.vertices){
if(vertice!=null && vertice.length!=0 && vertice[1] < minimumY){
minimumY=vertice[1];
}
}
return minimumY;
}
/**
* @author David R. Damerell
* @param hersheyCode
* @return
*/
private static HersheyFontCharacter parseHesheyCode(String hersheyCode){
System.err.println("Hershey code: "+hersheyCode);
//Pattern whiteSpace=Pattern.compile("\\s+");
Pattern startWhiteSpace=Pattern.compile("^\\s+");
Pattern endWhiteSpace=Pattern.compile("\\s+$");
String glyphNumS=hersheyCode.substring(0, 5);
System.err.println("Glyph number: "+glyphNumS);
String numberOfVerticesS=hersheyCode.substring(6,8);
System.err.println("Number of vertices: "+numberOfVerticesS);
//int glyphNum=Integer.parseInt(whiteSpace.matcher(glyphNumS).replaceAll(""));
//int numberOfVertices=Integer.parseInt(whiteSpace.matcher(numberOfVerticesS).replaceAll(""));
int rAsInt='R';
int leftPosition=hersheyCode.charAt(8)-rAsInt;
System.err.println("Lenght: "+hersheyCode.length());
int rightPosition=hersheyCode.charAt(9)-rAsInt;
System.err.println("Left position: "+hersheyCode.charAt(8));
System.err.println("Right position: "+hersheyCode.charAt(9));
List<Float[]> vertices=new ArrayList<Float[]>();
if(hersheyCode.length()>10){
String points=hersheyCode.substring(10,hersheyCode.length());
points=startWhiteSpace.matcher(points).replaceAll("");
points=endWhiteSpace.matcher(points).replaceAll("");
for(int i=0;i<points.length()-1;i++){
int posI=points.charAt(i)-rAsInt;
if(points.charAt(i)==' ' && points.charAt(i+1)=='R'){
vertices.add(null);
i++;
}else{
int posJ=points.charAt(++i)-rAsInt;
vertices.add(new Float[]{(float) posI,(float) posJ});
}
}
float maximumNegativeX=0f,maximumNegativeY=0f;
for(Float vertice[]:vertices){
if(vertice!=null && vertice.length>0){
if(vertice[0]<0){
if(maximumNegativeX > vertice[0]){
maximumNegativeX=vertice[0];
}
}
if(vertice[1]<0){
if(maximumNegativeY>vertice[1]){
maximumNegativeY=vertice[1];
}
}
}
}
if(maximumNegativeX!=0){
float offSetX=Math.abs(maximumNegativeX);
for(Float[] vertice:vertices){
if(vertice!=null && vertice.length>0){
vertice[0]+=offSetX;
}
}
}
}
int width=rightPosition-leftPosition;
return new HersheyFontCharacter(vertices,width);
}
/**
* @author David R. Damerell
* @param readerInstance
* @throws IOException
* @throws NumberFormatException
* @throws FontCharacterException
*/
public static Font parseFontDefinition(Reader readerInstance) throws IOException, NumberFormatException, FontCharacterException{
Font font=new Font();
BufferedReader reader=new BufferedReader(readerInstance);
String line;
while((line=reader.readLine())!=null){
boolean at=false;
boolean ti=false;
if(line.startsWith("@")){
at=true;
line=line.replaceFirst("@", "at");
}else if(line.startsWith("~")){
ti=true;
line=line.replaceFirst("~", "line");
}
String components[]=line.split("@");
String infoCols[]=components[0].split("~");
if(at){
infoCols[0]="@";
}else if(ti){
infoCols[0]="~";
}
FontCharacter fontCharacter;
FontSegment fontSegment;
fontCharacter = new FontCharacter(infoCols[0].charAt(0), Double.parseDouble(infoCols[1]));
float maxHeight=0f;
float minHeight=1000f;
for(int i=1;i<components.length;i++){
fontSegment=new FontSegment();
fontCharacter.add(fontSegment);
components[i]=components[i].replaceFirst("@", "");
components[i]=components[i].replaceFirst(",$", "");
String points[]=components[i].split(",");
for(int j=0;j<points.length;j++){
FontPoint fontPoint=new FontPoint(Double.parseDouble(points[j]),Double.parseDouble(points[++j]));
fontSegment.add(fontPoint);
if(maxHeight < fontPoint.getY()){
maxHeight=(float)fontPoint.getY();
}
if(minHeight > fontPoint.getY()){
minHeight=(float)fontPoint.getY();
}
}
}
fontCharacter.height=maxHeight-minHeight;
if(font.characterSetMinHeight > fontCharacter.height){
font.characterSetMinHeight=fontCharacter.height;
}
if(font.characterSetMaxHeight < fontCharacter.height){
font.characterSetMaxHeight=fontCharacter.height;
}
font.put(infoCols[0].charAt(0), fontCharacter);
font.orderedFontList.add(fontCharacter);
}
return font;
}
/**
* @author David R. Damerell
*/
static HashMap<FONT,Font> fonts=new HashMap<FONT,Font>();
/**
* @author David R. Damerell
* @author david
*
*/
public enum FONT{
GREEK(),ROMAN(),STANDARD();
}
/**
* @author David R. Damerell
* @param font
* @return
*/
public static Font getFont(FONT font){
if(fonts.containsKey(font)){
return fonts.get(font);
}else{
return null;
}
}
/**
* @author David R. Damerell
*/
@Override
public Font clone(){
Font cloneFont=new Font();
for(FontCharacter character:orderedFontList){
FontCharacter cloneCharacter=character.clone();
cloneFont.orderedFontList.add(cloneCharacter);
cloneFont.put(cloneCharacter.getCharacter(), cloneCharacter);
}
cloneFont.characterSetMaxHeight=characterSetMaxHeight;
cloneFont.characterSetMinHeight=characterSetMinHeight;
cloneFont.fontName=fontName;
return cloneFont;
}
/**
* @author David R. Damerell
* @param fonts
* @return
*/
public static Font mergeFonts(List<Font> fonts){
Font mergedFont=new Font();
mergedFont.characterSetMaxHeight=Float.MIN_VALUE;
mergedFont.characterSetMinHeight=Float.MAX_VALUE;
for(Font fontX:fonts){
fontX=fontX.clone();
for(FontCharacter character:fontX.orderedFontList){
if(!mergedFont.containsKey(character.getCharacter())){
mergedFont.put(character.getCharacter(), character);
mergedFont.orderedFontList.add(character);
if(mergedFont.characterSetMinHeight > character.height){
mergedFont.characterSetMinHeight=character.height;
}
if(mergedFont.characterSetMaxHeight < character.height){
mergedFont.characterSetMaxHeight=character.height;
}
}
}
}
return mergedFont;
}
/**
* @author David R. Damerell
*/
static {
try{
Font font=Font.parseFontDefinition(Font.class.getResourceAsStream("/ac/uk/icl/dell/vaadin/canvas/basiccanvas/font/hershey/romans.jhf"),
Font.class.getResourceAsStream("/ac/uk/icl/dell/vaadin/canvas/basiccanvas/font/maps/romans_character_map"), FontFormat.HERSHEY);
fonts.put(FONT.ROMAN,font);
}catch(Exception e){
e.printStackTrace();
}
try{
Font font=Font.parseFontDefinition(Font.class.getResourceAsStream("/ac/uk/icl/dell/vaadin/canvas/basiccanvas/font/hershey/greeks.jhf"),
Font.class.getResourceAsStream("/ac/uk/icl/dell/vaadin/canvas/basiccanvas/font/maps/greeks_character_map"), FontFormat.HERSHEY);
fonts.put(FONT.GREEK,font);
}catch(Exception e){
e.printStackTrace();
}
List<Font> mergeFontList=new ArrayList<Font>();
mergeFontList.add(fonts.get(FONT.ROMAN));
mergeFontList.add(fonts.get(FONT.GREEK));
fonts.put(FONT.STANDARD, Font.mergeFonts(mergeFontList));
}
public static void main(String args[]){
}
}