Acessando informações kerning fonte em Java2D
Pergunta
Little fundo ..
Eu estou no processo de fazer um jogo OpenGL usando Java e LWJGL . Eu escrevi um TextRenderer
de classe que presta texto usando páginas em cache de glifos. Os glifos em si são prestados em Java2D para BufferedImage
s e embalados em páginas textura, juntamente com as medidas de glifo. TextRenderer
desenha os personagens como quads texturizados, utilizando as informações armazenadas em cache.
Tudo isso funciona bem, exceto por uma coisa: falta kerning. Concedido, não é necessário ter como a aparência de texto bem como está, mas seria melhorar a qualidade se eu tivesse acesso à informação kerning fonte.
E a pergunta é ..
É possível obter as informações kerning usando plain Java, de uma forma que seria portável para Windows, Linux e MacOS X? Voltar quando eu escrevi o TextRenderer
I brevemente olhou em volta, mas não conseguiu encontrar uma forma ..
Uma possível solução
Se não há nenhuma maneira de fazer isso em Java puro, eu estava pensando em escrever uma ferramenta separada usando Freetype . Como listado em sua página de recursos:
FreeType 2 fornece informações que muitas vezes não é disponível a partir de outra motores de fonte semelhante, como kerning distâncias , nomes de glifos, vertical métricas, etc.
A ferramenta seria armazenar os pares de kerning para personagens comuns em um arquivo que o meu processador de texto iria carregar e fazer uso. Portanto, este é provavelmente o que vou fazer se vocês não vêm-se com uma alternativa melhor. :)
Solução
As únicas bibliotecas que eu conheço que leia a informação kerning "somwhat" corretamente são iText FOP de Apache.
http://www.1t3xt.info/api /com/lowagie/text/pdf/BaseFont.html http: / /svn.apache.org/viewvc/xmlgraphics/fop/tags/fop-0_95/src/java/org/apache/fop/fonts/ (um link para o svn como parece haver nenhuma API on-line)
Outras dicas
A partir do Java SE 6, Java pode fornecer informações kerning quando a fonte fornece. É desativado por padrão e pode ser ligado assim:
Map<TextAttribute, Object> textAttributes = new HashMap<TextAttribute, Object>();
textAttributes.put(TextAttribute.FAMILY, "Arial");
textAttributes.put(TextAttribute.SIZE, 25f);
textAttributes.put(TextAttribute.KERNING, TextAttribute.KERNING_ON);
Font font = Font.getFont(textAttributes);
Este tópico do fórum contém uma discussão mais detalhada sobre o tema:
no meu caminho para encontrar as informações de ajuste de espaço e fornecendo-lo offline dentro do meu JavaScript, eu li esta pergunta aqui, também e desde que não havia nenhuma resposta, eu procurei mais adiante e no final eu tenho esse:
Dois javascript objetos que podem ser indexados pelo unicode do glifo:
GLYPHS = {};
KERNS = {};
E eles são definidos como este:
// GLYPHS[ unicode ] = [ "name", width ];
// KERNS [ unicode ] = { "nextGlyphName" : horizontalAdjustment };
// = { GLYPHS[ unicode ][ 0 ] : horizontalAdjustment };
Então, se eu tenho a minha string "Texto", eu passar por isso caractere por caractere e usar sua unicode para acessar o nome e largura de glifos como esta:
glUnicode = "Text".charCodeAt( strIndex ); // "T" == 84
glName = GLYPHS[ glUnicode ][ 0 ];
glWidth = GLYPHS[ glUnicode ][ 1 ];
Para acessar o valor kerning, temos de olhar para os próximos caracteres Unicode valor que é o seguinte:
nextGlyphUnicode = "Text".charCodeAt( strIndex + 1 ); // "e" == 101
E se existe o seguinte objeto, então esta afirmação vai lhe dar a largura kerning (você tem que verificar para a existência primeira como esta:
if ( !( KERNS[ glUnicode ] == undefined ) ) {
if ( !( KERNS[ glUnicode ][ GLYPHS[ nextGlyphUnicode ][ 0 ] ] == undefined ) ) {
...
):
kernWidth = KERNS[ glUnicode ][ GLYPHS[ nextGlyphUnicode ][ 0 ] ];
Neste exemplo, kernWidth para o "e" após o "T" seria
kernWidth == -143
Eu acho, isso é o que você estava procurando, certo? Todas as informações acessíveis através do valor Unicode do caractere eo valor Unicode do caractere seguinte. Muito simples e muito agradável.
Então, eu criei um arquivo para cada fonte e os primeiros olhares página como esta:
// Family Name
// 'Times New Roman'
// EM size
// '2048'
// is_quadratic
// '1'
//
// GLYPHS[ unicode ] = [ "name", width ];
// KERNS [ unicode ] = { "nextGlyphName" : horizontalAdjustment };
// = { GLYPHS[ unicode ][ 0 ] : horizontalAdjustment };
GLYPHS = {};
KERNS = {};
GLYPHS[ 32 ] = [ "space", 512 ];
KERNS [ 32 ] = {
"Upsilondieresis" : -76,
"Upsilon" : -76,
"Tau" : -37,
"Lambda" : -113,
"Delta" : -113,
"Alpha" : -113,
"Alphatonos" : -113,
"Y" : -76,
"W" : -37,
"V" : -37,
"T" : -37,
"A" : -113
};
GLYPHS[ 33 ] = [ "exclam", 682 ];
GLYPHS[ 34 ] = [ "quotedbl", 836 ];
GLYPHS[ 35 ] = [ "numbersign", 1024 ];
GLYPHS[ 36 ] = [ "dollar", 1024 ];
GLYPHS[ 37 ] = [ "percent", 1706 ];
GLYPHS[ 38 ] = [ "ampersand", 1593 ];
GLYPHS[ 39 ] = [ "quotesingle", 369 ];
GLYPHS[ 40 ] = [ "parenleft", 682 ];
GLYPHS[ 41 ] = [ "parenright", 682 ];
GLYPHS[ 42 ] = [ "asterisk", 1024 ];
GLYPHS[ 43 ] = [ "plus", 1155 ];
GLYPHS[ 44 ] = [ "comma", 512 ];
GLYPHS[ 45 ] = [ "hyphen", 682 ];
GLYPHS[ 46 ] = [ "period", 512 ];
GLYPHS[ 47 ] = [ "slash", 569 ];
GLYPHS[ 48 ] = [ "zero", 1024 ];
GLYPHS[ 49 ] = [ "one", 1024 ];
KERNS [ 49 ] = {
"one" : -76
};
GLYPHS[ 50 ] = [ "two", 1024 ];
GLYPHS[ 51 ] = [ "three", 1024 ];
GLYPHS[ 52 ] = [ "four", 1024 ];
GLYPHS[ 53 ] = [ "five", 1024 ];
GLYPHS[ 54 ] = [ "six", 1024 ];
GLYPHS[ 55 ] = [ "seven", 1024 ];
GLYPHS[ 56 ] = [ "eight", 1024 ];
GLYPHS[ 57 ] = [ "nine", 1024 ];
GLYPHS[ 58 ] = [ "colon", 569 ];
GLYPHS[ 59 ] = [ "semicolon", 569 ];
GLYPHS[ 60 ] = [ "less", 1155 ];
GLYPHS[ 61 ] = [ "equal", 1155 ];
GLYPHS[ 62 ] = [ "greater", 1155 ];
GLYPHS[ 63 ] = [ "question", 909 ];
GLYPHS[ 64 ] = [ "at", 1886 ];
GLYPHS[ 65 ] = [ "A", 1479 ];
KERNS [ 65 ] = {
"quoteright" : -227,
"y" : -188,
"w" : -188,
"v" : -152,
"Y" : -188,
"W" : -164,
"V" : -264,
"T" : -227,
"space" : -113
};
GLYPHS[ 66 ] = [ "B", 1366 ];
GLYPHS[ 67 ] = [ "C", 1366 ];
Você pode copiar o conteúdo de cada arquivo que você precisa em seu código fonte ou lê-lo em em tempo de execução para ter os objetos availlable.
e os arquivos podem ser criados com o seguinte script que funciona muito bem dentro de "embedded" 2,7 interpretador Python da fontforge
.
Este script é projetado para uma máquina Windows, então você tem que adaptar os seus caminhos em primeiro lugar!
#
# run these two commands in the fontforge "embedded" python interpreter (ffpython.exe)
# >>> script = open( "Scripts\\Kernings.py", "r" )
# >>> exec script
import fontforge
fontFilenames = [
"arial.ttf",
"arialbd.ttf",
"ariali.ttf",
"arialbi.ttf",
"ARIALN.TTF",
"ARIALNB.TTF",
"ARIALNI.TTF",
"ARIALNBI.TTF",
"calibri.ttf",
"calibrib.ttf",
"calibrii.ttf",
"calibriz.ttf",
"cambria.ttc",
"cambriab.ttf",
"cambriai.ttf",
"cambriaz.ttf",
"times.ttf",
"timesbd.ttf",
"timesi.ttf",
"timesbi.ttf",
"verdana.ttf",
"verdanab.ttf",
"verdanai.ttf",
"verdanaz.ttf"
]
for actFontFile in fontFilenames :
print( "c:\\windows\\fonts\\" + actFontFile )
out = open( "Scripts\\Kern_" + actFontFile[ : len( actFontFile ) - 4 ] + "_json.txt", "w" )
font = fontforge.open( "c:\\windows\\fonts\\" + actFontFile )
out.write(
"// Family Name\n// '" + font.familyname + "'\n"
+ "// EM size\n// '" + str( font.em ) + "'\n"
+ "// is_quadratic\n// '" + str( font.is_quadratic ) + "'\n"
+ "//\n"
+ '// GLYPHS[ unicode ] = [ "name", width ];\n'
+ '// KERNS [ unicode ] = { "nextGlyphName" : horizontalAdjustment };\n'
+ "// = { GLYPHS[ unicode ][ 0 ] : horizontalAdjustment };\n"
+ "GLYPHS = {};\n"
+ "KERNS = {};\n\n"
)
glyphIdIterator = font.__iter__()
for glyphName in glyphIdIterator :
if font[ glyphName ].unicode >=0 :
kerningStrings = []
outstring = ( "GLYPHS[ "
+ str( font[ glyphName ].unicode ).rjust( 5 ) + " ] = [ \""
+ glyphName + "\","
+ str( font[ glyphName ].width ).rjust( 5 ) + " ];\n"
)
subs = font[ glyphName ].getPosSub("*")
if len( subs ):
for sub in subs:
if len( sub ):
for subsub in sub:
if str( subsub ).lower().find( "'kern'" ) >=0:
kerningStrings.append(
(" \"" + str( sub[ 2 ] ) + "\"").ljust( 20 )
+ ":" + str( sub[ 5 ] ).rjust( 6 )
)
break
krnStrLen = len( kerningStrings )
if ( krnStrLen ) :
outstring = outstring + ( "KERNS [ "
+ str( font[ glyphName ].unicode ).rjust( 5 ) + " ] = {" )
for kerningString in kerningStrings :
outstring = outstring + "\n" + kerningString + ","
outstring = outstring.rstrip( "," )
outstring = outstring + "\n};\n"
out.write( outstring )
out.close()
font.close()
Espero, isso pode ajudar. Muito obrigado pela sua atenção,
Richard