MyScript 開發文檔

一、IInk SDK runtime

1.1 引擎創建

iink SDK 運行時由一個Engine對象表示。

此對象將允許您創建其他關鍵對象、配置識別和微調 SDK 行爲。

它通過類的create()靜態方法實例化Engine:

在 java 中,我們強烈建議將引擎創建包裝成單例

import com.myscript.certificate.MyCertificate;
import com.myscript.iink.Engine;

public class IInkApplication extends Application
{
  private static Engine engine;

  public static synchronized Engine getEngine()
  {
    if (engine == null)
    {
      engine = Engine.create(MyCertificate.getBytes());
    }

    return engine;
  }
}

1.2 對象釋放

在關閉應用程序或打開新包之前,請確保首先釋放對相應對象的所有引用。如果不這樣做,您可能會遇到意外行爲,因爲仍會分配本機資源。

此資源管理必須應用於實現該IAutoCloseable接口的所有對象:ContentPackage、ContentPart、 Editor、Engine、ParameterSet、 RecognitionAssetsBuilder和Renderer。

要強制立即釋放本機資源,您必須顯式調用close相應對象的方法。通過將它們的引用也設置爲 null,確保不要在其他地方引用它們。

// close contentPart and set its reference to null
if (contentPart != null)
{
  contentPart.close();
  contentPart = null;
}

// then close contentPackage and set its reference to null
if (contentPackage != null)
{
  contentPackage.close();
  contentPackage = null;
}

1.3 獲取並設置配置

配置

必須配置一個新引擎,讓 iink SDK 知道要識別哪種內容。

作爲一個靈活的工具包,iink SDK 還附帶了許多配置參數。雖然默認值在大多數情況下都有意義,但有時可能需要對其進行配置以滿足您的需要。

訪問配置

可以通過調用 getConfiguration() 引擎對象來獲取配置。

可以將實現 IConfigurationListener 接口的偵聽器附加到給定的配置對象,以便在進行配置更改時得到通知。

配置識別

要識別任何語言的數學、圖表或文本,需要創建引擎並指向所需的資產。

資產包由一組配置文件( *.conf)組成,這些 文件指定所需的識別參數,這些參數組合到稱爲資源文件( *.res) 的二進制文件中。資源文件包含引擎識別所需的所有內容。

示例一,配置中文:
將中文語言配置文件下載下來,防止到對應文件夾:

import com.myscript.iink.Configuration;
import com.myscript.iink.Engine;
import java.io.File;

...
Engine engine = IInkApplication.getEngine();

// configure recognition
Configuration conf = engine.getConfiguration();
conf.setStringArray("configuration-manager.search-path", new String[] { "zip://" + getPackageCodePath() + "!/assets/conf" });
conf.setString("lang", "zh_CN");

配置數學計算結果浮點精度爲2位(默認位3):

import com.myscript.iink.Configuration;
import com.myscript.iink.Engine;
import java.io.File;

public class Calculator
{

  private Engine engine;

  /**
   * @param packageCodePath when used from an AppCompatActivity object, getPackageCodePath() output.
   * @param filesDirPath when used from an AppCompatActivity object, getFilesDir().getPath() output.
   */
  public Calculator(String packageCodePath, String filesDirPath)
  {

    //Creating engine and accessing the configuration object
    engine = IInkApplication.getEngine();
    Configuration configuration = engine.getConfiguration();

    // set the recognition configuration
    configuration.setStringArray("configuration-manager.search-path", new String[]{"zip://" + packageCodePath + "!/assets/conf"});

    // set the temporary directory
    String tempDir = filesDirPath + File.separator + "tmp";
    configuration.setString("content-package.temp-folder", tempDir);

    // set the math fractional part precision
    configuration.setNumber("math.solver.fractional-part-digits", 2);
  }
}

二、文件存儲

2.1 支持的內容的類型

Interactive Ink SDK 目前支持以下內容類型:

  • 文本( "Text") - 簡單的多行文本塊(包括可能的換行符),響應式迴流。它默認顯示參考線,但可以停用這些參考線(例如,當處理來自外部來源的未在參考線上對齊的墨水時)。

  • Math ( "Math") - 支持一個或多個方程的塊

  • 圖表( "Diagram") - 支持圖表的塊,具有自動文本/非文本區分和動態重組的可能性。

  • 繪圖( "Drawing") - 塊託管一組筆畫,無需任何解釋。適用於塗鴉和繪畫活動。

  • 文本文檔( "Text Document") - 託管文本、數學、圖表、原始內容和繪圖塊的有序集合的容器。它是一個垂直、動態和響應式的內容流。

  • 原始內容( "Raw Content") - 阻止託管原始數字墨水,沒有明確分割爲文本、數學、圖表或 iink SDK 已知語義的其他項目。iink SDK 分析內容以從其餘內容中檢索與文本塊對應的墨跡。在原始數字墨水上實現墨水搜索功能是關鍵。您可以通過 JIIX 導出獲取此信息。在非文本塊中,根據其配置,iink SDK 可以進一步區分形狀與其他塊。通過自定義樣式,您可以在編寫此分類時立即獲得反饋。

括號之間提供的字符串值區分大小寫,並以明確的方式標識模型中的類型。

2.2 模型結構

ContentPackage
ContentPackage 可以理解爲一個儲存墨水的容器,是 Part 的有序集合。它可以保存爲文件系統上的一個文件,然後在用戶之間共享或者重新加載。

ContentPart
ContentPart 對應於一個獨立的內容單元,可以唄 iink SDK 處理。每個 part 都有一個特定的類型。對應於其根塊的類型,可以通過其 getType() 方法檢索。

可以通過調用 getPartCount() 來獲取單個 package 的 part 數量,getPart()並將其傳遞給要加載的部件的基於 0 的索引。

ContentBlock
ContentBlock 稱之爲一個內容塊,通常對應於一個可操作的語義單元。

儘管一個 part 有時承載單個 ContentBlock ,但這兩個概念並不等效。一個 ContentPart 對應一個序列化單元,而一個 ContentBlock 對應一個可操作的語義單元。

2.3 ContentPackage 的相關操作

2.3.1 臨時文件夾

MyScript iink SDK 也需要對文件系統上的至少一個文件夾具有讀/寫訪問權限:臨時文件夾,它將輸出正在處理的中間文件。默認情況下,iink SDK 使用包文件所在的文件夾。某些平臺強制要求設置臨時文件夾,可以通過設置 content-package.temp-folder 來更改臨時文件夾。

2.3.2 創建和加載 ContentPackage

調用engine.openPackage()接口,有如下重載接口

ContentPackage openPackage(java.io.File file)
Opens the specified package using the EXISTING package open option.

ContentPackage openPackage(java.io.File file, PackageOpenOption openOption)
Opens the specified package.

ContentPackage openPackage(java.lang.String path)
Opens the specified package using the EXISTING package open option.

ContentPackage openPackage(java.lang.String path, PackageOpenOption openOption)
Opens the specified package.

其中 PackageOpenOption 有如下選項:

  • EXISTING 打開現有包並在它不存在時失敗(默認行爲)。
  • CREATE 打開現有包或創建它,如果它不存在。
  • CREATE_NEW 確保創建和打開以前不存在的包。
  • TRUNCATE_EXISTING 創建並打開一個新包,覆蓋同一位置的任何預先存在的包。

要創建新的包,也可以調用 engine.createPackage(),等同於 engine.openPackage() 使用 CREATE_NEW 選項。

2.3.3 保存 ContentPackage

Interactive Ink SDK 提供了兩種不同的方法來保存內容: save()saveToTemp() 兩個方法都是需要在 ContentPackage 實例上調用。

  • save()將所有數據序列化到以包命名的 zip 存檔中,無論這些數據是在內存中還是已卸載到臨時文件夾中。生成的文件是獨立的,可以在以後重新加載。由於壓縮,這種方法相當慢。
  • saveToTemp()將加載到內存中的內容保存到臨時文件夾中。由於它只寫入給定時間點內存中的內容並且不需要壓縮,因此調用此方法要快得多。如果 iink SDK 沒有保存到壓縮存檔而被迫退出,它可以讓 iink SDK 恢復此類數據。

2.3.4 刪除 ContentPackage

調用 deletePackage() 刪除 ContentPackage,刪除之前需要確保釋放對這個包的所有引用,包括你通過顯示調用它們各自的 close() 方法打開的部分。

2.4 ContentPart 的相關操作

2.4.1 創建 Contentpart

ContentPart contentPackage.createPart(java.lang.String type)
創建根塊,需要傳入內容的類型。

