Automating import and layout of content in InDesign

Note: A version of this post appears in the proceedings of XML Prague 2024


My current project is to simplify the work involved in creating assessment content for learners. These assessments consist of short activities where the student must look at an image, listen to an audio file, or read a text, and then choose the correct response from multiple options, fill in a gap to complete a sentence, etc.

We want to create the content once and reuse it in both print and digital formats.

The chosen XML format for the content is QTI. We want to import the content into Adobe InDesign in order to produce a print PDF suitable for publication.

Current process

Currently each assessment is created twice, once for print and once for digital.

The initial version of the content is made in Microsoft Word and imported into InDesign.

There is a lengthy process of review and correction, which also takes place in InDesign. Designers and editors work together to produce a layout ready to print.

The same content is recreated in a digital platform to produce an online version of the assessment.

If further corrections are needed, they are also done twice, once in InDesign and again on the digital platform.

Project requirements

The requirements stem from the desire to reduce duplication of effort by creating and updating content in one place for both print and digital formats.

We want to automate the import of QTI XML into InDesign and the application of styles to the text.

The "Import XML" feature of InDesign partly automates this, e.g. XML elements can be mapped to InDesign styles in the template.

But this is not sufficient for the complex assessment content we are dealing with. For example, XML attributes cannot be mapped to styles, so features of QTI that are distinguished by @class attribute values must be styled manually after import.

A more fully automated process still has to fit around work done by hand in InDesign to finesse the layout. The print pages are densely populated and designed to appeal to students.

An InDesign document generated by automated transformation from QTI may end up with numerous styling overrides assigned to its components, e.g. to resize or move an image, to arrange part of the text in multiple columns instead of a single column, etc. This is all part of the process to make the content attractive to the student and to fit neatly on to a page.

If the text content also needs to be updated or corrected, we want to make this in the source QTI, so that it only has to be done once for both print and digital outputs.

Third parties such as government ministries that control curricula have the ability to request changes late in the production process. By the time the QTI XML has been updated, the InDesign layout may have had styling overrides applied to it.

When the XML is updated and re-imported, we want to preserve any styling overrides that have been made, and not reset the content to a default layout.

InDesign basics

Adobe InDesign is an application used for typesetting books, magazines, leaflets, worksheets, etc.

A common workflow is to import content from Word. Styling in Word can be mapped automatically to styling in InDesign.

Once imported, text and images etc are laid out in "Spreads". Further styling can be applied.

Once the layout is approved, a PDF suitable for printing can be exported.

InDesign concepts

I will explain a few terms that will be used in this article.

IDML

IDML is a human-readable packaging format for InDesign.

An IDML file is a zip package of XML files describing design, layout, text content, etc.
Contents of an IDML package, shown in the Archive browser in Oxygen XML Editor

(The .docx and .xlsx formats used in Microsoft Word and Excel use a similar principle.)

There is no official current public documentation on IDML. The most recent version of an IDML spec I have found is version 8 (from 2012), but this is not available from the Adobe web site.

The 2012 spec does encourage the kind of project I describe in this post:

We've designed IDML to make it a key part of automated workflows. Using IDML, you can…generate or modify IDML documents…using data from databases or other data sources.

But in practice the spec does not describe all the markup constraints required in order for InDesign to render an IDML document accurately. There is also very little activity on StackOverflow around IDML. In this project there has therefore been a lot of trial and error figuring out the hidden rules of the IDML markup.

I had access to a previous attempt to automate the import of XML into InDesign (using a different XML model from QTI), but this was unfinished and undocumented.

Spread

A Spread consists of one or two Pages, and objects of various kinds (Text Frame, Image, etc) with properties defining their position, size, orientation etc on the Page.

A Page may inherit properties from a Master Spread

Each Spread and Master Spread is an XML file in the IDML document.

The Spreads are what appear in the print publication.

