필기체 인식 API

커뮤니티 그룹 초안 보고서,

이 버전:
https://wicg.github.io/handwriting-recognition/
편집자:
Jiewei Qian (Google)
참여:
GitHub WICG/handwriting-recognition (새 이슈, 열린 이슈)
커밋:
GitHub spec.bs 커밋

요약

필기체 인식 API는 기존 운영 체제의 기능을 활용하여 웹 애플리케이션이 필기한 텍스트를 인식할 수 있도록 합니다.

이 문서의 상태

이 명세는 웹 플랫폼 인큐베이터 커뮤니티 그룹에서 발행하였습니다. W3C 표준이 아니며 W3C 표준화 트랙에도 포함되어 있지 않습니다. W3C 커뮤니티 기여자 라이선스 계약(CLA)에 따라 일부 옵트아웃이 제한되며 기타 조건이 적용됩니다. W3C 커뮤니티 및 비즈니스 그룹에 대해 자세히 알아보세요.

1. 소개

필기 입력은 그림입니다. 그림은 사람의 펜팁 움직임을 디지털로 재현하는 데 필요한 정보를 담고 있습니다.

여기서 제안하는 API는 운영 체제의 기능을 웹에 노출하는 것을 목표로 합니다. 필기체 인식 능력은 운영 체제에 따라 달라질 수 있으므로, API는 운영 체제별 기능과 쉽게 통합될 수 있도록 유연한 설계를 목표로 합니다.

사용자 에이전트는 이 명세에 정의된 웹 API 데이터 구조를 호스트 운영 체제에서 사용 가능한 구조로 변환하여 웹 API와 운영 체제 API를 연결해야 합니다.

이 API는 모든 플랫폼에서 동일하게 동작하는 인식을 정의하지 않습니다.

1.1. 정의

이 명세에서는 다음 개념을 정의합니다. 손글씨 "WEB"을 예로 듭니다:

Handwriting Concepts

필기체 인식기(handwriting recognizer)는 인터페이스(일반적으로 외부 애플리케이션이나 서비스에 의해 구현됨)로서 다음을 수행:

필기체 인식기의 정의는 사용자 에이전트의 재량입니다.

데이터를 필기체 인식기에 적합한 포맷으로 변환할 때, 사용자 에이전트는 이 명세에서 정의한 내용을 인식기에서 사용하는 개념과 일치시켜야 합니다.

운영 체제에서 사용 가능한 일부 필기체 인식기는 다음과 같습니다:

필기체 인식기는 웹 애플리케이션이 필기 데이터를 더 잘 처리할 수 있도록 추가 정보를 출력할 수 있습니다(예: 손글씨에서 문자 삭제).

분절(Segmentation)은 그래핌(사용자 인지 문자)을 그 구성 획 및 점에 매핑합니다. 하나의 그래핌은 여러 개의 유니코드 코드포인트를 포함할 수 있습니다.

x는 하나의 UTF-16 코드포인트(\u0078)로 구성됨
g̈는 두 개의 UTF-16 코드포인트(\u0067\u0308)로 구성됨.
षि는 두 개의 UTF-16 코드포인트(\u0937\u093f)로 구성됨.

예시: 손글씨 "int"

Handwriting Segmentation

애플리케이션에서 문자 "t"를 삭제하고 싶다면, 획 3, 4를 그림에서 제거하면 됩니다.

2. API 표현

작업 소스(task source)는 이 명세서에서 필기체 인식 작업 소스(handwriting recognition task source)입니다.

알고리즘이 필기체 인식 API 작업T를 큐에 넣을 때, 사용자 에이전트는 전역 작업(global task) T필기체 인식 작업 소스글로벌 객체(global object)와 함께 현재 realm 레코드에 큐잉해야 합니다.

별도 지정이 없으면, 알고리즘 단계에 의해 생성된 JavaScript 객체의 realm현재 realm 레코드입니다.

3. 기능 질의

기능 질의 인터페이스는 웹 애플리케이션이 구현체별 기능을 질의할 수 있도록 하여, 해당 기능을 사용할지 결정할 수 있습니다.

const modelConstraint = { languages: ['zh-CN', 'en'] };
const modelDesc = await navigator.queryHandwritingRecognizer(modelConstraint);

