Editing

This page focuses on the role of the editor object, the central point to interact with content.

Different editors

There are currently four different classes of editor:

  • InteractiveInkEditor provides interactive ink technology leveraging the offscreen mode with Websocket protocol. It is based on a Raw Content part type and allows you to take advantage of all of iink's interactive features:
  • InteractiveInkSSREditor provides interactive ink technology with Server Side Rendering (SSR) with Websocket protocol. It is either based on a Math or a Text part type and allows you to take advantage of limited iink's interactive features. This online example illustrates Text recognition:
  • InkEditor provides ink capture and display as well as batch recognition leveraging the REST recognizer endpoint, which can be used either for Text, Math, Raw Content, Shape or Gesture recognition. This online example illustrates Raw Content recognition:
  • InkEditorDeprecated provides ink capture and display as well as batch recognition leveraging the REST batch endpoint.

To get the most out of iink, we recommend you to use an InteractiveInkEditor to get full interactivity. For the situations where you may prefer to use REST, we recommend you to use an InkEditor but in this case you only benefit from iink non interactive features.

While most pages of this iinkTS guide apply to both interactivity modes, this page focuses on offscreen interactivity specificities

How to create an editor

There are two ways to create an editor:

  • You can instantiate it directly with its configuration options. For instance, to use a InteractiveInkEditor you can use the following code:
let editor = new InteractiveInkEditor(rootElement, options as TInteractiveInkEditorOptions)

await editor.initialize()
  • You can use the Editor load method, which will create the right editor according to its type and configuration options arguments:
  const editorElement = document.getElementById('editor');

  const editor = await iink.Editor.load(editorElement, "INTERACTIVEINK",
        configuration: {
            server: {
                applicationKey: '#YOUR MYSCRIPT DEVELOPER APPLICATION KEY#',
                hmacKey: '#YOUR MYSCRIPT DEVELOPER HMAC KEY#'
            }
        }
  });

Configuration options of the editor

Since not all configuration options are relevant for each editor type, each editor type comes with a default configuration, which is defined in its own object: For example, InteractiveInkEditorConfiguration contains the default configuration for InteractiveInkEditor.

This configuration can be customized during the creation of the editor with configuration options (see above section).

It is not possible to change the configuration of an existing editor, so to change the configuration you must load a new editor

Input capture

Incremental input

MyScript iink SDK typically processes user input in real time. Once strokes are captured, iinkTS handles the communication with the server. The protocol used (REST or WebSocket) depends on the configuration. It also detects errors and attempt retries for you. Once a recognition result is received, the display is updated and rich events are sent to enable custom integration and interaction with your application.

Custom capturing

For most integration use cases, the input capture provided is enough but for your custom need you may want to inject the strokes yourself. This can be done by calling the following methods of the iinkTS object:

  • pointerDown() - When the pointer first touches the surface.
  • pointerMove() - When the pointer moves while staying in contact with the surface.
  • pointerUp() - When the pointer is lifted from the surface.

Each of these methods requires you to provide:

  • x and y - The coordinates of the pointer on the surface
  • t - The timestamp of the pointer event
  • p - The pressure information associated to the event (normalized between 0 and 1)
  • pointerId - An identifier for this pointer.
  • pointerType - The type of pointer ("PEN", "MOUSE", "TOUCH" or "ERASER")

Remarks:

  • The timestamp is typically the time in ms since Jan 1st, 1970. You can set it to -1 to let iink SDK generate one for you based on the current time of the system.
  • MyScript iink SDK does not use the pressure information. It is stored in the model and can be retrieved at export or when implementing your own inking. If you don't have or need this information, you can set it to 0.
  • If you only have one pointer simultaneously active, you can pass a pointer id of -1.

Series of events

In some cases, you may want to send a set of strokes to the engine in a single pass, for instance if you import ink from outside of the iink model or if you want to extract a math equation from a digital ink drawing.