The Master Spread does not appear, but if a Page on a Spread is linked to a Master Spread, the content of the Master Spread will appear on that Page in the print publication. This feature enables (for example) a page footer to be created once, as an object in the Master Spread, and appear on each Page that links to that Master Spread.

XML tree for an InDesign Spread

Text Frame

A Text Frame is an object on a Spread.

It displays text that is physically held in a linked Parent Story.

Text Frames can be chained together if the Parent Story is too long for one Text Frame. The Story then overflows into the next linked Text Frame.

For example, the text of an entire book can be held in a single Story that is displayed across Text Frames chained together across the Pages of the book.

Linked Text Frames can also be used to produce effects such as two-column layout. (The columns are linked Text Frames.)

We also make use of "anchored" Text Frames, which are objects within a Story that appear inline with the content of the Story.

Text Frame edges visible in InDesign. One Text Frame is selected.)

Story

A Story contains a chunk of text that is to be displayed on a Page (or Pages).

Each Story is an XML file in the IDML document.

InDesign has an "Import XML" feature. Importing XML using this feature creates a "Backing Story", which is a separate XML file in the IDML document.

A Backing Story is only visible in the structured view in InDesign, until elements are dragged into Spreads.

The Structure panel in InDesign

Drag an element from the structured view to a Spread to create a Text Frame with the content of that node.

(This also snips the XML and creates a new Story as Parent Story for the Text Frame.)

Paragraph and Character styles can be applied to the text.

A fragment of the XML tree for a Story, shown in the Outline view in Oxygen XML Editor

QTI basics

QTI is an industry standard XML model for assessment content.

The model contains a mixture of presentational elements (HTML) and functional elements.

Types of interaction include text entry, multiple choice, and ordering.

One or more interactions of the same type may be grouped together as an "assessment item" (or "activity").

QTI also contains structures for holding the correct responses and for scoring the student's own responses against them and providing feedback.

QTI example: multiple choice interaction

This example shows:

  • a variable to hold the student's response, with an indication of the correct response
  • a variable to hold the student's score
  • a variable to hold the feedback to be given to the student, depending on their response
  • an instruction to the student
  • a reference to an external file containing the text they must read
  • a question
  • three options for the answer
  • feedback for each option
  • a reference to an external file containg the logic for processing the response and assigning values to the variables
<?xml version="1.0" encoding="UTF-8"?>
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p1"
    xmlns:xi="http://www.w3.org/2001/XInclude"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p1 http://www.imsglobal.org/xsd/imsqti_v2p1.xsd"
    identifier="mcq-example" title="Multiple choice example" adaptive="false" timeDependent="false">

    <responseDeclaration identifier="RESPONSE" cardinality="single" baseType="identifier">
        <correctResponse>
            <value>Choice_C</value>
        </correctResponse>
    </responseDeclaration>

    <outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float">
        <defaultValue>
            <value>0</value>
        </defaultValue>
    </outcomeDeclaration>

    <outcomeDeclaration identifier="FEEDBACK" cardinality="single" baseType="identifier"/>

    <itemBody>
        <rubricBlock view="candidate">
            <p>Read the text and choose the correct answer.</p>    
        </rubricBlock>      

        <div id="mcq-activity" class="activity-item">
            <xi:include href="rt-mcq-example.xml"/>
            <p>What does the writer like about Blackpool?</p>
            <choiceInteraction responseIdentifier="RESPONSE1" maxChoices="1">
                <simpleChoice identifier="Choice_A">The tower</simpleChoice>
                <simpleChoice identifier="Choice_B">The North Pier</simpleChoice>
                <simpleChoice identifier="Choice_C">The Pleasure Beach</simpleChoice>
            </choiceInteraction>
            <feedbackBlock outcomeIdentifier="FEEDBACK" identifier="Feedback_A">
                <p><span class="emphasis">The tower</span> is not correct because the writer says "I am afraid of heights and didn't climb the tower".</p>
            </feedbackBlock>
            <feedbackBlock outcomeIdentifier="FEEDBACK" identifier="Feedback_B">
                <p><span class="emphasis">The North Pier</span> is not correct because it is closed for renovation.</p>
            </feedbackBlock>
            <feedbackBlock outcomeIdentifier="FEEDBACK" identifier="Feedback_C">
                <p><span class="emphasis">The Pleasure Beach</span> is correct because the writer says that "I went on the roller coaster at the Pleasure Beach three times".</p>
            </feedbackBlock>
        </div>

    </itemBody>

    <responseProcessing template="match_correct.xml"/>

 </assessmentItem>