contentPackage.clonePart(ContentPart part)
克隆一個已經存在的 ContentPart 添加到當前的 ContentPackage。

要將部件從一個包“移動”到另一個包,首先將其克隆到新包中,然後從原始包中刪除該部件。

2.4.2 獲取 ContentPart

contentPackage.getPart(int index)
contentPackage.getPart(String id)
傳入Part 的索引或者 id

2.4.3 刪除 ContentPart

void contentPackage.removePart(ContentPart part)
刪除一個 ContentPart

2.5 元數據 Metadata

您可以將元數據附加到ContentPart和ContentPackage對象,它們將與文件一起序列化。這可以證明對於存儲客戶端特定的參數很有用。

使用setMetadata()和getMetadata()分別存儲和檢索附加到對象的元數據。

// Retrieve the metadata from a part
ParameterSet metadata = contentPart.getMetadata();

// Set and Get (key, value) pairs
// ...

// Store the metadata back into the part. You have to set expressively as the content part is a native object and metada is not.
contentPart.setMetadata(metadata);

表示元數據的結構與引擎配置參數結構相同,操作方式也相同。

三、渲染

Iink SDK 渲染的幾個重要概念:

3.1 渲染目標 Render target

一個實現了 IRenderTarget 或者 IRenderTarget2 的 View, 繪製的內容將顯示在上面。

3.2 畫布 Canvas

畫布對象提供了一個平臺,實現由iink SDK調用,以呈現內容繪製命令。它在 ICanvasICanvas2 接口中定義

3.3 渲染器

渲染器負責決定如何呈現每層的內容,決定哪些區需要刷新,以及模型的面積、參數,如縮放因子或者視圖的偏移量,它將通過將執行實際繪圖操作的畫布對象發出渲染命令。

1.4 版爲渲染器引入了一種新的渲染能力,它基於離屏表面的繪製。作爲一種快捷方式,我們稱之爲“離屏渲染”。這提高了渲染速度,甚至是數學動畫等新功能所必需的。所以它絕對是我們推薦的選擇。

要使用此渲染,您必須實現 IRenderTarget2ICanvas2,或使用參考實現提供的 和來處理離屏表面的繪製請求。

1.4 版本之前的傳統渲染模式稱之爲“直接渲染”,使用它必須實現 IRenderTargetICanvas

iink SDK 1.4 渲染器仍然兼容直接渲染的傳統渲染模式。

3.4 層

出於性能原因,渲染器在兩個不同的層上工作。兩層分別是:

  • 一個模型層,對應於模型中已經被引擎處理過的所有內容(指南、筆畫、圖像、排版文本……),
  • 一個捕獲層,渲染在屏幕上繪製但尚未被引擎處理的墨水。
    每個圖層都可以獨立於其他圖層刷新,因此不需要重新繪製所有內容。

示例代碼:

import com.myscript.iink.Editor;
import com.myscript.iink.Engine;
import com.myscript.iink.Renderer;

public class Calculator
{
  private Renderer renderer;
  private Editor editor;

  public Calculator()
  {
    // Previous initialization
    ...

    // Create the view
    EditorView editorView = new EditorView(context);
    editorView.setEngine(engine);
    // ... add it to the UI hierarchy ...
    ...
    // accessing renderer
    renderer = editorView.getRenderer();
    // accessing editor
    editor = editorView.getEditor();
  }
}

四、 編輯器 Editor

Editor對象是與內容交互的中心點,通過渲染目標獲取。

4.1 設置 ContentPart

// Create a new package
ContentPackage contentPackage = engine.createPackage(packageNameFile);

// Create a new part
ContentPart contentPart = contentPackage.createPart("Text");

// Accessing editor
Editor editor = editorView.getEditor();

// Associate editor with the new part
editor.setPart(contentPart);

在設置部件之前 ,您必須確保之前已調用 editor.setViewSize() 並將字體度量提供程序附加到編輯器。如果使用參考實現,調用 editorView.getEditor() 將自動完成。

4.2 輸入捕獲

4.2.1

遵循Interactive Ink交互模式,iink API 認爲筆事件專用於書寫或編輯內容(文本、數學或形狀內容、編輯手勢等),觸摸事件來操作內容(選擇、拖放、滾動等)。

由於您負責將事件傳播到編輯器 - 如果您的用戶使用手指或電容式觸控筆書寫,或者如果您的應用程序是圍繞一組模態工具構建的 - 您可以選擇 iink SDK 應視爲筆或觸摸事件。

輸入的類型由PointerType枚舉定義.

您也可以使用它來選擇具有特定行爲的工具(例如橡皮擦)。

4.2.2 指南

文本文檔和文本部分默認設置了指南。指南爲最終用戶提供了有用的提示,讓他們知道在何處書寫以及以何種大小書寫。它們還可以提高識別準確度,前提是手寫將它們用作基線。

您可以通過引擎或 編輯器配置的鍵來啓用或禁用文本部分的指南。指南之間的垂直間距可以通過文本樣式選項進行調整。text.guides.enable

如果您知道您的輸入與指南不匹配,例如來自非結構化上下文(如一張紙)的墨水,您必須禁用它們以確保良好的識別。

4.2.3 增量輸入

Interactive Ink SDK 通常實時處理用戶輸入。因此,您必須說明指針(筆、手指)如何與捕獲表面(通常是屏幕或圖形輸入板)交互。

這可以通過調用Editor對象的以下方法來完成:

  • pointerDown() - 當指針第一次接觸表面時。

  • pointerMove() - 當指針在保持與表面接觸的同時移動時。

  • pointerUp() - 當指針從表面擡起時。
    這些方法中的每一種都要求您提供:

  • x 和 y - 指針在表面上的座標

  • t - 指針事件的時間戳

  • f - 與事件相關的壓力信息(在 0 和 1 之間標準化)

  • pointerType - 指針的類型(鋼筆、手指或預定義的工具,如橡皮擦:參見PointerType枚舉)

  • pointerId - 該指針的標識符。

editor.pointerDown(0.0f, 0.0f, new Date().getTime(), .7f, PointerType.PEN, 1);
editor.pointerMove(1.4f, 2.0f, new Date().getTime(), .6f, PointerType.PEN, 1);
editor.pointerUp(2.0f, 4.0f, new Date().getTime(), .5f, PointerType.PEN, 1);

您可以調用 pointerCancel() 讓編輯器刪除並忽略正在進行的事件序列。

備註:

  • 時間戳通常是自 1 月 1 日以來的時間(以毫秒爲單位)英石, 1970. 您可以將其設置爲-1,讓iink SDK 根據系統當前時間爲您生成一個。
  • Interactive Ink SDK 不使用壓力信息。它存儲在模型中,可以在導出時或在實現自己的墨跡書寫時檢索。如果您沒有或不需要此信息,則可以將其設置爲 0。
  • 如果只有一個指針同時處於活動狀態,則可以傳遞一個指針 id 爲 -1。

在最簡單的情況下,您可以編寫如下內容:

final long NO_TIMESTAMP = -1;
final float NO_PRESSURE = 0.0f;
final int NO_POINTER_ID = -1;

editor.pointerDown(0.0f, 0.0f, NO_TIMESTAMP, NO_PRESSURE, PointerType.PEN, NO_POINTER_ID);
editor.pointerMove(1.4f, 2.0f, NO_TIMESTAMP, NO_PRESSURE, PointerType.PEN, NO_POINTER_ID);
editor.pointerUp(2.0f, 4.0f, NO_TIMESTAMP, NO_PRESSURE, PointerType.PEN, NO_POINTER_ID);

4.2.4 事件序列

在某些情況下,您可能希望一次性將一組筆畫發送到引擎,例如,如果您從 iink 模型外部導入要作爲單個批次處理的墨水。

對於除“文本文檔”之外的所有類型的部分,iink SDK 提供了一種方法,可以一次性輸入一系列指針事件pointerEvents(),這些事件以PointerEvent對象數組爲參數。

下面是一個例子:

ArrayList<PointerEvent> events = new ArrayList<PointerEvent>();

// Stroke 1
events.add(new PointerEvent().down(184.f, 124.f));
events.add(new PointerEvent().move(184.f, 125.f));
events.add(new PointerEvent().move(184.f, 128.f));
events.add(new PointerEvent().move(184.f, 133.f));
events.add(new PointerEvent().move(184.f, 152.f));
events.add(new PointerEvent().move(184.f, 158.f));
events.add(new PointerEvent().move(184.f, 163.f));
events.add(new PointerEvent().move(183.f, 167.f));
events.add(new PointerEvent().move(183.f, 174.f));
events.add(new PointerEvent().move(183.f, 183.f));
events.add(new PointerEvent().up(183.f, 184.f));