MyScript iink SDK provides a method to input in a single pass a series of pointer events, importPointEvents(), that take in parameter an array of PointerEvent objects.

 const strokes = [
  {
    "pointerType": "mouse",
    "pointerId": 0,
    "pointers": [
      { "x": 295, "y": 187, "t": 1693517651635, "p": 0.1 },
      { "x": 298, "y": 180, "t": 1693517651753, "p": 0.79 },
      { "x": 303, "y": 171, "t": 1693517651769, "p": 0.68 },
      { "x": 309, "y": 160, "t": 1693517651786, "p": 0.65 },
      { "x": 315, "y": 149, "t": 1693517651802, "p": 0.65 },
      { "x": 318, "y": 142, "t": 1693517651819, "p": 0.79 },
      { "x": 325, "y": 136, "t": 1693517651836, "p": 0.81 },
      { "x": 328, "y": 134, "t": 1693517651853, "p": 0.7 },
      { "x": 333, "y": 147, "t": 1693517651920, "p": 0.63 },
      { "x": 335, "y": 158, "t": 1693517651937, "p": 0.67 },
      { "x": 335, "y": 165, "t": 1693517651953, "p": 0.78 },
      { "x": 336, "y": 171, "t": 1693517651970, "p": 0.77 },
      { "x": 335, "y": 180, "t": 1693517651987, "p": 0.81 },
      { "x": 334, "y": 184, "t": 1693517652003, "p": 0.72 }
    ]
  },
  {
    "pointerType": "mouse",
    "pointerId": 0,
    "pointers": [
      { "x": 313, "y": 161, "t": 1693517652401, "p": 0.1 },
      { "x": 317, "y": 160, "t": 1693517652454, "p": 0.72 },
      { "x": 321, "y": 159, "t": 1693517652487, "p": 0.72 },
      { "x": 325, "y": 159, "t": 1693517652503, "p": 0.71 },
      { "x": 336, "y": 159, "t": 1693517652520, "p": 0.67 },
      { "x": 342, "y": 159, "t": 1693517652536, "p": 0.77 },
      { "x": 346, "y": 158, "t": 1693517652554, "p": 0.72 }
    ]
  }
]

editor.importPointEvents(strokes);

Edit

MyScript iink SDK supports all the standard gestures defined as part of Interactive Ink.

There is nothing particular to do to benefit from gestures apart from using the WebSocket protocol as the gestures are not available with REST. The SDK will take care of detecting and applying the effect of the gestures from the provided input without any plumbing needed.

With offscreen mode you can fine tune the gestures that you want to recognize. Refer to the configuration reference

Other edit operations

The following operations can be directly made on the content of a part via a iinkTS object:

  • Undo
  • Redo
  • Clear

Three steps are required to plug buttons to the commands of the editor.

Add controls to the DOM:

<body>
  <div id="controls">
    <button id="undo" disabled></button>
    <button id="redo" disabled></button>
    <button id="clear" disabled></button>
  </div>

  <div id="editor" touch-action="none"></div>

  <script>
    const editorElement = document.getElementById('editor');
    const undoElement = document.getElementById('undo');
    const redoElement = document.getElementById('redo');
    const clearElement = document.getElementById('clear');

  </script>
</body>

Call the editor's controls on click:

  undoElement.addEventListener('click', () => {
    editorElement.editor.undo();
  });
  redoElement.addEventListener('click', () => {
    editorElement.editor.redo();
  });
  clearElement.addEventListener('click', () => {
    editorElement.editor.clear();
  });

Listen to the editor's change event to update the context:

  editorElement.addEventListener('changed', (event) => {
    undoElement.disabled = !event.detail.canUndo;
    redoElement.disabled = !event.detail.canRedo;
    clearElement.disabled = !event.detail.canRedo;
  });
If you need to integrate iink SDK undo/redo with your own undo/redo stack, refer to this page (advanced)

Monitoring changes in the model

Various events are sent by the DOM Element attached to the editor and let you as a developer to plug some custom logic when they occur.

  • loaded: Fired when the editor is fully loaded.
  • changed: Fired when the editor state change, e.g. when undo/redo is available or not.
  • converted: Fired when the editor content is converted.
  • exported: Fired when the editor content export is available.
  • idle: Fired when the editor idle state changes, when asked.
  • error: Fired when an error occurs.
  • undo: Triggered when undo() is called.
  • redo: Triggered when redo() is called.
  • clear: Triggered when clear() is called.
  • convert: Triggered when convert() is called.
  • export: Triggered when export_() is called.

The method waitForIdle() provides by iinkTS will raise an idle event when server and browser processing are over. This may be useful for instance in the case you want some synchronous recognition.

The following example let you explore by yourself how to handle the error event. Same logic could be applied with any other event.

Smart guide

As you may have seen, when using the WebSocket protocol, the editor includes an helper called smart guide that appears as soon as writings are detected. The smart guide enables different actions. It shows in real time a preview of the converted text. It offers you the possibility to change a word using suggestions; it is as simple as clicking on a word and clicking on the suggestion. It also allows you to convert, copy or delete your text, which are available using the actions button on the right.

In iinkTS the smart guide is enabled by default if the type of the recognition is TEXT.

Three parameters are available to modify the default smart guide behavior.

Smart guide could be disabled by setting the attribute smartGuide to false in text parameter of the option passed when instantiating your editor.

const options = {
  configuration: {
      server,
      recognition: {
        type: "TEXT"
      },
      smartGuide: {
          enable: false
        }
    }
};

The search example illustrates a use of TEXT without a smart guide:

If you want the smart guide to disappear after a specific duration (no fade-out by default), you can use smartGuide fadeOut and its two properties, enable and duration. By default, the duration is set to 10000 milliseconds.

const options = {
  configuration: {
      server,
      recognition: {
        type: "TEXT"
      },
      smartGuide: {
          enable: true,
          duration: 10000
        }
    }
};