Skip to main content

Export content from InDesign and deliver it to your digital service

Dan BadhamContensis
Dan BadhamFront-end developer and designer
Advice and expertise8 min

We all use a variety of systems on a daily basis. You might have content in a catalogue system, a learning management system, or publishing software. When you want to use this content elsewhere – say on your website, in an app, or in printed material – you probably have to resort to copying and pasting content from one system to another.

We know how frustrating this can be, so we've tried to make it as easy as possible to get content in and out of Contensis. Using our Management API you can import content from other systems into Contensis as entries. Once you've added any extra data – such as images or metadata – you can then publish this content to your website or any other digital services using the Delivery API.

At a conference late last year I met a team who publish a magazine for people working in the museums industry. They were looking for a way to get content that they had written for their magazine from Adobe InDesign and onto their website. I thought I'd put together a blog showing how easy it is to do that using Contensis.

In this blog you'll learn how to:

  1. Create an XML .DTD file to set a schema for the content
  2. Tag the InDesign document to match the DTD
  3. Export the content from InDesign as XML
  4. Create a new Contensis Content Type called ‘Story’ to match the schema
  5. Create an API key
  6. Use the Contensis Management API write a simple script to create the entries
  7. Run the script locally

InDesign allows you to export your content as XML by tagging the content. By using the Contensis Management API and some C# you can then write a bit of logic to read the XML and populate Contensis with entries.

Before reading further, get the source code on GitHub:


Download the source code from GitHub

Step 1: Creating an XML .DTD file

First take a look at your InDesign document and examine how you need to break it down for export as XML. An XML structure is defined by a Document Type Definition, or DTD, which is stored in a .dtd file.

You will need to break your content down into individual elements that will correspond with fields in your content type. This may take a bit of working out if you are starting from scratch. If you are using our example files, then we’ve already done this for you.

In our example, as we wanted to export multiple stories from our InDesign document, we created the following structure for our DTD:

<!ELEMENT Cover (CoverTitle,CoverSubtitle)>
<!ELEMENT CoverTitle (#PCDATA)>
<!ELEMENT CoverSubtitle (#PCDATA)>
<!ELEMENT Stories (Story*)>
<!ELEMENT Story (StoryHeading*,StoryIntroduction*,StorySubHeading*,StoryText*,StoryImage*,StorySubHeading*,StoryImage*,FeatureImage*)*>
<!ELEMENT StoryHeading (#PCDATA)>
<!ELEMENT StoryIntroduction (#PCDATA)>
<!ELEMENT StoryText (#PCDATA)>
<!ELEMENT StorySubHeading (#PCDATA)>
<!ATTLIST StoryImage href CDATA "">
<!ELEMENT FeatureImage EMPTY>
<!ATTLIST FeatureImage href CDATA "">

This .dtd file describes how our InDesign document should be tagged. We’re tagging the whole document data as a Magazine. It will have a Cover with a CoverTitle and a CoverSubtitle. It will then have a set of Story items within Stories – each of which should have a StoryHeadingStoryIntroduction, and a StoryBody which contains our StorySubHeadingsStoryText, and optional StoryImage areas.

Note: You may notice that we’ve also added StoryImage and FeatureImage tagging in the .dtd file. We don’t use these in this example, but we will cover the import of these into Contensis in a separate blog post.

Step 2: Tag your Adobe InDesign content to match the DTD

The Tags panel in Adobe InDesign.

The next step involves tagging elements of the magazine style document to match the structure of the DTD. In this example we’re looking to tag the multiple stories in the magazine along with their headings, introduction text, story text, and any story images.

If you’ve never looked into tagging an InDesign document before, we’d recommend reading the basics on the Adobe support pages for InDesign. Again, if you like you can use our example document on GitHub.

Create the following tags:

Open the Tags panel in InDesign (Window > Utilities > Tags) and create the following tags:

  1. Magazine
  2. Cover
  3. CoverTitle
  4. CoverSubtitle
  5. Stories
  6. Story
  7. StoryHeading
  8. StorySubheading
  9. StoryImage
  10. FeatureImage

In your InDesign document you can now select the cover title and cover subtitle and tag them appropriately.

Once you've done that, select each appropriate element of your first story – tag the StoryHeading, StoryIntroduction, StorySubheading(s) and StoryText.

Finish building the structure for the XML output

Our goal with the tagging structure is to match the DTD ( see Step 1), which looks like this:

  • Magazine
    • Cover
      • CoverTitle
      • CoverSubtitle
  • Stories
    • Story
      • StoryTitle
      • StoryIntroduction
      • StoryBody
        • StorySubheading
        • StoryText
        • StoryImage
      • Feature Image
        • Src
        • Caption

 In InDesign you can view and manage your tag structure by opening the Structure pane (View > Structure > Show Structure). You can use this to build the final structure to match your DTD.

The Structure pane in Adobe InDesign.

Step 3: Export your content from InDesign as XML

Now load your DTD into InDesign (go to Structure Panel > Menu > Load DTD) and validate your tag structure against the DTD.

The Load DTD option highlighted in the Structure pane menu in Adobe InDesign.
The Validate from Root Element option highlighted in Structure pane menu in Adobe InDesign.

As long as there are no errors, you can now export the XML (Structure Panel > Menu > Export XML). If there are errors, make sure your tagging structure matches the .dtd file exactly.

The Export XML dialog box in Adobe InDesign.

If you are following this example to export content into Contensis, uncheck the Include DTD Declaration box and finish the export.

Step 4: Create a new Contensis Content Type to match the XML file format

Now create a new content type in your Contensis instance and call it Story

The Create new content type window in Contensis.
  1. Open up the Content Types window
  2. Create a new content type called Story
  3. Add a text field called Title
  4. Add a text field called Subtitle
  5. Add a composer field called StoryComposer
  6. Then add a markup field to this composer called StoryMarkup
  7. If you like you can also add an image field to this composer called FeatureImage, but we will be covering importing images in a future blog post.
The Contensis content type builder.

Most of this is pretty obvious. Hopefully you can see how the structure matches our XML. The content type builder in Contensis makes this part very simple to set up and adding a composer field will allow the story area to grow over time. If, for example, the InDesign story area is changed in the future – imagine you need to add tables, user quotes, or other data – then it will be much easier to update your content type to store this data.

Step 5: Create an API key

In Contensis open the Content Types & Entries menu and select API Keys. Create a new API key.

The Create a new API key window in Contensis.

Step 6: Use the Contensis Management API and write a simple script to create the entries

You can now open Visual Studio and run the project provided. You will need to install the dependencies.

Open the program.cs file and add your CMS URL. Then add the Client ID (line 28) and Shared Secret (line 29) from the API key you created earlier.

// Create the management client
var client = ManagementClient.Create("[ADD CMS URL HERE]",

You also need to ensure the Contensis project name is set to match your project. Update line 32 to match your project name.

// Get the project
var project = client.Projects.Get("[ADD YOUR CONTENSIS PROJECT NAME HERE]");

You will then need to ensure the XElement.Load is referencing your XML document. Update the path as required (line 35).

// Load the XML file into an XElement
XElement doc = XElement.Load(@"C:\Sites\indesign-import-to-contensis\dotnet-xml-import\XMLImport\finalXML.xml");

The XML file is loaded as an XElement. We can then iterate through its descendants and create a set of stories. We then iterate through the stories and call the CreateEntryFromXML function.

// Load the XML file into an XElement
XElement doc = XElement.Load(@"C:\Sites\indesign-import-to-contensis\dotnet-xml-import\XMLImport\finalXML.xml");

// Navigate the XElement to create the stories
IEnumerable<Story> stories = (from x in doc.Descendants("Stories").Elements("Story")
                              select new Story
                                  Title = x.Element("StoryHeading").Value,
                                  Introduction = x.Element("StoryIntroduction").Value,
                                  StoryBody = x.Element("StoryBody").Elements()

// Local testing in console
//Console.WriteLine("Story title: " + stories.First().Title);
//Console.WriteLine("Story Intro: " + stories.First().Introduction);
//Console.WriteLine("Story Body: " + stories.First().StoryBody);

// Iterate through stories and call entry creator
foreach (var story in stories)
    CreateEntryFromXML(project, story);

In the CreateEntryFromXML function, we are passing in the Contensis project name and the story. We create a new ‘Story’ entry, create the fields for the story, and add a new StoryMarkup field to the composer field (composers are initially empty).

// Create entries 
private static void CreateEntryFromXML(Project project, Story story)
    // Set-up a new story entry
    var storyEntry = project.Entries.New("story");

    var composer = new ComposedField();
    string html = "";
    foreach (XElement el in story.StoryBody)
        html = html + ElementCleaner(el);
    byte[] bytes = Encoding.Default.GetBytes(html);
    html = Encoding.UTF8.GetString(bytes);

    composer.Add(new ComposedFieldItem("storyMarkup", html));
    storyEntry.Set("storyComposer", composer);

    // Set each field value
    storyEntry.Set("title", story.Title);
    storyEntry.Set("subtitle", story.Introduction);

    // Save and publish the story
    Console.WriteLine({{APP}}quot;Saved story {storyEntry.Get("title")}");

We also clean the data slightly to create proper HTML headings and paragraphs.

private static string ElementCleaner(XElement el)
    string s = "";
    switch (el.Name.LocalName)
        case "StoryText":
            s = "<p>" + el.Value + "</p>";
        case "StorySubHeading":
            s = "<h2>" + el.Value + "</h2>";
    return s;

7. Run the script locally

If you’ve followed the details above exactly (using our XML file and structure) you can now run the application in Visual Studio.

Within the Entries screen within Contensis you should see 3 new stories have now been created.

A filtered view of an entry listing in Contensis showing three entries.

Editing one of the stories should provide you with the populated fields. Each of the stories should have a title, subtitle, and a story composer populated with the correct HTML headings and paragraphs.

Taking this further and next steps

Once you are happy with your content migration, next steps might include better tooling of this to aid your Adobe InDesign editors. You may, for example, want to monitor a folder on your local network for updates so that when an editor deploys their InDesign XML to this folder the entries will be automatically populated in Contensis.

Another option is to build a webpage based on this script to manage your InDesign imports.

In a future article, we will take a look at exporting the images from InDesign and bringing them into Contensis in a useful managed format.

If you’re exploring the Contensis Management API you should also read:

If your focus is on InDesign tagging, there are a few resources we found useful: