Opentype 글꼴의 GPOS 테이블에서 커닝 쌍을 사용하고 추출하여 글리프를 Java에서 Path2D로 올바르게 표시하려면 어떻게해야합니까?
이 질문은 오래 전에 제기 된 몇 가지 질문과 관련이 있습니다. Java에서 Opentype 글꼴이 지원되지 않는다는 댓글을 보았지만 11 년 전이었습니다. 요즘은 그렇습니다. 유일한 문제는 커닝 쌍이 GPOS 테이블 에만 제공 된다는 것 입니다. 나는 그들이 거기에 있다는 것을 보았지만 코드가 올바른지 확인하기가 어렵습니다.
나는 현재 커닝 쌍까지 포인터를 따르려고 GPOS 테이블을 덤프하고 있습니다.
지금까지의 코드는 GPOS 테이블이 이전에 어레이에 복사 된 곳 gpos
입니다. 테이블을 덤프하는 기능은 dumpGPOS()
입니다. 내가하는 일이 올바른지, TODO 부분 을 코딩하는 방법을 확인하려면 도움이 필요합니다 .
byte[] gpos;
char[] hexasc( char[] hex, byte num ) {
int up = num >> 4;
int down = num & 15;
hex[0] = (char) ((up < 10)? '0' + up : 'A' + up - 10);
hex[1] = (char) ((down < 10)? '0' + down : 'A' + down - 10);
return hex;
}
char[] hex = { '0', '0' };
void printHex(byte b) {
hexasc(hex, b);
System.out.print(hex[0]);
System.out.print(hex[1]);
}
void dumpGPOS() {
int i, j;
System.out.println("GPOS header");
System.out.print("Version: ");
for ( i = 0; i < 4; i++ ) printHex(gpos[i]);
System.out.println(" (" + (gpos[0] << 8 | gpos[1]) + "." + (gpos[2] << 8 | gpos[3]) + ")" );
j = i;
System.out.print("TheScriptList: ");
for ( i = 4; i < 6; i++ ) printHex(gpos[i]);
System.out.println(" (" + (gpos[j] << 8 | gpos[j+1]) + ")" );
j = i;
System.out.print("TheFeatureList: ");
for ( i = 6; i < 8; i++ ) printHex(gpos[i]);
System.out.println(" (" + (gpos[j] << 8 | gpos[j+1]) + ")" );
j = i;
System.out.print("TheLookupList: ");
for ( i = 8; i < 10; i++ ) printHex(gpos[i]);
int lookup = (gpos[j] << 8 | gpos[j+1]);
System.out.println(" (" + lookup + ")" );
j = i;
System.out.println("Lookup List Table");
System.out.print("lookupCount: ");
for ( i = lookup; i <= lookup+1; i++ ) printHex(gpos[i]);
System.out.print('\n');
int count = (gpos[lookup] << 8 | gpos[lookup+1]);
int tab = lookup + 2;
int[] LookupList = new int[count];
for ( j = 0; j < count; j++ ) {
System.out.print("lookup[" + j + "] = ");
printHex(gpos[tab]);
printHex(gpos[tab + 1]);
System.out.println(" (" + ( LookupList[j] = (gpos[tab] << 8 | gpos[tab+1]) ) + ")" );
tab += 2;
}
int item, sub, size;
for ( j = 0; j < count; j++ ) {
item = lookup + LookupList[j];
System.out.println("Lookup [" + j + "]");
System.out.println("Lookup Type: " + (gpos[item] << 8 | gpos[item + 1]) );
System.out.print("Lookup flag: ");
printHex(gpos[item + 2]);
printHex(gpos[item + 3]);
size = (gpos[item + 4] << 8 | gpos[item + 5]);
System.out.println("\nNumber of subtables: " + size);
sub = item + 6;
int[] subTable = new int[size];
System.out.println("Subtable offsets");
for ( i = 0; i < size; i++ ) {
subTable[i] = (gpos[sub] << 8 | gpos[sub +1 ]);
sub += 2;
System.out.println( " " + subTable[i] );
}
for ( i = 0; i < size; i++ ) {
System.out.println("Subtable [" + i + "]");
sub = item + subTable[i];
printSubtable(sub);
}
}
}
void printSubtable(int sub) {
int format = gpos[sub] << 8 | gpos[sub + 1];
System.out.println("Format: " + format );
if (format == 1) {
/* TODO format 1*/
return;
}
/* TODO format 2*/
}
이 문제는 질문과도 관련이 있습니다. https://stackoverflow.com/questions/64761064/how-to-use-kerning-pairs-extracted-from-a-ttf-file-to-correctly-show-glyphs-as-p/64777046.
컨텍스트는 동일하지만 https://github.com/jaredrummler/TrueTypeParserOpentype 글꼴을 읽지 않으며 Opentype 글꼴이 GPOS 테이블 의 커닝 정보 만 사용 하므로 훨씬 더 어렵습니다.
Microsoft Opentype 참조를 사용하고 있지만 너무 모호하고 그림이 없으며 예제 코드가 없으며 예제가 충분하지 않습니다. 무엇 (자세한 힌트, 일부 도면, 코드 조각, 더 많은 예제가 될 수 특히에서 커닝 테이블을 추출하기위한 GPOS의 테이블을 )?
이 코드가 실제로 수행해야하는 작업을 수행하는지 100 % 확신 할 수있는 방법은 무엇입니까?
답변
문제 해결됨!
조언 : Java에서이 작업을 수행하려는 경우 시간이 낭비됩니다. 이 문제는https://github.com/opentypejs/opentype.js 및 사이트 https://opentype.js.org, 그러나 특히 https://opentype.js.org/glyph-inspector.html 과 https://opentype.js.org/font-inspector.html.
페이지 소스 에서 복사하여 붙여 넣어 두 코드를 모두 다운로드했습니다 . 그런 다음 수정했습니다.https://opentype.js.org/glyph-inspector.html일을하기 위해. 커닝 쌍을 얻으려면 Opentype.js 에 깊이 들어가야 하지만 모든 것이 있습니다 (아래 코드 확인).
결국 전체 프로그램을 JavaScript로 포팅했습니다. 이전에는 JavaScript를 너무 많이 프로그래밍하지 않았습니다. 따라서 이미 JavaScript로 프로그래밍 한 사람에게는 매우 쉬울 것입니다. 이 프로그램은 글리프, 커닝 쌍 및 너비 ( 각 글리프 의 advanceWidth) 로 Java 클래스를 생성합니다 .
다음은 결과를 보여주는 이미지입니다.