// \`modelDesc\`는 \`modelConstraint\`에 부합하는 필기체 인식기를 설명합니다.
// 조건을 충족하지 못하면, \`modelDesc\`는 null이 됩니다.
{
  textAlternatives: true,
  textSegmentation: true,
  hints: {
    alternatives: true,
    textContext: true,
    inputTypes: ['mouse', 'touch', 'stylus']
  }
}
[SecureContext]
partial interface Navigator {
  Promise<HandwritingRecognizerQueryResult?>
      queryHandwritingRecognizer(HandwritingModelConstraint constraint);
};

dictionary HandwritingModelConstraint {
  required sequence<DOMString> languages;
};

dictionary HandwritingRecognizerQueryResult {
  boolean textAlternatives;
  boolean textSegmentation;
  HandwritingHintsQueryResult hints;
};

dictionary HandwritingHintsQueryResult {
  sequence<HandwritingRecognitionType> recognitionType;
  sequence<HandwritingInputType> inputType;
  boolean textContext;
  boolean alternatives;
};

enum HandwritingRecognitionType{
  "text", "per-character"
};

enum HandwritingInputType {
  "mouse", "stylus", "touch"
};

3.1. queryHandwritingRecognizer(constraint)

이 메서드는 웹 애플리케이션이 기본 인식기의 기능을 질의하여 해당 인식기를 사용할지 결정할 수 있도록 합니다:

동일한 HandwritingModelConstraintcreateHandwritingRecognizer(constraint) 를 통해 해당 조건을 만족하는 HandwritingRecognizer 를 생성할 때 사용할 수 있습니다.

queryHandwritingRecognizer(constraint) 메서드가 호출될 때, 다음을 수행:
  1. constraintlanguages 멤버를 포함하지 않으면, 거부된 promiseTypeError와 함께 반환합니다.

  2. p새 promise로 설정합니다.

  3. 다음 단계를 병렬로 실행합니다:

    1. constraint를 필기체 인식기에 적합한 형태로 변환

    2. 아래 중 하나라도 해당되면:

      • constraintlanguages 멤버가 빈 리스트일 때.

      • 사용자 에이전트가 변환된 constraint를 만족하는 플랫폼별 필기체 인식기를 찾거나 생성할 수 없는 경우.

      필기체 인식 API 작업 queuepnull로 resolve하고 나머지 단계를 중단.

    3. 그 외의 경우, 필기체 인식 API 작업 queue에 아래를 수행:

      1. resultHandwritingRecognizerQueryResult를 새로 만듦

      2. 인식기의 기능 설명 변환result에 모든 멤버를 채움.

      3. presult로 resolve.

  4. p를 반환합니다.

HandwritingRecognizerQueryResultHandwritingHintsQueryResult로 변환할 때 구현체는 아래 규칙을 따라야 합니다:

3.2. HandwritingModelConstraint 속성

이것은 하위 필기체 인식기(생성될 경우)가 반드시 만족해야 하는 제약조건을 설명합니다.

이는 createHandwritingRecognizer(constraint)로 필기체 인식기를 생성할 때도 사용됩니다.

languages
[BCP47] 언어 태그의 리스트로, 인식기가 인식해야 할 언어를 설명합니다.

둘 이상의 언어가 제공되면 인식기는 모든 언어를 인식해야 제약조건을 만족합니다.

사용자 에이전트는 주어진 언어 태그의 모든 스크립트를 고려해야 합니다. 예를 들어, 라틴 알파벳("az-Latn")만 인식하는 인식기는 "az" 언어 태그에 적합하지 않습니다. 아제르바이잔어는 키릴 문자("az-Cyrl")로도 쓰일 수 있기 때문입니다.

스크립트 구분이 중요한 경우 가장 구체적인 언어 태그 사용을 권장합니다. (예: 라틴 문자 아제르바이잔어만 필요한 경우 "az-Latn" 사용)

어떤 인식기는 단일 언어만 지원할 수 있습니다. 상호 운용성을 위해 언어별 인식기를 각각 생성하는 것이 도움이 될 수 있습니다.

3.3. HandwritingRecognizerQueryResult 속성