// Stroke 2
events.add(new PointerEvent().down(150.f, 126.f));
events.add(new PointerEvent().move(151.f, 126.f));
events.add(new PointerEvent().move(152.f, 126.f));
events.add(new PointerEvent().move(158.f, 126.f));
events.add(new PointerEvent().move(166.f, 126.f));
events.add(new PointerEvent().move(184.f, 126.f));
events.add(new PointerEvent().move(190.f, 128.f));
events.add(new PointerEvent().move(196.f, 128.f));
events.add(new PointerEvent().move(200.f, 128.f));
events.add(new PointerEvent().move(207.f, 128.f));
events.add(new PointerEvent().move(208.f, 128.f));
events.add(new PointerEvent().up(209.f, 128.f));

// Feed the editor
editor.pointerEvents(events.toArray(new PointerEvent[0]), false);

在調用 pointerEvents() 處理大量筆畫時,應將 processGestures 參數設置 false 爲明確防止手勢檢測並獲得更好的性能。

對於“文本文檔”部分的特殊情況, 您應該:

  1. 根據您要處理的內容類型,將每批指針事件發送到專用的“文本”、“數學”、“圖表”或“繪圖”部分。如果您不能確保它們與墨跡單詞的基線相匹配,請記住禁用“文本”部分上的指南。
  2. 調用 waitForIdle() 以確保識別完成。
  3. 將塊粘貼到“文本文檔”部分的適當位置。

將“文本”部分粘貼到“文本文檔”時,iink SDK 會嘗試自動將手寫內容調整爲指南。

4.3 編輯和裝飾手勢

Interactive Ink SDK 支持所有定義爲Interactive Ink一部分的標準手勢。

沒有什麼特別可以從手勢中受益。SDK 將負責檢測和應用來自提供的輸入的手勢效果,無需任何管道。

裝飾可以設置樣式,並在生成一些導出格式時考慮在內(例如:文本下劃線將被簡單的文本導出忽略,在導出的情況下將變爲粗體並在docx導出中進行語義標記jiix)。

4.4 其它編輯操作

可以通過Editor對象直接對部件的內容進行以下操作:

  • 撤消/重做:撤消/重做堆棧處理的 iink 操作是修改模型的操作。此類操作包括添加筆畫、應用手勢、轉換內容。撤消/重做行爲取決於內容類型:

    1. 對於“文本”、“文本文檔”和“繪圖”,它是基於筆畫的。
    2. 對於“圖表”和“原始內容”,它基於會話。一個會話對應於在 500 毫秒超時後被一起識別的一組筆畫。
    3. 對於“數學”,默認模式基於筆畫,但由於該math.undo-redo.mode屬性,可以將其設置爲會話。
  • Clear應用於整個“ContentPart”。

然而,大多數操作都是在內容塊上完成的。

4.5 識別反饋

UI 參考實現帶有一個“智能指南”組件,可讓您向最終用戶提供實時文本識別反饋,並允許他們從引擎中選擇替代解釋。

有關詳細信息,請參閱描述如何使用文本識別候選項的頁面。

4.6 監控模型的變化

在某些情況下,通知模型中發生的事情是有意義的。例如,您可能希望更新界面的撤消/重做按鈕的狀態,或者僅在有要導出的內容時才允許導出。

您可以調用 addListener()removeListener() 分別附加和刪除您選擇的實現該 IEditorListener 接口的對象:該 contentChanged() 方法將告訴您模型中何時發生任何更改。

IEditorListener 還提供了一種 onError() 方法,您可以實現該方法以在出現任何問題時收到通知。強烈建議實施它,因爲它允許檢測一些常見問題,例如引擎未發現的識別資產或配置。

此外,Editor 該類還提供了其他有用的方法/屬性,例如:

  • isIdle() - 如果引擎正在進行的任何處理結束,則返回true
  • waitForIdle() - 阻塞線程直到引擎空閒。它允許在導出、轉換或操作內容之前等待識別完成。

isIdle()在 contentChanged() 通知中返回始終爲 false。
爲避免死鎖,請勿 waitForIdle() 從 IEditorListener 通知內部調用。

4.7 塊管理

4.7.1 ContentBlock

ContentBlock 是該內容的語義細分,並且可以包含數據或其它模塊。它有一個唯一的 id、一個定義的類型(“文本”、“數學”、“圖表”、“繪圖”、“原始內容”、“容器”……)和一個邊界框。

例如:

  • “數學”部分將只包含一個塊,託管數學內容本身。
  • “文本文檔”部分會更加複雜,因爲它可以包含文本段落、數學方程、圖表和繪圖,以複雜的佈局排列,有時一個接一個,有時一個一個。這是“容器”塊可用於在語義上將子塊組合在一起的地方。

下圖顯示了這些不同的塊如何在其父部件內相互關聯:
contentblock

當在配置中diagram.enable-sub-blocks設置爲true時,“圖表”塊包含描述圖表內容的“文本”、“節點”、“邊”或“多邊”類型的子塊。

4.7.2 ContentBlock層次結構

不同的塊形成一個層次結構,可以通過調用getRootBlock()父部分的方法來獲得根。反過來,每個塊都有一個getChildren()方法,將返回其自己的子項(如果有),以及一個getParent()將返回其父項的方法。

重要的是要注意塊層次結構僅在給定的時間點有效。例如,在文本文檔的情況下,插入新塊、使用手勢刪除文本段落等是一些可能使您之前檢索的塊層次結構無效的事件示例。

您可以通過調用其isValid()方法來檢查 ContetBlock 是否仍然有效。或者在 Editor 對象上通過使用 IEditorListenerEditor 監聽 contentChanged() 事件將提供您的塊可能已失效的提示(受影響塊的 id 列表在參數中提供)。

4.7.3 添加ContentBlock

您創建的任何部分都將包含一個 rootBlock。

但是,您可以使用 editor 的 addBlock() 方法在兼容部分的給定位置添加新塊,作爲導入內容的一種方式 (目前只有“文本文檔”部分支持此功能)。

專用方法 addImage() 允許您在文本文檔部分中插入圖像。

4.7.4 ContentBlock操作

某些操作可以使用塊進行,其粒度比單一部件級別更精細:

  • hitBlock()讓您知道給定位置的最頂層塊(如果有)。例如,它可以知道用戶點擊或按下了哪個塊。
  • removeBlock() 允許您刪除非根塊。
  • convert() 允許您轉換給定塊內的墨水。
  • export_() 允許您導出特定塊的內容,包括其子項。
  • copy() 允許您將塊複製到內部剪貼板中。然後,您可以使用 將其粘貼到給定位置 paste(),就像添加新塊一樣。文本從各種文本塊源(“文本”、“圖表”或“原始內容”)複製和粘貼到“文本文檔”或“文本”部分。在後一種情況下,在執行粘貼之前部件必須是空的。
  • 您可以監控影響給定 ContentBlock 的事件,因爲有關受影響 ContentBlock 的信息是通過接口 IEditorListener 的 contentChanged() 方法提供給您的。

4.8 編輯器級別的配置

雖然 iink SDK 可以在引擎級別全局配置,但可以在編輯器級別覆蓋此配置。這在類似表單的用例中特別有用,在這些用例中您需要操作具有不同配置的字段。

您可以通過調用 getConfiguration() 和設置應該覆蓋全局配置的鍵的值來訪問特定編輯器的配置,以類似級聯的方式。您未在編輯器級別明確設置的鍵值仍遵循引擎級別配置。

例如:

Configuration globalConfig = engine.getConfiguration();
Configuration editorConfig = editor.getConfiguration();

// Global configuration values apply ...
String globalUnit = globalConfig.getString("math.solver.angle-unit"); // -> "deg"
String editorUnit = editorConfig.getString("math.solver.angle-unit"); // -> "deg"
globalConfig.setString("math.solver.angle-unit", "rad");
globalUnit = globalConfig.getString("math.solver.angle-unit");        // -> "rad"
editorUnit = editorConfig.getString("math.solver.angle-unit");        // -> "rad"

// ... except if overridden at editor level
editorConfig.setNumber("math.solver.fractional-part-digits", 4);
globalConfig.setNumber("math.solver.fractional-part-digits", 2);
Number editorDigits = editorConfig.getNumber("math.solver.fractional-part-digits"); // -> 4
Number globalDigits = globalConfig.getNumber("math.solver.fractional-part-digits"); // -> 2

通過調用適當的Editor方法來響應用戶操作來方便地完成插入撤消、重做和清除:

public void undo()
{
  editor.undo();
}

public void redo()
{
  editor.redo();
}