JavaScript의 아래 코드는 GPOS 커닝 테이블을 문자열로 덤프하여 text
각 행에 두 번째 문자와 kern 값을 포함하는 세트 목록을 표시하고 쌍의 첫 번째 문자는 행 시작 부분에 Java 문자로 나타납니다. 문자의 ASCII 코드를 사용하면 글리프 인덱스를 피할 수 있습니다.
이것은 ' '
(공백)을으로 만 덤프 '}'
하며 이는 영어에 유용합니다. 다른 문자로 확장하려면 유니 코드를 사용하십시오.
<!-- indoc: true -> writes in HTML on this page -->
const indoc = true;
const nl = (indoc) ? "<br>" : "\n";
var chars = font.tables.cmap.glyphIndexMap;
var g1,g2;
var i, j, kern;
var text = "";
for ( i = 32; i < 126; i++ ) {
g1 = font.glyphs.get(chars[i]);
text += "'" +
(( i == 39 || i == 92) ? "\\" + String.fromCharCode(i) : String.fromCharCode(i) )+ "'";
for ( j = 32; j < 126; j++ ) {
g2 = font.glyphs.get(chars[j]);
if ( (kern = font.getKerningValue(g1,g2)) !== 0 ) {
text += ", { '" +
(( j == 39 || j == 92) ? "\\" + String.fromCharCode(j) : String.fromCharCode(j) )+
"', " + kern + " }";
}
}
text += nl;
}