이는 필기체 인식기 구현체의 내재적 기능을 설명합니다.

textAlternatives
구현체가 하나의 전사 대신 여러 개의 전사 문자열(transcriptions)을 반환하는지 여부를 나타내는 boolean.
textSegmentation
구현체가 각 전사에 분절(segmentation) 정보를 반환하는지 여부를 나타내는 boolean.
hints
HandwritingHintsQueryResult 객체로, startDrawing()에서 허용되는 힌트를 설명합니다.

3.3.1. HandwritingHintsQueryResult 속성

이 객체는 startDrawing() 시 선택적으로 제공할 수 있는 힌트의 집합을 설명합니다. 정확도 또는 성능을 높이는 데 도움이 됩니다.

일반적으로 이 속성의 이름은 startDrawing() 메서드에서 허용하는 이름과 일치합니다.

recognitionType
HandwritingRecognitionType 열거형의 리스트로, 그려질 텍스트의 유형을 설명합니다.

힌트를 제공하더라도 결과 전사가 힌트 설명을 반드시 따르는 것은 아닙니다.

"text"
자유 형식의 일반 텍스트. 그림이 실제 단어임을 의미합니다. 예: 일상적인 문장.
"per-character"
서로 연관 없는 개별 그래핌으로 필기가 구성됨. 예: 일련번호, 라이선스 키.
inputType
HandwritingInputType 열거형의 리스트로, 그림이 어떻게 입력되는지 설명합니다.
"touch"
손가락으로 그림.
"stylus"
스타일러스로 그림.
"mouse"
마우스 커서로 그림.
textContext
textContext가 허용되는지 나타내는 boolean. textContext는 사용자에게 보여지는 텍스트이거나, 현재 그림 앞에 위치하는 이전에 인식된 텍스트 문자열입니다.
alternatives
대체 전사의 개수 설정이 가능한지 나타내는 boolean. 이는 getPrediction에 반환되는 최대 대체값 개수를 제한합니다.

4. 필기체 인식기 생성

HandwritingRecognizer 는 인식을 수행하는 데 필요한 리소스를 관리합니다.

const modelConstraint = { languages: ['en'] };

try {
  const recognizer = await navigator.createHandwritingRecognizer(modelConstraint);
  // recognizer를 사용하여 인식 작업을 수행합니다.
} catch (err) {
  // 제공된 모델 제약조건을 만족할 수 없습니다.
}
[SecureContext]
partial interface Navigator {
  Promise<HandwritingRecognizer>
      createHandwritingRecognizer(HandwritingModelConstraint constraint);
};

4.1. createHandwritingRecognizer(constraint) 메서드

이 메서드는 제공된 HandwritingModelConstraint 를 만족하는 HandwritingRecognizer 객체를 생성하고 인식 수행에 필요한 리소스를 예약합니다. 이것은 필기체 인식기의 진입점을 나타냅니다.

사용자 에이전트가 필기체 모델 설치 및 다운로드를 사용자에게 요구할 수 있습니다. 웹 애플리케이션은 이 메서드가 항상 빠르게 resolve된다고 가정하면 안 됩니다.

createHandwritingRecognizer(constraint) 메서드가 호출되면, 다음을 수행합니다:
  1. constraintlanguages 멤버가 없다면, 거부된 promiseTypeError와 함께 반환한다.

  2. p새 promise로 둔다.

  3. 다음 단계를 병렬로 실행한다:

    1. constraint를 플랫폼에 종속적인 필기체 인식기 생성을 위해 적합한 형태로 변환한다.

    2. 필기체 인식 API 작업을 큐에 넣는다:

      1. 만약 사용자 에이전트가 인식을 수행할 필기체 인식기를 생성하거나 준비할 수 없는 경우, 실패 원인에 따라 새 예외로 p를 reject한다:

      2. 그 외의 경우:

        1. result를 새 HandwritingRecognizer 객체로 생성한다.

        2. result와 직전에 생성된 플랫폼 종속 필기체 인식기를 연결한다.

        3. result.active 플래그를 true로 설정한다.

        4. p를 result로 resolve한다.

  4. p를 반환한다.

5. 인식기 사용