QTI example: response processing

This example shows the file for processing the student's response to the multiple-choice activity shown above.

It contains structures for assigning the student a score of 1 or 0 and specific feedback, depending on their response.

<?xml version="1.0" encoding="UTF-8"?>
<responseProcessing xmlns="http://www.imsglobal.org/xsd/imsqti_v2p1"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p1 http://www.imsglobal.org/xsd/qti/qtiv2p1/imsqti_v2p1.xsd">

    <responseCondition>
        <responseIf>
            <match>
                <variable identifier="RESPONSE"/>
                <correct identifier="RESPONSE"/>
            </match>
            <setOutcomeValue identifier="SCORE">
                <baseValue baseType="float">1</baseValue>
            </setOutcomeValue>
        </responseIf>
        <responseElse>
            <setOutcomeValue identifier="SCORE">
                <baseValue baseType="float">0</baseValue>
            </setOutcomeValue>
        </responseElse>
    </responseCondition>

    <responseCondition>
        <responseIf>
            <match>
                <variable identifier="RESPONSE"/>
                <baseValue baseType="identifier">Choice_A</baseValue>
            </match>
            <setOutcomeValue identifier="FEEDBACK">
                <baseValue baseType="float">Feedback_A</baseValue>
            </setOutcomeValue>
        </responseIf>
    </responseCondition>
    <responseCondition>
        <responseIf>
            <match>
                <variable identifier="RESPONSE"/>
                <baseValue baseType="identifier">Choice_B</baseValue>
            </match>
            <setOutcomeValue identifier="FEEDBACK">
                <baseValue baseType="float">Feedback_B</baseValue>
            </setOutcomeValue>
        </responseIf>
    </responseCondition>
    <responseCondition>
        <responseIf>
            <match>
                <variable identifier="RESPONSE"/>
                <baseValue baseType="identifier">Choice_C</baseValue>
            </match>
            <setOutcomeValue identifier="FEEDBACK">
                <baseValue baseType="float">Feedback_C</baseValue>
            </setOutcomeValue>
        </responseIf>
    </responseCondition>
</responseProcessing>

QTI example: text entry interaction

This example shows the <textEntryInteraction> element that indicates where a student will provide their response.

In print this will be rendered as a write-on line. In a digital format it will be a text box.

<?xml version="1.0" encoding="UTF-8"?>
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p1"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p1 http://www.imsglobal.org/xsd/imsqti_v2p1.xsd"
    identifier="gapfill-example" title="Transform sentence example" adaptive="false" timeDependent="false">
    <responseDeclaration identifier="RESPONSE" cardinality="single" baseType="string">
        <correctResponse>
            <value>your</value>
        </correctResponse>
    </responseDeclaration>

    <outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float">
        <defaultValue>
            <value>0</value>
        </defaultValue>
    </outcomeDeclaration>

    <itemBody>
        <rubricBlock view="candidate">
            <p>Complete the sentence with the correct form of the word in brackets.</p>    
        </rubricBlock>
        <div id="gapfill301-activity-item-1" class="activity-item">
            <div id="gapfill301-artwork-1" class="artwork-supplementary">
                <img id="resource-fragment-group2-19" src="n/a" alt="TBC"/>
            </div>
            <p>Where is <textEntryInteraction id="gapfill301-textEntryInteraction-1" 
                responseIdentifier="RESPONSE"/> hat, Nigel? 
                <span class="prompt">you</span></p>
        </div>
    </itemBody>

    <responseProcessing template="../rptemplates/match_correct_no_feedback.xml"/>