public void clear()
{
  editor.clear();
}

五、轉換

在 iink SDK 術語中,“轉換”是指將一些手寫內容替換爲乾淨的排版等效內容。

5.1 轉換與識別

轉換是一個顯式操作,您可以觸發以使用排版等效項替換墨跡內容。它不同於它所依賴的識別過程,後者在後臺運行並解釋任何輸入,使其具有交互性。

您可以通過調用 editor 對象的 convert() 方法來轉換任何 ContentBlock。

5.2 轉換的目標狀態

調用 convert() 時,您需要提供想要達到的目標狀態。

  • DigitalPublish - 排版內容,適合出版(小字體,在文本文檔的情況下適合圖形),
  • DigitalEdit - 排版內容,適合編輯(字體大小足以編輯,擴展圖形)。

您可以在 DigitalPublish 和 DigitalEdit 狀態之間來回轉換。

5.3 計算字體度量

要轉換您的文本內容,iink SDK 需要有關於您使用的字體的信息,這本身取決於您的樣式選項。爲此,它需要您將IFontMetricsProvider使用setFontMetricsProvider().

由於實現字體度量提供程序是一項相當棘手的任務,MyScript 通過 iink SDK 示例存儲庫爲您提供 參考實現。

如果您依賴參考實現,在編輯器視圖中註冊編輯器會自動將字體度量提供程序附加到您的編輯器。

應用實例:

比如數學部分由單個塊(託管數學內容)組成,您可以使用 editor.getRootBlock() 選擇它。如果按下 UI 的“解決”按鈕調用對象的 solve() 方法。

editor.convert(editor.getRootBlock(), ConversionState.DIGITAL_EDIT)

由於在處理數學內容並且沒有停用求解器,如果所需條件到位,轉換數學將觸發求解。

六、導入導出

Interactive Ink SDK 區分序列化/反序列化(以快速且節省空間的方式存儲模型的全部內容,以供 SDK 將來重用)和導入/導出(作爲與其他應用程序交換 iink 內容的一種方式)。

6.1 導入內容

6.1.1 導入 ContentBlock

您可以將數據導入ContentBlock。例如,以下代碼將“Hello iink SDK”文本字符串導入“Text”部分:

ContentPart textPart = ... // Get the part
editor.setPart(textPart);
editor.import_(MimeType.TEXT, "Hello iink SDK", editor.getRootBlock());

在這種情況下,您可以省略指定塊。由於該部分僅託管單個根塊,因此 iink SDK 可以自行確定將內容導入到何處:

editor.import_(MimeType.TEXT, "Hello iink SDK", null);

對於可以承載多個塊的部件,例如“文本文檔”部件,您需要明確指定目標塊。如果還不存在,可以調用addBlock()直接傳數據導入。

可以通過調用getSupportedImportMimeTypes()編輯器來獲取給定塊支持的 MIME 類型列表。例如:

MimeType[] supportedMimeTypes = editor.getSupportedImportMimeTypes(editor.getRootBlock());

導入內容是“破壞性的”:預先存在的內容將被清除和替換(從 JIIX 導入的文本數據除外)。

6.1.2 JIIX 文本導入

對於文本數據,JIIX 導入目前僅限於文本詞候選更改。後續將提供更多導入功能。

要更改給定文本、原始內容或圖表塊中的候選文本:

  1. 將塊導出爲 JIIX 格式。
  2. 用列表中的另一個詞替換label目標的。wordcandidates
  3. 將修改後的 JIIX 數據導入到您的塊中。

只有在目標塊自導出後未修改的情況下,才能導入 JIIX 數據。

6.1.3 JIIX 導入圖表和原始內容部分

將 JIIX 導入“圖表”和“原始內容”部分時,執行的操作取決於配置的屬性 diagram.import.jiix.actionraw-content.import.jiix.action 。因此,您應該根據需要調整它們:

  • 當屬性被設置爲update,如所描述iink改變文本候選。這是“圖表”部件的默認操作。

  • 當該屬性設置爲add或 時replace,iink 會導入墨跡數據:筆畫、字形和圖元。請注意,這不會重新注入識別結果,並且 iink 會觸發新的識別。

本段的其餘部分適用於add和replace操作。兩種操作之間的區別在於,在這種replace情況下,iink 執行清除操作,即在導入墨跡數據之前從部件中刪除所有內容。“原始內容”部分的默認操作是add。

爲了簡化給定位置的墨跡數據的導入,您可以在 jiix 中添加一個“轉換”鍵,以將此轉換應用於 jiix 數據。json 語法是“轉換”:[xx, yx, tx, xy, yy, ty]。您可以查看Transform API 瞭解有關轉換組件的詳細信息。

目前在變換中只允許平移和正比例,因此 xy 和 yx 應設置爲 0,xx 和 yy 應設置爲 > 0

讓我們以以下用例爲例。想象一下,您想將一個文本節點的大小加倍到一個“圖表”部分。以下是如何進行:

  • 將“圖表”部分導出爲 JIIX 格式。