const drawingHints = { textContext: "Hello world." }
const drawing = recognizer.startDrawing(textContext)

// drawing과 함께 작업합니다.

// recognizer와 연관된 리소스를 해제합니다.
recognizer.finish()

5.1. HandwritingRecognizer 객체

[Exposed=Window, SecureContext]
interface HandwritingRecognizer {
  HandwritingDrawing startDrawing(optional HandwritingHints hints = {});

  undefined finish();
};

dictionary HandwritingHints {
  DOMString recognitionType = "text";
  DOMString inputType = "mouse";
  DOMString textContext;
  unsigned long alternatives = 3;
};

HandwritingRecognizeractive 플래그(불리언)를 가집니다. active 플래그의 초기값은 true이며, finish() 호출 시점에 false가 됩니다.

인식기의 active 플래그가 true면, 웹 애플리케이션은 이 인식기와 연결된 새 drawing을 생성하고 인식 작업을 할 수 있습니다.

사용자 에이전트는 한 웹사이트의 active 필기체 인식기 개수에 제한을 둘 수 있습니다.

5.2. startDrawing(hints)

이 메서드는 이후 인식을 위한 drawing 정보를 저장하는 HandwritingDrawing 을 생성합니다.

HandwritingDrawingstrokes (획)의 리스트를 가지며, 초기값은 비어 있습니다.

HandwritingDrawingrecognizer를 가지며, 이 HandwritingDrawing을 생성한 HandwritingRecognizer 참조를 가집니다.

startDrawing(hints)가 호출되면, 다음을 수행:
  1. thisactive 플래그가 true가 아니면, 새로운 DOMException 객체를 발생시키고, 이 객체의 name 멤버를 "InvalidStateError"로 지정한 후 중단한다.

  2. 제공된 hints를 필기체 인식기에 적합한 포맷으로 변환한다.

  3. 새로운 HandwritingDrawingresult로 생성하고, 필요하면 변환된 힌트를 그 안에 저장한다.

  4. result.recognizerthis로 설정한다.

  5. this.strokes를 새로운 빈 list로 설정한다.

  6. result를 반환한다.

제공한 hints에 인식기가 지원하지 않는 기능이 포함되어 있으면, 해당 속성은 무시해야 합니다.

hints가 제공되지 않으면, 사용자 에이전트는 자체 기준으로 기본값을 적용할 수 있습니다.

user agent는 변환된 hints를 동등한 필기체 인식기 drawing 객체로 전달하고 HandwritingDrawing에 저장하지 않을 수도 있습니다.

5.3. finish()

이 메서드는 thisactive 플래그를 false로 설정하고, 할당된 필기체 인식기 리소스를 해제하며, 이후 this와 관련된 작업은 모두 실패하게 만듭니다.

  1. this.activetrue가 아니면 중단.

  2. this.activefalse로 설정합니다.

사용자 에이전트는 필기체 인식기와 연관된 리소스를 해제해야 합니다.

finish() 호출 후에는 getPrediction()HandwritingDrawing (이 this로 생성된)에 호출하면 실패하게 됩니다.

6. HandwritingDrawing 빌드

HandwritingDrawing 은 drawing의 컨텍스트 정보를 관리하며, drawing을 구성하는 획과 점을 관리합니다. drawing을 나타냅니다.

user agent는 모든 stroke와 point를 메모리에 저장한 뒤, getPrediction() 호출 시 필기체 인식기에 적합한 포맷으로 변환할 수 있습니다. 이 경우 HandwritingDrawingHandwritingStroke 는 리스트처럼 동작합니다.

또는, user agent는 각 stroke와 point를 즉시 변환해 필기체 인식기에 전달할 수 있습니다. 각 HandwritingStrokeHandwritingDrawing 메서드 호출 시 플랫폼별 객체로 감싸 동작합니다.

user agent는 drawing의 변화를 추적해 getPrediction() 성능을 높일 수 있습니다. 관련 메서드 호출 시 해당 stroke를 "수정됨"으로 표시해 변화 추적이 가능합니다. 변화 추적을 통해 인식기의 증분 인식을 수행할 수 있습니다.