</assessmentItem>

QTI package

A QTI "package" contains individual files for activities, tests (groupings of activities), media assets, response processing, and a manifest.

The manifest contains metadata about each resource in the package.

Outline of the new process

The starting point is an IDML "template" containing Master Spreads, styles, and placeholder Text Frames.

We then want to import the content of a QTI "package" using the following steps:

  • unzip the IDML package
  • transform the QTI XML into IDML Stories
  • update other files in the IDML package
  • zip up the new IDML package

I wrote an XProc pipeline (executed by MorganaXProc-IIIse) to run this process.

XSLT transformation design

The source document for the main transformation is the QTI manifest.

The path to the folder containing the unzipped IDML template is passed as a parameter.

From the manifest, we identify the test and load it as a document. (The test is an XML file containing references to the individual activities, which are separate XML files.)

A global variable is created with some information that we will need later.

We will be creating new Spreads for the activities, based on the Master Spread. The Master Spread in the template contains Text Frames for the test heading and content, positioned and sized appropriately, and containing placeholder content. The new Spreads will contain clones of these Text Frames that point to new Stories containing the actual test header and content.

The global variable provides details of the new Spreads, Text Frames, and Stories, mapped to the QTI nodes that provide the Story content for each Text Frame.

From the IDML folder, we identify the Backing Story.

We transform the Backing Story XML, importing and applying templates to QTI XML from the test and activities.

This process tries to go with the grain of the manual process by importing XML into the Backing Story and extracting nodes into new Stories and Text Frames.

The core part of the transformation is relatively straightforward. An element in the source QTI XML is transformed to a <XMLElement> element with a @MarkupTag attribute containing the name of the original element. An attribute in the source XML becomes a <XMLAttribute> element with @Name and @Value attributes containing the original attribute's name and value. A text node is retained, wrapped in a <Content> element.

It is necessary for us to include a representation of the source XML structure, as it is crucial to supporting the process of updating an InDesign document when source XML is updated, e.g. with corrections.

There are several more complex features of the transformation that are needed to produce the desired output.

Application of styles

When matching a node that has a paragraph or character style, we apply the style by creating a <ParagraphStyleRange> or <CharacterStyleRange> element.

The mapping of QTI structures to template styles is hard-coded into the XSLT and accessed via functions getParagraphStyle() and getCharacterStyle().

This XML fragment shows a fragment of a Story representing a write-on line populated with an example answer.

<XMLElement 
    Self="d8e143_textEntryInteraction_default"
    MarkupTag="XMLTag/textEntryInteraction">
  <ParagraphStyleRange 
      AppliedParagraphStyle="ParagraphStyle/answer-example">
    <CharacterStyleRange 
        AppliedCharacterStyle="CharacterStyle/$ID/[No character style]">
      <Content>your</Content>
      <Br/>
    </CharacterStyleRange>
  </ParagraphStyleRange>
  <XMLAttribute 
      Self="d8e143_XMLAttribute_responseIdentifier"
      Name="responseIdentifier"
      Value="RESPONSE2"/>
</XMLElement>

The IDML hierarchy of <ParagraphStyleRange> (for applying block-level styles) containing <CharacterStyleRange> (for inline styles) containing <Content> (for text nodes) is straightforward, but the rules for how <XMLElement> elements (representing the source XML structure) are interleaved are complex and undocumented. InDesign is highly sensitive to errors in this structure.

A second XSLT transformation on the generated Stories was necessary in order to impose these rules.

Creation of new Stories

We have already prepared a global variable identifying the QTI nodes that provide the content for Text Frames in the output Spreads.

When matching one of these nodes in the QTI XML, we create a new Story whose identifier is already provided by the global variable.

