Skip to content

2. Examples from this document

I’d like to write a short article. The interactions between an AI and a user are presented in the article [Generating a Python Script with AI Tools], which I’ll refer to from now on as [ref1]. Interactions with Gemini and ChatGPT will only be mentioned briefly. In any case, it was impossible to present all the iterations.

I will now present examples of the specific features of my ODT/DOCX documents that the Gemini/ChatGPT converter handles correctly. It is this very document that we will submit for HTML conversion to the Gemini/ChatGPT script. We’ll see what it does with it.

2.1. Lists

The Gemini/ChatGPT converter can handle bulleted and numbered lists, even if they are nested:

2.1.1. Bulleted lists s

  • Item 1;
  • Item 2:
  • Item 3;
    • Item 3.1;
      • Item 3.1.1
      • Element 3.1.2
        • Element 3.1.2.1
        • Element 3.1.2.2
    • Element 3.2;
  • Element 4;

2.1.2. Numbered lists

  1. Element 1;
  1. Element 2;
    1. Element 2.1
      1. Element 2.1.1
        1. Element 2.1.1.1
        2. Element 2.1.1.2
      2. Element 2.1.2
    2. Element 2.2
  2. Element 3;

2.1.3. Mixed lists 1

  • Element 1;
  • Element 2:
  • Element 3;
    • Element 3.1;
      1. Element 3.1.1
      2. Element 3.1.2
        • Element 3.1.2.1
        • Element 3.1.2.2
    • Element 3.2;
  • Element 4;

2.1.4. Mixed lists 2

  1. Element 1;
  2. Element 2;
    1. Element 2.1
      1. Element 2.1.1
        • Element 2.1.1.1
        • Element 2.1.1.2
      2. Element 2.1.2
    2. Element 2.2
  3. Element 3;

2.1.5. Manually numbered lists

Manual numbering here means that the user sets the number of a numbered paragraph: [right-click on the numbered paragraph / Paragraph / Paragraph / Restart Numbering / Start With].

I start a list with a number other than 1.

  1. Element 6
  2. Element 7

Here I break the list to say something, but then I want to continue the numbering.

  1. Item 8
  2. Item 9

Then I start a new numbered list:

  1. item 11
  2. item 12

2.2. Code blocks

My courses contain a lot of code blocks. These are often formatted code (bold, keyword colors) from IDEs (Eclipse, PyCharm, WebStorm, NetBeans). This formatted code is rendered identically by the converter.