예시: 세 문단으로 구성된 drawing에서 각 문단 예측값을 저장한 뒤, 웹 애플리케이션이 세 번째 문단에 stroke를 추가하면, 변화 추적으로 변경된 부분만 새로 인식하고 결과를 병합할 수 있습니다.

const handwritingStroke = new HandwritingStroke()

// 획에 점을 추가합니다.
handwritingStroke.addPoint({ x: 1, y: 2, t: 0});
handwritingStroke.addPoint({ x: 7, y: 6, t: 33});

// 이 획의 점 리스트를 조회합니다.
// 모든 점의 복사본을 반환하며, 반환된 점을 수정해도 원본에는 영향이 없습니다.
const points = handwritingStroke.getPoints();

[ { x: 1:, t:2, t: 0 }, { x: 7, y: 6, t: 33} ];

// 획의 모든 점을 삭제합니다.
handwritingStroke.clear();

// drawing에 획을 추가합니다.
drawing.addStroke(handwritingStroke);

// drawing의 모든 획을 조회합니다.
// drawing에 포함된 HandwritingStroke 리스트를 반환합니다. 웹 앱은
// stroke를 직접 수정할 수도 있습니다. 예: HandwritingStroke.addPoint() 호출.
drawing.getStrokes();

[ HandwritingStroke, /* ... */ ]

// drawing에서 한 획을 삭제합니다.
drawing.removeStroke(handwritingStroke);

// drawing의 모든 획을 삭제합니다.
drawing.clear();
[Exposed=Window, SecureContext]
interface HandwritingDrawing {
  undefined addStroke(HandwritingStroke stroke);
  undefined removeStroke(HandwritingStroke stroke);
  undefined clear();
  sequence<HandwritingStroke> getStrokes();

  Promise<sequence<HandwritingPrediction>> getPrediction();
};

[SecureContext, Exposed=Window]
interface HandwritingStroke {
  constructor();
  undefined addPoint(HandwritingPoint point);
  sequence<HandwritingPoint> getPoints();
  undefined clear();
};

dictionary HandwritingPoint {
  required double x;
  required double y;

  // Optional. Number of milliseconds since a reference time point for a
  // drawing.
  DOMHighResTimeStamp t;
};
웹 애플리케이션이 tHandwritingPoint에 제공한다면, 한 HandwritingDrawing에 대해 모든 t 값은 하나의 기준시점에서부터 측정해야 합니다.

예: t === 0startDrawing() 호출시의 시점으로 정의. 혹은 Date.now()를 사용해 수집할 때(예: touchmove 이벤트 발생 시) 기준으로 할 수 있습니다.

6.1. HandwritingStroke

HandwritingStroke획(stroke)을 나타냅니다. 하나의 움직임을 재현하는 데 필요한 정보를 저장합니다.

HandwritingStrokePoints라는 리스트를 가지며, 이 stroke의 점(point)을 저장합니다. points 의 초기값은 비어 있습니다.

6.1.1. HandwritingStroke()

  1. HandwritingStroke 객체 result를 만듭니다.

  2. resultpoints를 비어 있는 리스트로 설정합니다.

  3. result를 반환합니다.

6.1.2. addPoint(point)

이 메서드는 점(point)this에 추가합니다. 호출 시 다음을 수행:
  1. pointx 멤버가 없으면, 새로운 TypeError 를 throw하고 중단한다.

  2. point.x가 숫자가 아니면, 새로운 TypeError 를 throw하고 중단한다.

  3. pointy 멤버가 없으면, 새로운 TypeError 를 throw하고 중단한다.

  4. point.y가 숫자가 아니면, 새로운 TypeError 를 throw하고 중단한다.

  5. pointt 멤버가 있고, t가 숫자가 아니면 새로운 TypeError 를 throw하고 중단한다.

  6. p를 새로운 객체로 둔다.

  7. p.xpoint.x를 할당한다.

  8. p.ypoint.y를 할당한다.

  9. pointt 멤버가 있으면, p.tpoint.t를 할당한다.

  10. p this.points에 추가한다.

pointt가 없다면, 구현체가 보간(interpolate)하거나 기본값을 사용해서는 안 됩니다. p.t에 값이 없음을 그대로 반영해야 합니다.