By using <xsl:result-document> we move out of the Backing Story and create a new file for the Story. We continue the processing of the source QTI XML from that node but now within the new Story. This mirrors the manual process in InDesign of dragging a node from the structured view, which creates a new Story.

Handling non-print QTI structures

The QTI model contains features that support digital interactivity but which are not relevant in print, e.g. the indication of the correct response to an activity. The IDML model allows us to preserve this content as "Hidden Text", but we just remove it in the transformation. This makes it simpler to apply the rules for interleaving <XMLElement> elements with the other IDML Story elements.

Creation of new Spreads

We then make the new Spreads, containing:

  • Page(s) linked to Master Spread
  • Text Frames linked to the new Stories, cloned from corresponding placeholder Text Frames in the Master Spread

(Stories are not visible until we assign them to Text Frames in Spreads. The Story identifier associated with each Text Frame is provided in the global variable we created earlier.)

Because a Page in a new Spread is linked to the Master Spread (in order to inherit its properties from there), text frames on the Master Spread will be visible.

Some of these we want to keep, e.g. a generic page footer.

But we don't want to see the placeholder Text Frames in the final document, because we have cloned them into the new Spreads and linked them to Stories containing the test content. So we remove these placeholders from the Master Spread.

Update the Design Map

The Design Map is a manifest of the resources in the IDML package. Because the contents of the package have changed (new Spreads etc) we need to update this.

It was simplest to do this as a separate XSLT transform within the XProc pipeline.

Challenges

Extent of the document

We can't be certain of how many Pages the QTI content will fill. How we calculate this is TBC. We could make a rough calculation based on word count in the text, for example, or make an assumption that each activity will occupy a certain number of pages.

Complex content and styling

Importing plain text into InDesign would be relatively straightforward, even with the lack of documentation for IDML.

But we are creating assessment content, which has a lot of semantic features that require special handling, for example:

  • gaps or write-on lines for a student to write their response
  • multiple choice options that may be inline, or laid out across the page
  • images to illustrate the activities, or even as multiple choice options
  • "reading texts" for a student to analyze, which may be presented in various specific styles (e.g. blog post, SMS conversation, etc.)

For a write-on line, we create a Text Frame "anchored" within the Story containing the main body of text (and size it appropriately for the length of response required).

Breaking out of the XML hierarchy

In some places we don't simply follow the XML hierarchy of the source document.

To get the heading for an activity we need to reach out to the manifest file and pull it from the activity's metadata held there.

To produce a worked example of a text entry activity, we go to the <responseDeclaration> element in the QTI XML to find the correct answer and pull it into the content of the Text Frame for the activity's write-on line.

Bear traps

IDML appears to have no official current public documentation, but I did find an old spec and a cookbook (on a site where I normally find manuals for kitchen appliances). (Links are working at time of writing.)

Neither of these helped with a peculiar problem I had.

The process creates a placeholder for an image if the image file is not yet available. This placeholder is a Text Frame whose content is the "art brief", i.e. a description of what the image will contain.

A Text Frame gets content by linking it to a parent Story. The @ParentStory attribute of the TextFrame element matches the @Self attribute of the Story whose content we want to appear in the Text Frame.

The Text Frame in my sample was linked correctly to a Story containing the art brief, but the content was not visible in the output.

The Text Frame:

<TextFrame Self="d11e141_img_TextFrame"
  ParentStory="d11e141"
  ContentType="TextType"
  ItemTransform="1 0 0 1 0 0"
  AppliedObjectStyle="ObjectStyle/WoL-V">
  <Properties>
    <!-- various Text Frame properties -->
  </Properties>
</TextFrame>

The Story:

<idPkg:Story DOMVersion="18.5"
             xmlns:idPkg="http://ns.adobe.com/AdobeInDesign/idml/1.0/packaging">
  <Story Self="d11e141">
    <XMLElement 
        Self="resource-fragment-group2-19_img_default"
        MarkupTag="XMLTag/img"
        XMLContent="resource-fragment-group2-19">
      <ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/artwork-ref">
        <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/$ID/[No character style]"/>
      </ParagraphStyleRange>
      <XMLElement 
          Self="d6e449_string_default"
          MarkupTag="XMLTag/string">
        <ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/artwork-ref">
          <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/$ID/[No character style]">
            <Content>Photo of Blackpool Tower</Content>
            <Br/>
         </CharacterStyleRange>
       </ParagraphStyleRange>
     </XMLElement>
     <XMLAttribute
         Self="resource-fragment-group2-19_XMLAttribute_id"
         Name="id"
         Value="resource-fragment-group2-19"/>
     <XMLAttribute 
         Self="resource-fragment-group2-19_XMLAttribute_src"
         Name="src"
         Value="n/a"/>
     <XMLAttribute 
         Self="resource-fragment-group2-19_XMLAttribute_alt"
         Name="alt"
         Value="TBC"/>
    </XMLElement>
  </Story>
</idPkg:Story>

After lengthy experimentation I discovered that InDesign is sensitive to ordering of items in the Design Map. This file is part of the IDML package and contains references to each of the Stories in the package. If one Story contains a Text Frame that points to another, the Design Map must reference the Story that contains the Text Frame before the Story that the Text Frame points to.

My code was ordering the references in alphabetical order of file name, which happened to be wrong for the desired output.

Workflow for content corrections

A key requirement of the new process is that corrections and updates to the content are made in one place for both print and digital formats.

Currently this is an iterative process between Editors and Designers and takes place in InDesign itself.

InDesign does allow you to create and update XML tagging using the structured view. So it is possible to make changes in InDesign and export updated QTI XML from it, which we could then pass on to the digital platform.

But InDesign is not an XML editing tool, and our judgment was that this would not be a viable solution.

Our proposed workflow is to update the QTI XML and re-import it into InDesign.

But the layout may be updated in InDesign following the initial import, e.g. to position placeholder images.

The initial InDesign output uses Text Frames from the default Master Spread for its content layout, and hard-coded default sizes for the image placeholders.

Initial InDesign output for a text entry activity

The designer takes this output and makes adjustments to produce the desired appearance for this activity:

  • The instruction now fills the full width of the page instead of being restricted to the first column
  • The image placeholders have been enlarged and aligned with the text of the activity items

Resizing the image placeholders has the effect of pushing item 3 into the second column and creating a symmetrical layout for the activity.

InDesign output after layout changes

When we re-import the QTI XML we don't want to reset the layout and force the designer to re-apply the layout changes. We need to preserve those changes.

To refresh the InDesign document with updated source XML, we use the updated InDesign output as the IDML source instead of the template. The XSLT identifies that Stories in the new IDML source are already associated with QTI nodes from the source XML. We can do this because of the <XMLElement> and <XMLAttribute> tagging within the Stories.

Instead of creating new Spreads, we copy the existing spreads, their Text Frames, and styling properties applied to them. The XSLT recreates the Stories from the source IDML using the updated QTI XML.

Some styling overrides in the updated InDesign output are encoded in Story XML, e.g. ParagraphStyleRange/@LeftIndent. Although we are recreating the Story XML, the XSLT is able to identify and copy these overrides from the source Story by identifying an association between the styling override and the node in the source QTI.

Getting the <XMLElement> elements interleaved correctly with the IDML styling elements was crucial to getting this process working.

The end result is that the text content of the IDML document is completely refreshed from the source QTI XML but the adjusted styles and layout from the initial IDML output are preserved.

InDesign output refreshed from updated QTI, with layout preserved

Importing images

When an image has been supplied, we can update the QTI XML to include a reference to the image file, including its dimensions.

<img id="resource-fragment-group2-19" 
  src="../media/nigel.jpg" width="1300" height="953" 
  alt="TBC"/>

The transformation replaces the placeholder text frame with a Rectangle of the same shape and position, with the Image placed within it.