When the code is not formatted (code from Notepad or similar), the Gemini/ChatGPT converter recognizes it (Java, C#, XML, HTML, etc.) using language keywords specified in a configuration file. When it recognizes a language, it inserts a marker (fence) for MkDocs so that MkDocs can adapt the syntax highlighting of the code to the language used in the code block.

2.2.1. Enriched code blocks (Eclipse, Visual Studio, etc.)

Here are code blocks enhanced by various IDEs:

Java


package istia.st.spring.core;

import java.util.ArrayList;
import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Demo01 {

    @SuppressWarnings({ "unchecked", "resource" })
    public static void main(String[] args) {
        // Retrieve the Spring context
        ApplicationContext ctx = new ClassPathXmlApplicationContext("config-01.xml");
        // Retrieve the beans
        Person p01 = ctx.getBean("person_01", Person.class);
        Person p02 = ctx.getBean("person_02", Person.class);
        List<Person> club = ctx.getBean("club", new ArrayList<Person>().getClass());
        Apartment apt01 = ctx.getBean(Apartment.class);
...

C#


using System;

namespace Chap1 {
    class Taxes {
        static void Main(string[] args) {
            // Data arrays needed to calculate the tax
            decimal[] limits = { 4962M, 8382M, 14753M, 23888M, 38868M, 47932M, 0M };
            decimal[] coeffR = { 0M, 0.068M, 0.191M, 0.283M, 0.374M, 0.426M, 0.481M };
            decimal[] coeffN = { 0M, 291.09M, 1322.92M, 2668.39M, 4846.98M, 6883.66M, 9505.54M };

            // retrieve marital status
            bool OK = false;
            string response = null;
            while (!OK) {
                Console.Write("Are you married (Y/N)?");
                response = Console.ReadLine().Trim().ToLower();
                if (response != "y" && response != "n")
                    Console.Error.WriteLine("Incorrect answer. Please try again");
                else OK = true;
            }//while
            bool marie = response == "y";
...

Python


# ----------------------------------
def display(string):
    # displays string
    print("string=%s" % string)


# ----------------------------------
def display_type(variable):
    # displays the variable type
    print("type[%s]=%s" % (variable, type(variable)))


# ----------------------------------
def f1(param):
    # adds 10 to param
    return param + 10


# ----------------------------------
def f2():
    # returns a tuple of 3 values
    return "one", 0, 100


# -------------------------------- main program ------------------------------------
...

PHP


<?php

// strict types for function parameters
declare(strict_types=1);

// global constants
define("HALF-RATE_INCOME_LIMIT", 1551);
define("INCOME_LIMIT_SINGLE_PERSON_FOR_REDUCTION", 21037);
define("INCOME_LIMIT_FOR_COUPLES_FOR_REDUCTION", 42074);
define("REDUCTION_VALUE_HALF_PORTION", 3797);
define("SINGLE_PERSON_DISCOUNT_LIMIT", 1196);
define("COUPLE_DEDUCTION_LIMIT", 1970);
define("COUPLE_INCOME_THRESHOLD_FOR_DEDUCTION", 2627);
define("SINGLE_TAX_CAP_FOR_DEDUCTION", 1595);
define("MAXIMUM_10_PERCENT_DEDUCTION", 12502);
define("MINIMUM_10_PERCENT_DEDUCTION", 437);

// definition of local constants
$DATA = "taxpayersdata.txt";
$RESULTS = "results.txt";
$limits = array(9964, 27519, 73779, 156244, 0);
$coeffR = array(0, 0.14, 0.3, 0.41, 0.45);
$coeffN = array(0, 1394.96, 5798, 13913.69, 20163.45);

// read data
$data = fopen($DATA, "r");
if (!$data) {
  print "Unable to open the data file [$DATA] for reading\n";
  exit;
}

...

ECMAScript


'use strict';
// this is a comment
// constant
const name = "dupont";
// display on screen
console.log("name: ", name);
// an array with elements of different types
const array = ["one", "two", 3, 4];
// its number of elements
let n = array.length;
// a loop
for (let i = 0; i < n; i++) {
  console.log("array[", i, "] = ", array[i]);
}
// Initializing 2 variables with the contents of an array
let [string1, string2] = ["string1", "string2"];
// concatenating the two strings
const string3 = string1 + string2;
// display result
console.log([string1, string2, string3]);
...

VBScript


' Calculating a taxpayer's tax
' the program must be called with three parameters: married children salary
' married: character O if married, N if unmarried
' children: number of children
' salary: annual salary without cents

' mandatory variable declaration
  Option Explicit
  Dim error
  
' retrieve the arguments and check their validity
  Dim wife, children, salary
  error = GetArguments(wife, children, salary)
  ' error?
  If error(0) <> 0 Then wscript.echo error(1) : wscript.quit error(0)

' Retrieve the data needed to calculate the tax
  Dim limits, coeffR, coeffN
  error = getData(limits, coeffR, coeffN)
  ' error?
  If error(0) ≠ 0 Then wscript.echo error(1) : wscript.quit 5

  ' Display the result
  wscript.echo "tax=" & calculateTax(spouse,children,salary,limits,coeffR,coeffN)
  
  ' exit without error
  wscript.quit 0

XML


<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <configSections>
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
    </sectionGroup>
  </configSections>

  <spring>
    <context>
      <resource uri="config://spring/objects" />
    </context>
    <objects xmlns="http://www.springframework.net">
      <object name="dao" type="Dao.DataBaseImpot, ImpotsV7-dao">
        <constructor-arg index="0" value="MySql.Data.MySqlClient"/>
        <constructor-arg index="1" value="Server=localhost;Database=bdimpots;Uid=admimpots;Pwd=mdpimpots;"/>
        <constructor-arg index="2" value="select limit, coeffr, coeffn from tranches"/>
      </object>
      <object name="job" type="Job.JobTax, TaxesV7-job">
        <constructor-arg index="0" ref="dao"/>
      </object>
    </objects>
  </spring>
</configuration>

2.2.2. Plain text code blocks

Here are some examples of plain text:

Execution results

Note that the code does not start on line 1.

C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/bases/bases_01.py
name=dupont
list[0] = one
list[1] = two
list[2] = 3
list[3] = 4
[string1, string2, string1string2]
string = string1
type[4]=<class 'int'>
type[string1] = <class 'str'>
type[['one', 'two', 3, 4]] = <class 'list'>
type[has changed] = <class 'str'>
res1 = 14s
(res1, res2, res3) = [one, 0, 100]
list[0] = one
list[1] = 0
list[2] = 100
list[0] = 8
list[1] = 5
sum=13

Process finished with exit code 0

This case required dozens of iterations. The converter could never find the line number of the first line of code. Finally, I asked Gemini how to format the code block so that it would recognize it. Here is what it replied:

Image

I followed these steps and it worked. I was using the method via the list icon (Customize). So you may need to modify certain elements of the ODT/DOCX document to get the result you want.

The converter can handle unnumbered code blocks.

list[0]=one
list[1]=0
list[2]=100
list[0]=8
list[1]=5
sum=13

Here are the same examples as in the "2.2.1 " section, but without additional information. In this case, the keywords in the configuration file will guide the converter to the correct language.

Java

package istia.st.spring.core;

import java.util.ArrayList;
import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Demo01 {

@SuppressWarnings({ "unchecked", "resource" })
public static void main(String[] args) {
// Retrieve the Spring context
ApplicationContext ctx = new ClassPathXmlApplicationContext("config-01.xml");
// Retrieve the beans
Person p01 = ctx.getBean("person_01", Person.class);
Person p02 = ctx.getBean("person_02", Person.class);
List<Person> club = ctx.getBean("club", new ArrayList<Person>().getClass());
Apartment apt01 = ctx.getBean(Apartment.class);
...

C#

using System;

namespace Chap1 {
    class Taxes {
        static void Main(string[] args) {
            // Data tables required for tax calculation
            decimal[] limits = { 4962M, 8382M, 14753M, 23888M, 38868M, 47932M, 0M };
            decimal[] coeffR = { 0M, 0.068M, 0.191M, 0.283M, 0.374M, 0.426M, 0.481M };
            decimal[] coeffN = { 0M, 291.09M, 1322.92M, 2668.39M, 4846.98M, 6883.66M, 9505.54M };

            // retrieve marital status
            bool OK = false;
            string response = null;
            while (!OK) {
                Console.Write("Are you married (Y/N)?");
                response = Console.ReadLine().Trim().ToLower();
                if (response != "y" && response != "n")
                    Console.Error.WriteLine("Incorrect answer. Please try again");
                else OK = true;
            }//while
            bool marie = response == "y";
...

Python

# ----------------------------------
def display(string):
    # print string
    print("string=%s" % string)


# ----------------------------------
def display_type(variable):
    # displays the variable type
    print("type[%s]=%s" % (variable, type(variable)))


# ----------------------------------
def f1(param):
    # adds 10 to param
    return param + 10


# ----------------------------------
def f2():
    # returns a tuple of 3 values
    return "one", 0, 100


# -------------------------------- main program ------------------------------------
...

PHP

<?php

// strict types for function parameters
declare(strict_types=1);

// global constants
define("HALF-RATE_INCOME_LIMIT", 1551);
define("INCOME_LIMIT_SINGLE_PERSON_FOR_REDUCTION", 21037);
define("INCOME_LIMIT_FOR_COUPLES_FOR_REDUCTION", 42074);
define("REDUCTION_VALUE_HALF_PORTION", 3797);
define("SINGLE_PERSON_DISCOUNT_LIMIT", 1196);
define("COUPLE_DEDUCTION_LIMIT", 1970);
define("COUPLE_INCOME_THRESHOLD_FOR_DEDUCTION", 2627);
define("SINGLE_TAX_CAP_FOR_DEDUCTION", 1595);
define("MAXIMUM_10_PERCENT_DEDUCTION", 12502);
define("MINIMUM_10_PERCENT_DEDUCTION", 437);

// definition of local constants
$DATA = "taxpayersdata.txt";
$RESULTS = "results.txt";
$limits = array(9964, 27519, 73779, 156244, 0);
$coeffR = array(0, 0.14, 0.3, 0.41, 0.45);
$coeffN = array(0, 1394.96, 5798, 13913.69, 20163.45);

// read data
$data = fopen($DATA, "r");
if (!$data) {
  print "Unable to open the data file [$DATA] for reading\n";
  exit;
}

...

ECMAScript

'use strict';
// this is a comment
// constant
const name = "dupont";
// a screen display
console.log("name: ", name);
// An array with elements of different types
const array = ["one", "two", 3, 4];
// its number of elements
let n = array.length;
// a loop
for (let i = 0; i < n; i++) {
  console.log("array[", i, "] = ", array[i]);
}
// Initializing 2 variables with the contents of an array
let [string1, string2] = ["string1", "string2"];
// concatenating the two strings
const string3 = string1 + string2;
// display result
console.log([string1, string2, string3]);
...

VBScript

' Calculating a taxpayer's tax
' the program must be called with three parameters: married, children, salary
' married: character O if married, N if unmarried
' children: number of children
' salary: annual salary without cents

' mandatory variable declaration
  Option Explicit
  Dim error

' retrieve the arguments and check their validity
  Dim wife, children, salary
  error = GetArguments(wife, children, salary)
  ' error?
  If error(0) <> 0 Then wscript.echo error(1) : wscript.quit error(0)

' Retrieve the data needed to calculate the tax
  Dim limits, coeffR, coeffN
  error = getData(limits, coeffR, coeffN)
  ' error?
  If error(0) ≠ 0 Then wscript.echo error(1) : wscript.quit 5

  ' Display the result
  wscript.echo "tax=" & calculateTax(spouse,children,salary,limits,coeffR,coeffN)

  ' exit without error
  wscript.quit 0

XML

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <configSections>
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
    </sectionGroup>
  </configSections>

  <spring>
    <context>
      <resource uri="config://spring/objects" />
    </context>
    <objects xmlns="http://www.springframework.net">
      <object name="dao" type="Dao.DataBaseImpot, ImpotsV7-dao">
        <constructor-arg index="0" value="MySql.Data.MySqlClient"/>
        <constructor-arg index="1" value="Server=localhost;Database=bdimpots;Uid=admimpots;Pwd=mdpimpots;"/>
        <constructor-arg index="2" value="select limit, coeffr, coeffn from tranches"/>
      </object>
      <object name="metier" type="Metier.ImpotMetier, ImpotsV7-metier">
        <constructor-arg index="0" ref="dao"/>
      </object>
    </objects>
  </spring>
</configuration>

HTML

<!DOCTYPE HTML>
<HTML>
    <head>
        <title>Laragon</title>

        <link href="<a href="view-source:https://fonts.googleapis.com/css?family=Karla:400">https://fonts.googleapis.com/css?family=Karla:400</a> " rel="stylesheet" type="text/css">

        <style>
            HTML, body {
                height: 100%;
            }

            body {
                margin: 0;
                padding: 0;
                width: 100%;
                display: table;
                font-weight: 100;
                font-family: 'Karla';
            }

            .container {
                text-align: center;
                display: table-cell;
                vertical-align: middle;
            }

            .content {
                text-align: center;
                display: inline-block;
            }

            .title {
                font-size: 96px;
            }

            .opt {
                margin-top: 30px;
            }

            .opt a {
              text-decoration: none;
              font-size: 150%;
            }

            a:hover {
              color: red;
            }
        </style>
    </head>
    <body>
        <div class="container">
            <div class="content">
                <div class="title" title="Laragon">Laragon</div>

                <div class="info"><br />
                      Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11<br/>
                      PHP version: 7.2.11   <span><a title="phpinfo()" href="<a href="view-source:http://localhost/?q=info">/?q=info</a>">info</a></span><br />
                      Document Root: C:/myprograms/laragon-lite/www<br />

                </div>

The Gemini / ChatGPT converter preserves external links in ODT / DOCX documents. For example, Gemini 3 or [Generate a Python script with AI tools].

It can handle a link to a chapter Link to a chapter

A reference to a chapter:2.1.1 .

A reference to a preceding text marker:

A reference to a text anchor that follows: GitHub

2.4. Text formatting

The converter supports bold, italic, underlined, and highlighted text. It preserves the highlight color.

Text with words in bold, italics, underlined, or highlighted.

This also applies to links: [Generate a Python script with AI tools].

The converter also handles character colors.


It also handles the top and bottom borders of paragraphs.



  • It also handles the top and bottom borders of paragraphs.
  • It also handles the top and bottom borders of paragraphs.
  • It also handles the top and bottom borders of paragraphs.

  1. It also handles the top and bottom borders of paragraphs.
  2. It also manages the top and bottom borders of paragraphs.
  3. It also handles the top and bottom borders of paragraphs.
  1. Text with words in bold, italics, underlined, or highlighted.
  2. This also applies to links: [Generate a Python script with AI tools].
  3. The converter also handles character colors.
It also handles the paragraph background
  • It also handles the paragraph background
  1. It also handles the paragraph background

2.5. A title can also be enhanced.

2.6. Images

The Gemini/ChatGPT converter can handle images and image tables:

Image

In ODT documents, it is common to have drawings. Despite dozens of attempts, Gemini was unable to generate a script that would produce an image (such as a screenshot) of the drawing. Thus, in the example above, Image 5 is a screenshot of a drawing from an ODT document.

All images are clickable to enlarge. If you click on image [1-3] above, you get the following enlarged view:

2.7. Characters to protect

A MkDocs site1 has pages whose content is not HTML but Markdown. If the ODT/DOCX document contains characters that exist in Markdown, they may be interpreted by MkDocs and therefore not render as expected. Here are two examples:

The asterisk * has a Markdown meaning. The following line may therefore be misinterpreted:

The tax I is then equal to 0.15*R – 2072.5*nbParts.

Another example is when you want to insert a Markdown code block into your document like this:

# Word/ODT to HTML Site Converter (MkDocs)

🔗 **[See the generated demo site](https://stahe.github.io/word-odt-vers-html-janv-2026/)**

---

## 📝 Description

This project aims to provide readers with a Python converter for converting Word or ODT documents into a static HTML site.

When the ODT/DOCX document is suitable, the converter generates an HTML site using **MkDocs** that has the professional look of sites produced by Pandoc.

## 🤖 Development Background

This converter was built entirely by the **Gemini 3** AI (with a pro subscription). It is the result of successive iterations to finely manage the structure of ODT (OpenDocument Text) documents.

## ✨ Features

The `convert.py` script performs the following actions:

* **ODT to Markdown Conversion**: Parses the `.odt` (XML) file to extract its structure.
* **Heading Management**: Automatically generates the Table of Contents (TOC) and sidebar navigation.
* **Code Blocks**: Automatic language detection, syntax highlighting, and **precise line numbering** (`start-value` attributes).
* **Lists**: Support for bulleted and numbered lists with correct indentation.
* **Formatting**: Support for *bold*, *italic*, *underline*, and *highlighting* (preserving original colors).
* **Images**: Automatic extraction and embedding of images contained in the document.
* **Configuration**: Customization via a `config.py` file (Footer, Google Analytics, etc.).

## 🚀 Installation

### Prerequisites

* Python 3.x
* The following libraries:

```bash
pip install odfpy unidecode mkdocs mkdocs-material

```

2.8. Tables

A table can contain various types of content:

1
2

package istia.st.spring.core;

import java.util.ArrayList;
import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Demo01 {


    
3
4

# ----------------------------------
def display(string):
    # displays string
    print("string=%s" % string)


# ----------------------------------
def display_type(variable):
    # displays the variable type
    print("type[%s]=%s" % (variable, type(variable)))


An array containing links:

The Gemini / ChatGPT converter preserves
external links from the ODT / DOCX document. For example
It can handle a link to a chapter Link to a chapter
A reference to a chapter:2.1.1 .
A reference to a preceding text anchor:
A reference to a text marker that follows: GitHub

A table whose cells contain another table:

html:form
is used both to generate the HTML tag <form> and to provide information to the controller that will process this form:
action
url where the form values will be sent
name
name of the HTML form. This is also the name of the bean that will store the form's values
type
Name of the class that must be instantiated to obtain the form storage bean
Note that the method used to send form parameters (GET/POST) to the controller is not specified. This could be done using the method attribute. If this attribute is missing, the POST method is used by default.
html:text
Used to generate the <input type="text" value="..."> tag:
property
name of the field in the form bean that will be associated with the input field. When the form is submitted to the server (client -> server), the bean field will take the value of the input field. When the form is displayed (server -> client), the value contained in the bean field is displayed in the input field.
It also manages the background of the paragraph within a table
  • It also manages the background of the paragraph in a table
  1. It also manages the background of the paragraph in a table
 

2.9. Footnotes

The Gemini2 / ChatGPT converter handles footnotes. Here is another footnote3 .