addPoint(point) 호출 후 point를 수정해도 HandwritingStroke에는 영향을 주지 않습니다.

6.1.3. getPoints()

이 메서드는 이 획의 점들을 반환합니다.

이 메서드가 호출되면:
  1. result를 새 빈 리스트로 설정합니다.

  2. pointsp로 반복합니다.

    1. object pt를 만듭니다.

    2. pt.x 멤버에 p.x를 설정합니다.

    3. pt.y 멤버에 p.y를 설정합니다.

    4. pt 멤버가 있으면, pt.tp.t를 설정합니다.

    5. ptresult에 추가합니다.

  3. result를 반환합니다.

깊은 복사(Deep copy)는 내부 points의 수정 방지 및 변경 추적을 가능하게 합니다.

getPoints() 반환값을 수정해도 stroke에는 영향이 없습니다.

6.1.4. clear()

이 메서드는 이 획의 모든 점을 삭제하여, 이 획을 빈 상태로 만듭니다.

이 메서드가 호출되면,
  1. this.points를 비웁니다.

6.2. HandwritingDrawing

6.2.1. addStroke(stroke)

  1. strokeHandwritingStroke 인스턴스가 아니면, TypeError를 throw하고 중단합니다.

  2. stroke 참조를 this.strokes에 추가합니다.

6.2.2. removeStroke(stroke)

  1. strokeHandwritingStroke 인스턴스가 아니면, TypeError를 throw하고 중단합니다.

  2. this.strokes에서 해당 객체가 stroke와 동일한 항목을 제거합니다.

6.2.3. getStrokes()

이 메서드는 이 drawing에 포함된 획(stroke) 리스트를 반환합니다.

이 메서드가 호출되면,
  1. result를 새 빈 리스트로 설정합니다.

  2. strokess로 반복하여,

    1. s를 result에 추가합니다.

  3. result를 반환합니다.

6.2.4. clear()

  1. this.strokes를 비웁니다.

7. HandwritingDrawing의 예측값 얻기

// 이 drawing에 포함된 획들의 예측값을 구합니다.
const predictions = await drawing.getPrediction();

// \`predictions\`는 HandwritingPrediction 리스트이며,
// 각 예측값의 속성은 handwriting recognizer의 기능과,
// queryHandwritingRecognizer()에서 반환한 모델 descriptor에 따라 다릅니다.
//
// 예시: recognizer가 textSegmentation을 지원하는 경우.
[
  {
    text: "hello",
    segmentationResult: [
      {
        grapheme: "h", beginIndex: "0", endIndex: "1",
        drawingSegments: [
          { strokeIndex: 1, beginPointIndex: 0, endPointIndex: 32 },
          { strokeIndex: 2 , beginPointIndex: 0, endPointIndex:: 40 },
        ]
      },
      {
        grapheme: "2", beginIndex: "1", endIndex: "2",
        drawingSegments: [
          { strokeIndex: 2 , beginPointIndex: 41, endPointIndex:: 130 },
        ]
      },
      // ...
    ]
  },
  {
    text: "he11o",
    segmentationResult: [ /* ... */ ]
  },
  // startDrawing()의 alternatives 값 만큼 최대 결과가 반환될 수 있습니다.
];

7.1. getPrediction()

getPrediction() 메서드는 this drawing의 예측값들과 그 메타데이터 리스트를 반환합니다.

예측값들은 신뢰도(confidence) 순으로 내림차순 정렬되어 있습니다. 비어 있지 않다면 첫 번째 값이 가장 가능성이 높은 결과여야 합니다.

필기체 인식기가 어떤 것도 인식하지 못한 경우 getPrediction() 는 빈 리스트로 resolve되어야 합니다.

사용자 에이전트는 change tracking 및 증분 인식을 통해 성능을 개선할 수 있습니다.

