How to add a custom annotation

This guide guides you through creating a custom annotation in Fonto Content Quality using the DictionaryAnnotator as example for synonyms. Configuring custom annotations is required when using annotators that require additional configuration for Fonto Editor.

What you need

You have followed the Initial setup guide including the optional steps.

Fonto Editor needs configuration on how to handle annotations and what suggestions to give to the author. You configure this behavior based on the annotation type and metadata, produced by Annotators.

1

Update the analysis file

Change the analysis file as follows to also include a DictionaryAnnotator.

A compositor is required to allow the execution of multiple analytics. In this example you will be using the sequential compositor to execute the spellCheckAnnotator and dictionaryAnnotator sequentially.

The following example demonstrates an implementation of synonyms.

<editor>/content-quality/Configuration/analysis.xml

XML

<?xml version="1.0" encoding="utf-8"?>
<analysis
	xmlns="http://schemas.fontoxml.com/content-quality/1.0/analysis-configuration.xsd"
	xmlns:tutorial="urn:tutorial">

	<sequential>
		<spellCheckAnnotator
			languages="en en-US"
			hunspellAffix="en_US.aff"
			hunspellDictionary="en_US.dic"
			spelling="spelling.txt" />

		<dictionaryAnnotator>
			<fileSource relativePath="synonyms.txt" />
			<solrSynonymFormat annotationTypeId="tutorial:synonym" />
		</dictionaryAnnotator>
	</sequential>

</analysis>

Create a file that contains a list of synonyms in a SOLR synonym format and save that file within the <editor>/content-quality/Configuration folder for it to be accessible by the back-end.

<editor>/content-quality/Configuration/synonyms.txt

Other

# All occurrences of "oil" should be replaced with "lubricate".
oil => lubricate
Oil => Lubricate
2

Create the replacement operation

In most cases, an annotation should offer an operation to solve that particular annotation. You will be creating an operation that replaces the text of the annotation with the preferred synonym.

<editor>/packages/content-quality-configuration/src/operations.json

JSON

{
	"my-synonym-operation": {
		"steps": [
			{
				"type": "operation/content-quality-get-node-id-and-range",
				"data": {
					"annotationId": "{{annotationId}}"
				}
			},
			{
				"type": "operation/insert-text",
				"data": {
					"contextNodeId": "{{contentQualityAnnotationNodeId}}",
					"overrideRange": "{{contentQualityAnnotationRange}}"
				}
			},
			{
				"type": "operation/content-quality-resolve-annotation",
				"data": {
					"annotationId": "{{annotationId}}"
				}
			}
		]
	}
}

The first step of the operation is to execute the content-quality-get-node-id-and-range operation. This operation gives you the appropriate node identifier and OverrideRange to operate on. You will need this data in the following step, which is the insert-text operation. The final step of the operation is to execute the content-quality-resolve-annotation operation which instructs Fonto Content Quality to mark the current annotation as resolved and jump to the next annotation.

3

Create a card for the annotation

Add a React component that visualizes the card details for the synonym annotation.

An annotation card usually consists of some information about the annotation and optionally lists operations which can be performed.

Add a button to execute the operation that you created in the previous step, and pass the first synonym to that operation.

<editor>/packages/content-quality-configuration/src/SynonymAnnotationDetails.jsx

JSX

import React from 'react';

import { Flex, Text } from 'fds/components';

import ContentQualityTextWithAnnotationStyle from 'fontoxml-content-quality/src/ContentQualityTextWithAnnotationStyle.jsx';
import FxOperationButton from 'fontoxml-fx/src/FxOperationButton.jsx';
import t from 'fontoxml-localization/src/t.js';

const SynonymAnnotationDetails = ({ annotationId, metadata }) => {
	return (
		<Flex flexDirection="column" spaceSize="m">
			<Text>
				Replace <ContentQualityTextWithAnnotationStyle annotationId={annotationId} /> with:{' '}
				{metadata.synonyms[0]}
			</Text>

			<Flex flex="none" flexDirection="row">
				<FxOperationButton
					label={t('Replace')}
					operationData={{
						annotationId: annotationId,
						text: metadata.synonyms[0]
					}}
					operationName="my-synonym-operation"
				/>
			</Flex>
		</Flex>
	);
};

export default SynonymAnnotationDetails;

When creating an annotation card, make sure to follow our best practices in Create a good user experience.

4

Create the context menu

Create the context menu operations for the annotation by using the createContextualOperationsCallback API:

<editor>/packages/content-quality-configuration/src/install.js

JavaScript

export default function install() {
	// Returns contextual operations for the "Q{urn:tutorial}synonym" annotation type.
	const getSynonymContextMenuOperations = (annotationId, metadata) => {
		return [
			{
				name: 'my-synonym-operation',
				label: metadata.synonyms[0],
				operationData: {
					annotationId,
					text: metadata.synonyms[0]
				}
			}
		];
	};
}

When creating a context menu for an annotation, make sure to follow our best practices in Create a good user experience.

5

Register the annotation type in the editor

Register the tutorial:synonym annotation type (with qualified name Q{urn:tutorial}synonym) by using the registerContentQualityAnnotationType API.

This registration ties the context menu and card from the previous steps to this particular annotation type. Here you are also configuring the contextual operations.

<editor>/packages/content-quality-configuration/src/install.js

JavaScript

import t from 'fontoxml-localization/src/t.js';

import registerContentQualityAnnotationType from 'fontoxml-content-quality/src/registerContentQualityAnnotationType.js';

import SynonymAnnotationDetails from './SynonymAnnotationDetails.jsx';

export default function install() {
	// Returns contextual operations for the "Q{urn:tutorial}synonym" annotation type.
	const getSynonymContextMenuOperations = (annotationId, metadata) => {
		return [
			{
				name: 'my-synonym-operation',
				label: metadata.synonyms[0],
				operationData: {
					annotationId,
					text: metadata.synonyms[0]
				}
			}
		];
	};

	// Registers the "Q{urn:tutorial}synonym" annotation type.
	registerContentQualityAnnotationType({
		namespaceURI: 'urn:tutorial',
		localName: 'synonym',
		heading: t('Synonym'),
		color: 'green',
		icon: 'fas fa-dragon',
		CardContentComponent: SynonymAnnotationDetails,
		createContextualOperations: ({ annotationId, metadata }) =>
			getSynonymContextMenuOperations(annotationId, metadata),
		decoration: 'underline'
	});
}
6

Start the instance

You are now done adding a custom annotation in Fonto Content Quality and ready to do a test run. Start the development server and the Fonto Content Quality instance with the respective commands fdt editor run and fdt content-quality run in their respective directories.

If you already have an instance of Fonto Content Quality running, make sure to restart it after making changes that affect the analysis.

If you followed all of the steps of this guide, you will now have Fonto Content Quality configured to suggest the synonym lubricate when writing the word oil.

You have configured Fonto Content Quality to suggest synonyms using a DictionaryAnnotator. See the DictionaryAnnotator documentation on how to create more advanced implementations using custom dictionaries.