Fonto Why & How: How to insert an image caption with XQuery Update Facility

Fonto Why & How: How to insert an image caption with XQuery Update Facility

In this weekly series Martin describes a question that was raised by a Fonto developer, how it was resolved and why Fonto behaved like that in the first place. This week, a partner wondered how to insert a caption to a number of different elements. They already had an operation, but it did not always work!

A partner reached out, they are trying to add an image caption, which goes within the renderMultiMedia element, like <renderMultiMedia referencedObject=”Caution.jpg”><caption>text</caption></renderMultiMedia>. But the insertion button is always disabled. Other captions do work. I am using a set of alternatives to have a single button to rule them all.

My operation looks like this, and I am using it in the context menu of my image element:

	"insert-caption": {
        "label": "Caption",
        "description": "Inserts a caption",
        "alternatives": [
    "_insert-image-caption": {
        "label": "Caption",
        "description": "Inserts an image caption",
        "steps": [
                "type": "operation/execute-update-script",
                "data": {
                    "expression": "if ($data?contextNodeId) then (replace node $data?contextNode with <renderMultiMedia>{$data?contextNode/@*}<caption><?fontoxml-selection?></caption>{$data?contextNode/node()}</renderMultiMedia>) else ()",
                    "contextNodeId": "x__fonto:selection-common-ancestor()/ancestor-or-self::renderMultiMedia[1][not(caption)]"


What can I do to make this work?

Sincerely (and happy new year!)

A Fonto partner

So, to start, the operation looks odd because it uses alternatives. These alternative operations are considered one by one to see which one is applicable. It does it from top to bottom, so it will first test if the insert-table-caption operation will apply. If you’d have an image in a table, this might mean the operation to insert a caption in the table will apply first in the context menu of the image. Which is wrong: an author expects to target the image when opening the context menu of the image.

We should use the contextual variant of the operation, so the operation called _insert-image-caption. This one still does not work, unfortunately: it is disabled.

We should check what the step data will be at the start of the operation. A contextual operation is different from an operation in the toolbar. An operation in the context menu of an element will automatically have the contextNodeId property on OperationStepData filled in with the NodeId of that element. In our operation, we also see the contextNodeId property is overwritten with the result of the XPath fonto:selection-common-ancestor()/ancestor-or-self::renderMultiMedia[1][not(caption)]. This XPath will return the closest ancestor of the common ancestor of the selection that is an element called renderMultiMedia, with no caption. Since an image does not really have any place for the selection, this will often result to nothing.

If we just remove it, we see the operation in the context menu works! However, clicking it does nothing. A bit puzzled with this, we checked the XML and saw the caption element actually did appear in the image.

We soon found the result: the renderMultiMedia element was configured with configureAsImage. This family makes the image display, but any elements within the image are never rendered. Changing this to configureAsImageInFrame made this work as expected!

The next step is makign sure authors can actually insert these captions using the new operation we made. We recently released a guide on the Context menu that helps in that. This guide states we should Make commands available from other areas of the UI. The createElementMenuButtonWidget is a good way to do this: authors with no access to the right-click menu, or those that have not learned this pattern yet will have another way to reach this operation.

import configureAsImageInFrame from 'fontoxml-families/src/configureAsImageInFrame.js';
import { SxModule } from 'fontoxml-modular-schema-experience/src/sxManager';

export default function configureSxModule(sxModule: SxModule): void {
	configureAsImageInFrame(sxModule, xq`self::renderMultiMedia`, 'image', {
		referenceQuery: '@src',
		blockHeaderRight: [createElementMenuButtonWidget()],
		contextualOperations: [{name: 'insert-image-caption'}]

I hope this explained how Fonto works and why it works like that. During the years we built quite the product, and we are aware some parts work in unexpected ways for those who have not been with it from the start. If you have any points of Fonto you would like some focus on, we are always ready and willing to share! Reach out on Twitter to Martin Middel or file a support issue!

Stay up-to-dateFonto Why & How posts direct in your inbox

Receive updates on new Fonto Why & How blog posts by email

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top