getPrediction() 이 호출되면:
  1. this.recognizer.active 가 true가 아니면 InvalidStateError DOMException과 함께 reject된 promise를 반환합니다.

  2. this.strokes가 비어 있으면 새 빈 리스트로 resolve된 promise를 반환합니다.

  3. this drawing을 필기체 인식기에 적합한 포맷으로 변환합니다.

  4. p를 새 Promise로 두고, 다음 단계를 병렬로 실행합니다.

    1. 변환된 drawing을 필기체 인식기로 전송합니다.

    2. 필기체 인식기가 예측 결과를 반환할 때까지 대기합니다.

    3. 필기체 인식 API 작업 queue에 아래 단계 수행:

      1. result를 리스트로 둡니다.

      2. 반환된 예측 pred 각각에 대해 반복:

        1. pred를 HandwritingPrediction으로 변환하여 idl_pred에 저장합니다.

        2. idl_pred를 result에 추가합니다.

      3. presult로 resolve합니다.

  5. p를 반환합니다.

7.2. HandwritingPrediction 속성

HandwritingPrediction필기체 인식기의 예측 결과를 나타냅니다.

dictionary HandwritingPrediction {
  required DOMString text;
  sequence<HandwritingSegment> segmentationResult;
};

dictionary HandwritingSegment {
  required DOMString grapheme;
  required unsigned long beginIndex;
  required unsigned long endIndex;
  required sequence<HandwritingDrawingSegment> drawingSegments;
};

dictionary HandwritingDrawingSegment {
  required unsigned long strokeIndex;
  required unsigned long beginPointIndex;
  required unsigned long endPointIndex;
};
text
drawing의 전사(transcription)를 나타내는 DOMString입니다.
segmentationResult
인식된 그래핌(사용자가 인지하는 글자)을 해당 획과 점에 매핑하는 HandwritingSegment 리스트입니다.

필기체 인식기가 텍스트 분절(segmentation)을 지원하지 않는 경우 null입니다.

웹 애플리케이션은 textSegmentation 으로 이 속성이 null일지 여부를 확인할 수 있습니다.

7.2.1. HandwritingSegment 속성

HandwritingSegment 는 drawing에서 분절(segmentation)된 단일 그래핌에 대해 설명합니다.

grapheme
그래핌을 나타내는 DOMString입니다.
beginIndex
이 그래핌이 text에서 시작하는 인덱스입니다.
endIndex
이 그래핌이 text에서 끝나는 인덱스(다음 그래핌이 시작하는 인덱스)입니다.
drawingSegments
이 그래핌을 구성하는 drawing의 일부를 설명하는 HandwritingDrawingSegment 리스트입니다.

textbeginIndexendIndex로 자르면 grapheme이 나와야 합니다.

// 웹 애플리케이션은 \`text\`를 \`beginIndex\`, \`endIndex\`로 슬라이스할 수 있습니다.
// 예시: "घोषित"의 \`HandwritingPrediction\`

const prediction = {
  // UTF-16 code points: \u0918 \u094b \u0937 \u093f \u0924
  // Graphemes: घो, षि, त
  text: "घोषित",
  segmentationResult: [
    { grapheme: "घो", beginIndex: "0", endIndex: "2" },
    { grapheme: "षि", beginIndex: "2", endIndex: "4" },
    { grapheme: "त", beginIndex: "4", endIndex: "5" },
  ]
}

// 아래는 true입니다:
prediction.text.slice(0, 1) === "घो";
prediction.text.slice(2, 4) === "षि";
prediction.text.slice(4, 5) === "त";

// 웹 애플리케이션은 splice로 두 번째 그래핌(षि)만 삭제할 수 있습니다.
const withoutSecondGrapheme = (
  [...prediction.text]
    .splice(
      prediction.segmentationResult[1].beginIndex,
      prediction.segmentationResult[1].endIndex
    )
    .join('')
);
// => "घोत"

7.2.2. HandwritingDrawingSegment 속성

HandwritingDrawingSegmentHandwritingStroke의 연속된 구간을 설명합니다.

이 속성들은 HandwritingStrokeHandwritingDrawinggetPrediction()이 호출될 시점의 정보에 기반합니다.

strokeIndex
HandwritingStrokeHandwritingDrawing.strokes에 저장된 인덱스입니다.
beginIndex
drawing segment가 시작하는 인덱스입니다.
endIndex
drawing segment가 끝나는 인덱스(다음 drawing segment가 시작되는 위치)입니다.

8. 프라이버시 고려 사항

이 섹션은 규범적이지 않습니다.