The initial output was not promising.

Image placed at original size and relative to original anchor point of Text Frame

To understand and fix the problem I had to analyse the IDML markup for the Rectangle.

The properties of the <Rectangle> element (replacing the placeholder <TextFrame> element) illustrate the layout changes made by the designer to the original IDML output.

Original placeholder:

<TextFrame ItemTransform="1 0 0 1 0 0">
  <Properties>
    <PathGeometry>
      <GeometryPathType PathOpen="false">
        <PathPointArray>
          <PathPointType Anchor="0 -112"/>
          <PathPointType Anchor="0 0"/>
          <PathPointType Anchor="140 0"/>
          <PathPointType Anchor="140 -112"/>
        </PathPointArray>
      </GeometryPathType>
    </PathGeometry>
  </Properties>
</TextFrame>

The four <PathPointType> elements give the X and Y coordinates of the corners of the TextFrame, relative to its anchor point, the top-left corner.

After the Text Frame has been moved and re-sized by the designer, the properties have changed and are inherited by the replacement Rectangle containing the imported Image.

<Rectangle ItemTransform="1 0 0 1 10.106443481445353 -49.211789047799016">
  <Properties>
    <PathGeometry>
      <GeometryPathType PathOpen="false">
        <PathPointArray>
          <PathPointType Anchor="-10.106443481445353 -112"/>
          <PathPointType Anchor="-10.106443481445353 49.21178904779901"/>
          <PathPointType Anchor="223.75182423518075 49.21178904779901"/>
          <PathPointType Anchor="223.75182423518075 -112"/>
        </PathPointArray>
      </GeometryPathType>
    </PathGeometry>
  </Properties>
  <Image ItemTransform="1 0 0 1 0 0">
    <Link LinkResourceURI="file:/path/to/nigel.jpg"/>
  </Image>
</Rectangle>

The four <PathPointType> elements give the X and Y coordinates of the corners of the Rectangle, relative to its original anchor point. The 5th and 6th components of the Rectangle's @ItemTransform attribute describe the X and Y movement of the Rectangle away from its original anchor point.

The image was placed at its full size and positioned with its top-left corner at the original anchor point of the frame.

To rectify this, we needed to update the @ItemTransform attribute of the image, to place it correctly within the containing Rectangle.

The requirements were to scale the image's width to the width of the Rectangle and to centre it vertically within the Rectangle. The XSLT makes a calculation based on the image's dimensions, the dimensions of the Rectangle, and also the height of the original placeholder Text Frame.

<Image ItemTransform="0.17989097516663546 0 0 0.17989097516663546 
  -10.106443481445353 -117.1121551430023">
  <Link LinkResourceURI="file:/path/to/nigel.jpg"/>
</Image>

The 1st and 4th components of the @ItemTransform attribute are the scaling factor, the 5th and 6th are the X and Y movement to position the scaled image correctly within the Rectangle.

The scaled image is slightly taller than the containing Rectangle, resulting in the top and bottom edges being cropped.

Image scaled and repositioned

Next steps

Much progress has been made, but there is still a lot to do.

There are more types of QTI activity not yet handled by the transformation.

For the content corrections process, the transformation preserves a number of types of InDesign styling override, but there are more to do.

The process for making corrections to the QTI XML is not yet confirmed. If we are not making content corrections in InDesign, we still want a process that allows us to see the changes in InDesign as quickly as possible.

We could create a transformation from the Word manuscript to QTI, which would allow corrections to be made in Word (a familiar tool for editors) and transformed to QTI and then IDML in a single process.

As an alternative, I have created an authoring framework for QTI in Oxygen XML Editor as a proof of concept.

With the XProc pipeline set up as an External Tool in Oxygen, this would allow an Editor to update XML in a WYSIWYG environment and immediately see the results in InDesign.

Embedded Schematron rules (with QuickFix) can allow us to ensure the Editor produces valid XML output.