Opentype 글꼴의 GPOS 테이블에서 커닝 쌍을 사용하고 추출하여 글리프를 Java에서 Path2D로 올바르게 표시하려면 어떻게해야합니까?

Nov 14 2020

이 질문은 오래 전에 제기 된 몇 가지 질문과 관련이 있습니다. 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 % 확신 할 수있는 방법은 무엇입니까?

답변

JackLondon Nov 16 2020 at 20:12

문제 해결됨!

조언 : Java에서이 작업을 수행하려는 경우 시간이 낭비됩니다. 이 문제는https://github.com/opentypejs/opentype.js 및 사이트 https://opentype.js.org, 그러나 특히 https://opentype.js.org/glyph-inspector.htmlhttps://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;
}