지문 벡터는 기능 탐지와 인식기 구현의 두 부분에서 비롯됩니다.

노출되는 정보(엔트로피)의 양은 사용자 에이전트의 구현에 달려 있습니다. 만능 솔루션은 없다고 생각하며, 사용자 에이전트가 사용자에게 개인정보 보호(예: 퍼미션 프롬프트)가 필요한지 여부를 스스로 결정할 것을 권장합니다.

기능 탐지는 다음과 같은 정보를 노출할 수 있습니다:

지문 채취는 아래와 같이 완화할 수 있습니다:

인식기 구현은 운영체제, 디바이스, 사용자의 습관 등 정보를 노출할 수 있습니다. 이는 주로 사용하는 인식 기술에 따라 다릅니다.

아래는 여러 인식기 유형 및 그에 따른 위험 예시입니다:

하지만 현존 구현 가운데 이 유형이 사용된 사례는 인지하지 못했습니다. 이런 모델에는 반드시 프라이버시 보호 조치를 권고하며, 세션마다 초기화(클린 스테이트)를 권장합니다.

지문 채취 비용: 공격자는 모델별 차이점을 활용하기 위해 다양한 필기 drawing(적대적 샘플)을 신중히 생성해야 합니다. 이 샘플 생성 비용이 크겠지만, 동기가 확실한 공격자는 그런 샘플을 충분히 확보할 수 있다고 보는 것이 안전합니다.

색인

이 명세에서 정의된 용어

참조에 의해 정의된 용어

참조

표준 참조 문헌

[BCP47]
A. Phillips, Ed.; M. Davis, Ed.. Tags for Identifying Languages. September 2009. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc5646
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

IDL 색인

[SecureContext]
partial interface Navigator {
  Promise<HandwritingRecognizerQueryResult?>
      queryHandwritingRecognizer(HandwritingModelConstraint constraint);
};

dictionary HandwritingModelConstraint {
  required sequence<DOMString> languages;
};

dictionary HandwritingRecognizerQueryResult {
  boolean textAlternatives;
  boolean textSegmentation;
  HandwritingHintsQueryResult hints;
};

dictionary HandwritingHintsQueryResult {
  sequence<HandwritingRecognitionType> recognitionType;
  sequence<HandwritingInputType> inputType;
  boolean textContext;
  boolean alternatives;
};

enum HandwritingRecognitionType{
  "text", "per-character"
};

enum HandwritingInputType {
  "mouse", "stylus", "touch"
};

[SecureContext]
partial interface Navigator {
  Promise<HandwritingRecognizer>
      createHandwritingRecognizer(HandwritingModelConstraint constraint);
};

[Exposed=Window, SecureContext]
interface HandwritingRecognizer {
  HandwritingDrawing startDrawing(optional HandwritingHints hints = {});

  undefined finish();
};

dictionary HandwritingHints {
  DOMString recognitionType = "text";
  DOMString inputType = "mouse";
  DOMString textContext;
  unsigned long alternatives = 3;
};

[Exposed=Window, SecureContext]
interface HandwritingDrawing {
  undefined addStroke(HandwritingStroke stroke);
  undefined removeStroke(HandwritingStroke stroke);
  undefined clear();
  sequence<HandwritingStroke> getStrokes();

  Promise<sequence<HandwritingPrediction>> getPrediction();
};

[SecureContext, Exposed=Window]
interface HandwritingStroke {
  constructor();
  undefined addPoint(HandwritingPoint point);
  sequence<HandwritingPoint> getPoints();
  undefined clear();
};

dictionary HandwritingPoint {
  required double x;
  required double y;

  // Optional. Number of milliseconds since a reference time point for a
  // drawing.
  DOMHighResTimeStamp t;
};

dictionary HandwritingPrediction {
  required DOMString text;
  sequence<HandwritingSegment> segmentationResult;
};

dictionary HandwritingSegment {
  required DOMString grapheme;
  required unsigned long beginIndex;
  required unsigned long endIndex;
  required sequence<HandwritingDrawingSegment> drawingSegments;
};

dictionary HandwritingDrawingSegment {
  required unsigned long strokeIndex;
  required unsigned long beginPointIndex;
  required unsigned long endPointIndex;
};