...
{
   "type": "Text",
   "parent": 183,
   "id": 190,
   "bounding-box": {
    "x": 58.4949799,
    "y": 31.677475,
    "width": 10.9569321,
    "height": 5.24174881
   }, ...
  • 將具有 2 個比例因子(xx 和 yy)的變換對象插入要放大的節點。比例因子是與 (0,0) 相比完成的,因此如果您希望文本保持在同一位置的中心,請不要忘記計算翻譯值(tx 和 ty)。保留之前的示例,修改後的節點將是:
...
{
   "type": "Text",
   "parent": 183,
   "id": 190,
   "transform": [ 2, 0, -63.973446 , 0, 2, -34.298349],
   "bounding-box": {
    "x": 58.4949799,
    "y": 31.677475,
    "width": 10.9569321,
    "height": 5.24174881
   },...
  • 將修改後的 JIIX 數據導入您的 ContentPart。

將比例應用於排版節點時,請記住,在進一步轉換時,iink 可能會修改排版大小。

6.1.4 導入原墨

要導入原始墨水內容,請實例化編輯器並向其傳遞指針事件數組。請注意,在這種情況下,除非您正在處理“繪圖”部分,否則識別引擎將自動處理新筆畫。此方法記錄在本指南的Editor 部分。

6.2 導出內容

6.2.1 確保識別完成

識別有時需要一定的時間才能完成,尤其是當您一次向編輯器發送許多筆畫時。

如果要確保導出最終識別結果,則必須在調用 export_() 之前調用 waitForIdle()

6.2.2 選擇要導出的內容

導出操作是在內容塊上進行的。例如,這允許您從文本文檔部分導出特定圖表。

您可以通過調用 getSupportedExportMimeTypes() 編輯器來檢索給定塊支持的導出 mime 類型列表:

MimeType[] supportedMimeTypes = editor.getSupportedImportMimeTypes(block);

要導出內容,請調用export_()編輯器對象的方法,將要導出的塊和所需的 MIME 類型傳遞給它:

// Export a math block to MathML
String result = editor.export_(mathBlock, MimeType.MATHML);
// Export a text document to docx
editor.export_(textDocBlock, new File("export.docx"), MimeType.DOCX, imageDrawer);

如果 iink SDK 能夠從文件擴展名中明確猜測它,則 API 提供了一種方便的方法,允許您省略 mime 類型:

// Export a text document to docx
editor.export_(block, new File("export.docx"), imageDrawer);

您可以調用 getFileExtensions() 來獲取給定 mime 類型支持的擴展名。

6.2.3 圖片抽屜

某些格式要求您提供一個實現IImageDrawer接口的對象,以便 iink SDK 從內容生成圖像。對於png和jpeg導出來說,這是預期的情況,。

但對於docx, MyScript 提供了一個默認的、隨時可用的圖像抽屜實現,作爲UI 參考實現的一部分。

如果格式不需要圖像抽屜,您可以提供帶有空指針的導出方法。

要了解哪些格式需要圖像抽屜,詳情請閱讀導入和到處格式部分。

6.2.4 文本與二進制導出

文本格式導出作爲字符串返回,然後可以以編程方式操作。另一方面,二進制格式作爲文件保存在磁盤上您可以指定的位置。

IImageDrawer imageDrawer = new ImageDrawer();
imageDrawer.setImageLoader(editorView.getImageLoader());

editor.export_(editor.getRootBlock(), new File("out/export.docx"), imageDrawer);

您可以調用 MimeType 對象的 isTextual() 方法來了解格式是文本格式還是二進制格式

6.2.5 應用特定配置

某些導出功能可讓您臨時“覆蓋”當前編輯器配置以滿足特定導出的需要。如果您想在不影響全局或編輯器配置的情況下調整導出參數(例如要導出到 JIIX 的信息類型),這將非常有用。

以下示例顯示如何將塊識別結果導出爲 JIIX,而不包含原始墨水信息:

// Create an empty parameter set
ParameterSet params = engine.CreateParameterSet();
// Set the appropriate configuration to exclude strokes from the export
params.setBoolean("export.jiix.strokes", false);
// Export a block with the new configuration
String jiix = editor.export_(block, MimeType.JIIX, params);

6.2.6 應用圖像導出配置

圖像導出只能應用於頁面的一部分或超過所選塊範圍。您還可以選擇是否在圖像中顯示參考線。關於圖片導出屬性的詳細信息,請參考配置頁面

爲了導出圖像,需要一個圖像抽屜對象,如本節所述

以下示例說明了導出爲視口的 PNG 圖像。指南是可見的:

// Create an empty parameter set
ParameterSet imageParams = engine.createParameterSet();

// Set the appropriate configuration to tune the viewport to export
float originPx = 0f;
float widthPx = 100f;
float heightPx = 200f;
imageParams.setNumber("export.image.viewport.x", originPx );
imageParams.setNumber("export.image.viewport.y", originPx );
imageParams.setNumber("export.image.viewport.width", widthPx);
imageParams.setNumber("export.image.viewport.height", heightPx);

// Set the appropriate configuration to enable the guides into the exported image
imageParams.setBoolean("export.image.guides", true);

// Create the image drawer
ImageDrawer imageDrawer = new ImageDrawer();
imageDrawer.setImageLoader(editorView.getImageLoader());

// Export the image with the customised parameters
editor.export_(editor.getRootBlock(), new File("out/ImageFileName.png"), MimeType.PNG, imageDrawer,imageParams);

6.3 支持的導入導出格式

6.3.1 交換格式

Interactive Ink SDK 定義了自己的格式,稱爲JIIX(JSON Interactive Ink eXchange 格式的縮寫)。

這種格式提供了所支持的不同類型內容的一致表示,包括語義、位置、樣式 和與墨水相關的方面。

由於其 JSON 語法,它保持可讀性並且可以輕鬆解析,使其適合與主機應用程序交換信息或作爲支持自定義導出格式的臨時表示。

關於 JIIX 的詳情參考 JIIX 部分。

6.3.2 其它格式

Interactive Ink SDK 允許您導入和導出一些常用格式,例如數學內容的 LaTeX 或文本文檔塊的 Docx。

完整列表參考 導入/到處格式部分。

示例:
對於計算器示例,您已經編寫了一些代碼,可以讓您將歷史記錄保存到零件元數據中並在以後檢索它。現在讓我們看看如何使用導出功能填充歷史字符串列表。

在這裏,您需要在每次計算後將一個新項目附加到歷史記錄中,從而產生一個新狀態(多次按下“=”只會註冊一個狀態)。因此,您可以改進該solve()方法:

public void solve(TreeSet<String> history)
{
  // ... Do the conversion ...

  // Add the item to the history if there was a change
  String latexStr = editor.export_(editor.getRootBlock(), MimeType.LATEX);
  if (latexStr != null && !latexStr.equals("") && (history.isEmpty()  || history.last() != latexStr)) {
      history.add(latexStr);
  }
}

當您考慮該部分的全部內容時,您可以將空指針傳遞給導出方法的第一個參數,而不是根塊。

七 縮放和滾動

7.1 查看變換矩陣

儘管交互式墨水本質上是數字化的,但手寫本身卻源於物理世界。用戶在使用特定行距書寫時會感到舒適,並且會根據其手寫筆的類型和質量進行不同的書寫。同樣,識別引擎不會以相同的方式解釋墨環,如果它與一個點幾乎無法區分,或者它只有一釐米寬。對於物理世界中的這種錨定,iink SDK 在內部以毫米爲單位存儲其數據。

但是,您的應用程序很可能會在視圖座標中工作,並且您將依賴該單元將輸入分派到 SDK。由於您還可以指定縮放係數,因此事情可能會變得複雜。

Interactive Ink SDK 通過附加到 editor 對象的視圖變換在這兩個座標系之間建立鏈接。它考慮了 dpi、縮放和潛在偏移。

與大多數 iink SDK API 一樣,您不需要自己操作轉換矩陣。但是,您可以通過調用Renderer 的 getViewTransform()渲染器來訪問它,並使用它從一個系統轉換到另一個系統。

7.2 查看大小

必須使用 將視圖的大小提供給編輯器 setViewSize() 。否則,嘗試將 ContentPart 附加到 editor 時將引發異常(請注意,在參考實現中,此調用是作爲 EditorView 對象實現的一部分爲您進行的)。

視圖的大小在使交互式內容能夠動態重排並調整到視圖大小方面起着重要作用。

7.3 縮放和滾動管理

通過作用於視圖轉換矩陣來管理縮放和滾動。然而,不是直接操作它,而是在渲染器對象上提供了一組方便的方法。

如果更改渲染器轉換矩陣,則需要使渲染目標無效以強制重繪。

7.3.1 縮放

您可以通過調用 getViewScale() 和來操縱視圖比例的絕對值 setViewScale()。比例爲 2.0 會使您的內容看起來是實際情況的兩倍,而比例爲 0.5 會使內容縮小兩倍。

或者,您可以通過調用並傳遞所需的因子來應用相對於當前比例zoom()的縮放因子。

例如:

renderer.setViewScale(2.0f); // Scale = 2.0f
renderer.zoom(4.0f);         // Scale = 8.0f

如果您正在處理捏合縮放手勢,您可能還需要指定調整縮放的點的位置。在這種情況下,zoomAt()除了要應用的縮放係數之外,還使用 並提供要考慮的點的座標。

7.3.2 滾動

滾動視圖,只是通過調用應用偏移的渲染 setViewOffset() 與 x 和 y 偏移的考慮絕對分量。
例如:

renderer.setViewOffset(2.0f, 3.0f);
renderer.setViewOffset(1.4f, 2.0f);

您可以調用 getViewOffset() 獲取當前視圖偏移量。

7.3.4 監控轉換矩陣的變化

您可能希望將偵聽器附加到渲染器以接收轉換矩陣更改的通知(例如調整您的用戶界面)。這樣的偵聽器應實現接口的 viewTransformChanged() 方法,IRendererListener 並且可以使用 addListener()removeListener() 方法分別附加到渲染或從渲染中分離。

示例:
假設你想爲你的手寫計算器實現一個水平縮放滑塊,它調用對象 setZoomScale() 上的一個方法 Calculator,浮動值範圍在 0.25(當它在最左邊時)到 4.0(當它在最右邊時) ),以及 1.4 的中間值(當它位於中心時)。

然後,您可以按如下方式實現該方法:

public void zoom(float scale)
{
  renderer.setViewScale(scale);
  renderer.getRenderTarget().invalidate(renderer, EnumSet.allOf(IRenderTarget.LayerType.class));
}

現在,如果您啓動應用程序並將滑塊向右移動,您將放大,向左移動滑塊時將縮小。

八、主題樣式

8.1 設置主題

一個主題是樣式表影響外觀和感覺由特定呈現的內容的 Editor對象。它不特定於任何特定的內容,因此不存儲在 ContentPart 中。

相同的內容如果被配置了不同主題的兩個編輯器實例打開,看起來會有所不同。

樣式表應作爲字符串傳遞給對象的 setTheme() 方法 Editor。例如,要將當前編輯器的默認墨水顏色設置爲藍色,您可以編寫:

editor.setTheme("stroke { color: #0000FFFF; }");

Interactive Ink SDK 根據設備分辨率動態計算默認樣式參數,例如行高和字體大小。您可以通過設置主題來覆蓋此默認樣式:由您提供的樣式表定義的值將具有更高的優先級。

主題更改不受 iink SDK 撤消/重做堆棧管理。 要讓您的用戶撤消或重做主題更改,您必須在集成方面對其進行管理。 有關可能的實現路徑,請閱讀如何將 iink SDK 撤消/重做堆棧與應用程序(高級)的堆棧相結合。

8.2 改變畫筆的樣式

可以設置與筆關聯的樣式,例如更改其顏色或粗細。 這在爲最終用戶提供調色板或讓其定義鋼筆工具的特徵時非常有用。

有兩種可能的方法:通過主題或通過設置動態樣式。

8.2.1 通過主題改變畫筆樣式

主題化方法包括在自定義主題中指定與不同筆配置相對應的類,並通過在編輯器上調用 setPenStyleClasses() 將給定樣式應用於筆工具。

第一種方法是最有效的方法,更適合爲用戶提供一組固定的選擇。 然而,它將取決於主題,因此不會存儲在內容部分中。

例如:

editor.setTheme(".greenThickPen { color: #00FF00FF; -myscript-pen-width: 1.5; }");
editor.setPenStyleClasses("greenThickPen");

8.2.2 動態設置畫筆樣式

這種方法包括通過在編輯器上調用 setPenStyle() 直接設置鋼筆工具的樣式。

它在處理時間方面不如主題方法優化。 但是,它可以輕鬆地動態創建樣式(例如,如果您讓您的用戶構建自己的調色板)並將保存在內容部分中。

例如:

editor.setPenStyle("color: #00FF00FF; -myscript-pen-width: 1.5");

雖然可以隨時通過設置主題來更新內容的整體外觀和感覺,但 iink SDK 目前不提供專門修改選定元素樣式的可能性。 預計這將在未來的版本中登陸。

8.3 iink SDK 的 CSS 特效

級聯樣式表 (CSS) 是一種非常常見的聲明樣式內容的方式,例如在 Web 上。 Interactive Ink SDK 僅依賴於 CSS 的一個子集來進行樣式設置,需要牢記一些特殊性。

8.3.1 限制

有如下限制:

  • 僅支持有限的 CSS 屬性子集。
  • 支持的類型與常規 CSS 的類型不同(例如不支持 h1、p 或 div 等類型選擇器)。
  • 默認單位是 mm,並且帶有明確單位的屬性將被忽略。
  • 不支持 inherit、initial 或 unset 等關鍵字。
  • 不支持通用選擇器 (*),也不支持組合器。

8.3.2 CSS 類型

Interactive Ink SDK 公開以下類型層次結構:

  • ink - 將下面描述的所有類型分組:
  • stroke - 僅限手寫筆畫
  • glyph - 轉換後的文本字形
  • line - 轉換後的線,例如在轉換圖表時獲得
  • arc - 轉換後的橢圓弧、橢圓和圓
  • guide - 文本指南

8.3.3 內置類和屬性

詳情請閱讀 Style 部分。

8.4 在書寫時獲得的識別反饋樣式

您可以配置 iink 來調整樣式,以便在寫入“原始內容”部分時立即獲得引擎識別爲文本、形狀和繪圖的反饋。 要了解如何繼續,請參閱 Style 參考。

8.5 示例

8.5.1 自定義主題

讓我們首先調整計算器的主題。有幾種可能性可以對存儲在數學部分中的元素進行語義樣式化。
數學方程1

比如,您可能希望轉換後的內容看起來更藍一些,同時保持手寫墨水的默認黑色。您還可以爲數學求解器的結果選擇一種漂亮的綠色,並將字體設置爲粗體(粗細爲 700)和斜體。

代碼示例如下:

editor.setTheme("glyph.math { color: #3A308CFF; }" +
                "glyph.math-solved {" +
                "  color: #1E9035FF;" +
                "  font-weight: 700;" +
                "  font-style: italic;" +
                "}");

數學方程2

在此示例中,新樣式表中定義的值覆蓋了默認內置樣式表的值。

8.5.2 畫筆顏色選項

現在讓我們假設您想爲用戶提供一個調色板,讓他們使用兩種不同的墨水顏色,其中一種是您定義的默認藍色,另一種是紅色。

有兩種方法可以繼續:使用 setPenStyle() 或在主題級別指定一組與預定義顏色對應的類,並使用 setPenStyleClasses()

當您處理預定義的顏色時,第二種方法可能是最好的,而且它也是最有效的方法。

您可以先向自定義主題添加兩個新類(每支筆一個):

editor.setTheme(".math { color: #3A308CFF; }" +
                "glyph.math-solved {" +
                "  color: #1E9035FF;" +
                "  font-weight: 700;" +
                "  font-style: italic;" +
                "}" +
                ".defaultPen { color: #3A308CFF; }" +
                ".correctionPen { color: #FF0000FF; }");

現在,對筆選項之一的選擇做出反應只是調用以下任一方法:

editor.setPenStyleClasses("defaultPen");

或者

editor.setPenStyleClasses("correctionPen");

九、錯誤管理

9.1 獲取錯誤通知

iink SDK 有兩種可能返回錯誤的原因:

  1. 調用 API 時發生異常
  2. 後臺線程發生錯誤時回調 IEditorListener.onError()

9.1.1 異常

每個 API 調用時可能發生的異常在 API 標頭中有詳細描述

9.1.2 編輯器級別錯誤

強烈建議您將其 IEditorListener::OnError() 作爲集成的一部分來實施。這個回調提供了詳細的消息來解釋發生了什麼。

下表列出了主要錯誤及其可能的原因:

錯誤 信息 可能的原因/解決方法
配置錯誤 “error: no such configuration” 找不到配置文件。檢查包含*.conf文件的文件夾是否被引擎或編輯器配置的configuration-manager.search-path鍵引用。
“error: no such configuration bundle” 找不到配置包。檢查引擎或編輯器配置*.conf指定的文件中是否存在具有提供名稱的包。
“error: invalid configuration type” 解析*.conf文件時出錯。此消息存在許多變體,每個變體都應該是不言自明的。
“error: failed to expand environment variables in placeholders ${}”,“error: invalid command “ …
無法將墨跡添加到文本文檔 “ink rejected: stroke is too small (write larger)” 您發送到零件的筆畫太小。它可能源於使用錯誤的 dpi 值來配置渲染器。
ink rejected: stroke is too large (write smaller)” 您發送的筆畫在垂直方向上太大:文本文檔部分假定墨水是寫在參考線上的,而不是跨過參考線。
“ink rejected: stroke is above first line” 墨水寫在上邊距內。
“ink rejected: cannot write on DIGITAL PUBLISH paragraphs (convert to DIGITAL EDIT)” DIGITAL_PUBLISH轉換狀態的文本塊只能接收編輯/修飾手勢,但不能輸入。您必須將它們轉換爲DIGITAL_EDIT以添加額外內容。
“ink rejected: stroke is out of document bounds” 墨跡寫在頁面邊界之外。請注意,每次添加內容時,iink SDK 都會根據提供的視圖大小的高度在頁面末尾分配額外的垂直空間。
“ink rejected: stroke is too long” 行程的長度(即它的點數)超過了發動機可以處理的範圍。
導入錯誤 “could not import JIIX: transform contains a skew or a rotation component” 您導入了帶有包含傾斜或旋轉組件的變換的 JIIX,但現在只允許縮放和平移。
要處理的筆畫太多 “LIMIT_EXCEEDED” 您向識別引擎發送的筆畫比它可以同時處理的筆畫多。
意外的錯誤 “INVALID_STATE” or “INTERNAL_ERROR” 識別引擎遇到意外錯誤。

9.1.3 引擎級別錯誤

錯誤 信息 可能的原因/解決方法
無法打開ContentPackage “error: package is already opened” 在關閉 contentPackage 之前,您應確保編輯器處於空閒狀態,並且沒有引用此 contentPackage 的對象仍然存在。

9.1.4 證書錯誤

它通常採用INVALID_CERTIFICATE打印到控制檯的消息形式。

如果發生這種情況,請檢查:

  1. 證書沒有時間限制或仍然有效
  2. 如果您從 Developer Portal 檢索到證書,請檢查它是否是爲應用程序的捆綁 ID 生成的。

9.1.5 無法識別

如果識別不起作用,您通常可以通過查看編輯器引發的錯誤來了解根本原因:

最有可能涉及以下原因:

  • 找不到配置- 確保*.conf您引用的文件是提供給引擎/編輯器的路徑的一部分,並且包的名稱正確。
  • 找不到識別資源文件- 確保識別資源文件與您的應用程序一起正確部署並被*.conf文件正確引用。

9.1.6 識別質量差

在這種情況下要考慮以下情形:

  1. 您是否指定了正確的語言?- 檢查您的引擎或編輯器配置以及相應的*.conf文件和選定的包。

  2. 如果您正在識別文本,是否啓用了指南?- 如果它們是,但您不依賴它們,它們可能會對識別產生負面影響。

  3. 您waitForIdle()在嘗試檢索結果之前是否致電過?- 臨時結果可能不如最終結果相關。

  4. 您在實例化渲染器時是否提供了正確的 dpi 值?- 這是爲引擎提供“比例”感的基礎,如果缺乏上下文,這可以讓它區分圓形、字母“o”或點。

  5. 你發送正確的內容嗎?- 如果沒有,您不太可能獲得預期的輸出😉!

十、文字識別候選

10.1 單詞識別候選

MyScript 手寫識別嘗試正確猜測用戶在寫什麼,但輸入有時可能不明確,如下例所示:

hello

它應該被解釋爲“hello”還是“bella”?作爲人類,您可能會回答“你好”。這也是 MyScript 引擎在這種情況下的想法。然而,也有可能作者實際上是指“bella”、“hella”甚至“bello”……

在內部,識別引擎會考慮不同的假設,並嘗試提出最好的假設作爲選定的候選詞。它還將返回其他頂級假設,例如“bella”或“bello”作爲替代詞識別候選。

候選文本可以讓您幫助您的用戶輕鬆糾正潛在的識別錯誤,或者幫助實現一個搜索引擎,該引擎可以在一定程度的容忍度下對手寫筆記進行操作。

您可以通過編輯識別配置文件並使用 SetWordListSize 分配給所需的值來調整識別引擎應返回的最大候選詞數。

10.2 通過變成方式管理候選文本

您還可以通過自己的代碼與候選人進行交互(這實際上是提示器的實現方式)。
假設您得到以下墨水:
candidate

如果您要求 MyScript 進行文本導出,它只會返回“候選測試”,這對應於引擎的最佳解釋。 要訪問識別候選,您需要請求 JIIX 導出。

結果如下(以示例爲例,只保留了 JIIX 導出的相關部分,去除了所有墨跡和邊界框信息):

{
 "type": "Text",
 ...
 "label": "Candidate test",
 "words": [ {
   "label": "Candidate",
   "candidates": [ "Candidate", "candidate", "Candidates", "candidates" ],
   ...
  }, {
   "label": " "
  }, {
   "label": "test",
   "candidates": [ "test", "Test", "best", "tests", "Lest" ],
   ...
  } ]
}

“候選文本”解釋由頂級“標籤”鍵提供。

這個頂級識別結果被拆分成一個“詞”數組,每個詞都有自己的標籤和候選集(詞間空間“ ”除外)。

每個單詞的“candidates”鍵允許訪問候選數組,從最可能到最不可能排序。 默認情況下,存儲爲“標籤”的選定候選是列表的第一個元素。

要選擇替代候選人,您需要:

  1. 將標籤的值更新爲建議的候選者之一。
  2. 將 JIIX 內容重新導入原始塊。

從那裏,iink SDK 將保留您的選擇,除非內容的變化導致它完全重新考慮對這部分的識別。

重要的:

  • 始終使用與現有候選者對應的值更新標籤。 這是因爲否則 iink SDK 可能無法弄清楚如何將此解釋映射到原始墨水。
  • 要重新導入內容,請確保除了“標籤”值的更改之外,您沒有更改任何內容,無論是在原始塊中還是在 JIIX 內容中。

十一、自定義墨跡

11.1 術語

在 MyScript 術語中,“墨跡繪製”是指渲染墨跡筆劃的過程。墨跡算法通常處理筆畫點的座標、它們相關的壓力或時間戳信息,以及筆畫樣式,例如顏色或寬度。它由兩個主要步驟組成:

  1. 包絡的計算(即筆畫的幾何形狀),
  2. 描邊本身的繪製。

雖然 iink SDK 帶有自己的內置墨跡書寫,但該工具包足夠靈活,可以讓您自定義這些步驟。

11.2 用例

如果您將 iink SDK 集成到已實現其自己的墨跡功能的應用程序中,您可能需要確保對 iink 和應用程序管理的內容使用相同的渲染算法。

可以調整 Interactive Ink SDK 以解決以下用例:

  • 您只想影響要渲染的筆畫的形狀,並讓 iink SDK 通過撫摸或填充您定義的信封來繪製筆畫。在這種情況下,您所要做的就是實現並註冊您自己的 stroker。
  • 您還想自己繪製筆劃,例如,如果您需要繪製紋理筆劃,或者如果您依賴於 OpenGL 等渲染技術並希望使用粒子而不是填充信封。在這種情況下,除了實現自己的筆畫(iink SDK 需要它,如下節所述),您還需要將信息存儲到生成的路徑中,以便能夠使用它們來自己渲染筆畫。

這種墨跡定製應該特別小心,因爲修改默認實現可能會大大降低渲染性能。

11.3 iink SDK 的要求

獨立於您打算應用於筆畫渲染的自定義量,iink SDK需要儘可能準確地瞭解您打算繪製的筆畫的包絡。

一個信封對應於筆畫的幾何形狀,如下圖所示:
inking

除其他外,瞭解筆畫的包絡可以讓 iink SDK 優化渲染操作(通過計算區域以刷新模型中的變化),知道在給定的時間點渲染哪些項目並管理選擇的幾何形狀。精確的包絡定義是獲得良好結果的關鍵。

11.4 如何使用 API

要定義自己的筆觸,您應該:

  1. 實現IStroker接口:它stroke和isFill方法分別讓你返回給定筆畫的信封作爲一個IPath實例,並選擇 iink SDK 是填充還是隻對結果路徑進行描邊。
  2. 實現IStrokerFactory接口:該createStroker()方法將讓您返回自定義筆劃的實例。
  3. 實例化您的自定義司爐的工廠,並與註冊它IRenderer的接口registerStroker()方法(你可以註銷以後再使用 unregisterStroker()方法上的IRenderer接口)。此方法還可讓您指定與筆劃對應的畫筆名稱。
  4. 使用您定義的畫筆名稱來設置墨水樣式。

要自己渲染筆畫,您可能需要有自己的實現IPath來存儲通過接口的#{strokeMethodName}方法提供的筆畫信息IStroker。IPath然後,存儲在對象中的數據將可ICanvas drawPath()用於您以自定義方式繪製筆畫的方法。

通過 UI 參考實現爲每個目標平臺提供了特定於平臺的 IPath 接口實現。 您可以對其進行改編或子類化以存儲筆畫信息。

253 / 5000
翻譯結果
請注意,根據您是否關閉路徑以及使用 isFill() 返回的值,您將定義不同的信封。 下圖以黑色顯示取決於選項和紅色路徑狀態的結果信封:
ink

如您所見,如果您選擇在 isFill() 方法中返回 false,iink SDK 將考慮比您的路徑嚴格返回的更大的包絡,因爲它會考慮筆畫的像素寬度(寬度參數 方法)來描邊路徑。

11.5 代碼片段

作爲示例,本節展示瞭如何基於“line to”指令實現一個非常基本的墨跡算法。 如果您有興趣開發自己的墨跡書寫方式,您可能會實現更智能和更好看的東西,但代碼的結構將是相似的。

自定義筆畫
讓我們從實現 CustomStroker 類開始:

public class CustomStroker implements IStroker
{
    @Override
    public boolean isFill()
    {
        return false;
    }

    @Override
    public void stroke(InkPoint[] input, float width, float pixelSize, IPath output)
    {
        output.moveTo(input[0].x, input[0].y);
        for (int i=1; i< input.length; i++)
        {
            output.lineTo(input[i].x, input[i].y);
        }
    }
}

此處,代碼利用 iink SDK 將繪製未填充、非閉合路徑的事實來簡化代碼。 但是,在大多數情況下,您需要管理閉合路徑以創建漂亮的筆畫形狀。 InkPoint 結構包含有關筆畫點的所有必需信息。

現在,讓我們實現自定義筆畫工廠:

public class CustomStrokerFactory implements IStrokerFactory
{
    @Override
    public IStroker createStroker()
    {
        return new CustomStroker();
    }
}

您現在要做的就是實例化您的工廠並使用適當的畫筆名稱將其註冊到渲染器:

CustomStrokerFactory strokerFactory = new CustomStrokerFactory();
renderer.registerStroker("LineToBrush", strokerFactory);

最後,您可以在編輯器的主題中設置筆畫:

editor.setTheme("stroke { -myscript-pen-brush: LineToBrush; }");

就是這樣! 從現在開始,iink SDK 將依賴您的自定義筆觸來呈現墨水筆觸!

自定義描邊
首先,如上所述實現自定義筆劃以生成路徑。 如前所述,即使您自己繪製筆畫,iink SDK 也需要用於各種任務的筆畫包絡。

接下來,創建您自己的 IPath 實現(您可以修改或子類化 MyScript 提供的實現),以便您可以使用它來存儲點座標、壓力等信息,稍後您將需要這些信息來渲染您的筆劃。 如果筆畫從模型中刪除,iink SDK 會通過其內部緩存爲您保留這些信息並正確釋放內存。

這是一個可能的實現,基於 UI 參考實現中的 Path 類:

public class CustomPath extends Path
{
    private InkPoint[] inkPoints;
    private float width;

    public float getWidth()
    {
        return width;
    }

    public void setWidth(float width)
    {
        this.width = width;
    }

    public InkPoint[] getInkPoints()
    {
        return inkPoints;
    }

    public void setInkPoints(InkPoint[] inkPoints)
    {
        this.inkPoints = inkPoints;
    }
}

您現在可以更新您的自定義筆劃實現以將信息存儲到路徑中:

public void stroke(InkPoint[] inkPoints, float width, float pixelSize, IPath output)
{
    CustomPath customPath = (CustomPath)output;
    customPath.setInkPoints(inkPoints);
    customPath.setWidth(width);
}

您需要更新(或子類化)提供的 ICanvas 實現以構建自定義路徑:

@Override
public final IPath createPath()
{
  return new CustomPath();
}

現在將使用我們的路徑調用 ICanvas 的 drawPath() 方法,您可以訪問存儲的值並以您想要的方式繪製筆劃:

public void drawPath(IPath path)
{
    // Retrieve stored information
    CustomPath customPath = (CustomPath)path;
    InkPoint[] inkPoints = customPath.getInkPoints();
    float width = customPath.getWidth();

    // Custom drawing
    ...
}

如果您想支持紋理筆觸,您可以爲您支持的每個紋理註冊一個自定義筆觸,併爲您的自定義 drawPath() 實現存儲必要的信息以將其考慮在內。

確保筆劃完全繪製在信封內。 不這樣做可能會導致渲染問題。

十二、自定義識別

12.1 爲什麼要自定義識別

MyScript Developer Portal 允許您下載識別資產以支持多種語言以及數學和圖表用例。每個包都帶有可在大多數情況下使用的即用型配置。

但是,在某些情況下,您可能需要調整這些提供的配置:

  • 您需要引擎識別一些未包含在默認 MyScript 詞典中的詞彙,例如專有名詞。在這種情況下,您可以構建和附加自定義 lexicon。
  • 您使用數學應用程序針對不同的教育水平,並希望限制 MyScript 可以識別的符號數量:這將減少一些可能的歧義(許多數學符號非常相似)並改善整體用戶體驗。在這種情況下,-您可以構建並附加自定義數學語法。
  • 您正在構建表單應用程序並希望減少某些字段以僅接受某些類型的符號,例如字母數字符號、數字甚至大寫字母。在這種情況下,請考慮構建和附加子集知識。
  • 您需要向最終用戶提供或多或少的識別候選,或者您計劃爲搜索目的索引識別結果並只想考慮前 n 個候選。您可以相應地編輯配置。

12.2 識別資源

資源是可以附加到識別引擎以使其能夠識別給定語言或內容的知識片段。

12.2.1 字母知識

一個字母知識(AK)是一種資源,能夠使發動機識別單個字符對於給定的語言和給定的寫作風格。默認配置包括每個受支持語言的草書 AK。

您一次只能將一個 AK 連接到引擎。

12.2.2 語言知識

一個語言知識(LK)是爲用戶提供了一個給定的語言的語言信息的引擎的資源。它允許識別引擎通過偏愛其詞典中最有可能出現的詞來提高其準確性。默認配置包括每個受支持語言的 LK。

LK 不是強制性的,但不附加 LK 通常會導致精度顯着下降。如果您不希望寫出完整的有意義的單詞,例如,如果您打算過濾包含幾個字母的列表,則它可能是相關的。

所有語言的默認配置,但英語變體還附加了一個“輔助英語”LK,允許引擎識別目標語言和英語的混合。除了這種特殊情況,預計不會將語言混合在一起。

12.2.3 詞典

一個詞彙是一種資源,可以在除了包括哪些內容分爲語言知識資源識別表中的單詞。

您可以構建和附加自己的自定義詞典。

12.2.4 子集知識

一個子集知識(SK)是制約該發動機應嘗試識別的文字數的一個資源。因此,它對應於對 AK 資源的限制。它在表單應用程序中很有用,例如,將電子郵件字段的授權字符限制爲字母數字字符、@ 和一些允許的標點符號。

您可以構建和附加您自己的自定義子集知識。

12.2.5 數學語法

一個數學語法是制約數學符號和規則引擎必須能夠處理數量的資源。在教育用例中,將識別調整到給定的數學水平(例如,僅適用於學生的數字和基本運算符)被證明非常有用。

您可以構建和附加自己的自定義數學語法。

12.3 配置文件

12.3.1 角色

如指南的運行時部分所述,iink SDK 使用配置文件,這是一種提供正確參數和知識以識別特定類型內容的標準化方式。

您從 Developer Portal下載的每個識別資產包都包含其自己的配置文件。

12.3.2 部署和使用

要部署和使用配置,您需要:

  1. 將*.conf文件與您的應用程序以及它引用的所有資源文件一起部署(確保所有路徑都正確)。
  2. 將包含該*.conf文件的文件夾添加到configuration-manager.search-path密鑰的引擎配置中存儲的路徑。
  3. 根據內容類型,設置正確的配置鍵。例如,要識別文本(在“Text”、“Diagram”和“Text Document”部分),您需要確保text.configuration.bundle和text.configuration.name鍵的值與您的文本配置包和配置項名稱匹配(參見下面的示例)。

12.3.3 句法

配置文件是帶有*.conf擴展名的文本文件。它由一個標題(標識一個配置包)和一個或多個命名的配置項(定義配置名稱)組成,由空行分隔。

下面是一個例子:

# Bundle header
Bundle-Version: 1.4
Bundle-Name: en_US
Configuration-Script:
  AddResDir ../resources/

# Configuration item #1
Name: text
Type: Text
Configuration-Script:
  AddResource en_US/en_US-ak-cur.res
  AddResource en_US/en_US-lk-text.res
  SetTextListSize 1
  SetWordListSize 5
  SetCharListSize 1

# Configuration item #2
Name: text-no-candidate
Type: Text
Configuration-Script:
  AddResource en_US/en_US-ak-cur.res
  AddResource en_US/en_US-lk-text.res
  SetTextListSize 1
  SetWordListSize 1
  SetCharListSize 1

說明:

  • 以#和開頭的行!被視爲註釋並被忽略。
  • 以空格開頭的行是連續行。在這裏,幾個命令聚集在Configuration-Script.
  • 提供的值Bundle-name是包的名稱。這是 iink SDK 期望的text.configuration.bundle 配置鍵的可能值。在這個例子中,它將是en_US.
  • 提供的值Name定義了一個配置項。這是 iink SDK 期望作爲text.configuration.name 配置鍵的可能值的這些名稱之一 。在這個例子中,它可以是textand text-no-candidate。在任何時間點,給定的引擎只能爲每種類型的識別器配置一個配置項。
  • 對於可能的值Type重點是:Text,Math,Shape和Analyzer。它們對應於核心 MyScript 技術能夠識別的內容類型。

下表列出了您需要爲 iink SDK 提供的配置項類型以支持其不同的內容類型:

內容類型 需要的配置項類型
Text Text
Math Math
Diagram Text+ Shape+Analyzer
Drawing 沒有任何
Text Document Text+ Math+ Shape+Analyzer
Raw Content Text +Shape+Analyzer

必須用空行分隔配置項。

12.3.4 配置命令

下表列出了一些可能的配置命令(放置在 下Configuration-Script):

配置項類型 句法 論點
全部 AddResDir DIRECTORY 引擎應考慮用於資源文件相對路徑的文件夾
AddResource FILE 要附加的單個資源文件的名稱
文本 SetCharListSize N 1 到 20 之間的整數,表示要保留的候選字符數
SetWordListSize N 1 到 20 之間的整數,表示要保留的候選詞的數量
SetTextListSize N 1 到 20 之間的整數,表示要保留的文本候選數
  1. 如果您對原始內容啓用文本識別(請參閱配置) ↩
  2. 如果您對原始內容啓用形狀識別(請參閱配置) ↩

12.4 附加資源

Configuration-Script使用該AddResource命令在配置項部分附加資源。

例如,在en_USAK的情況下,您可以這樣寫:

AddResource en_US/en_US-ak-cur.res

確保資源路徑正確。

十三、應用程序整合撤銷/重做的堆棧集成

13.1 用例

如果您的應用程序基於 iink SDK 編輯器,則可以立即使用撤消/重做管理,如指南的編輯部分所示。

然而,您的應用程序可能更復雜,您可能需要將 iink SDK 操作集成到您自己的撤消/重做堆棧中。

13.2 撤銷/重做堆棧的概念

應用程序撤消/重做堆棧可以看作是一個狀態向量,每個狀態都由特定操作產生,但第一個除外。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章