Ootempl.Table (ootempl v0.3.0)

Table structure detection and analysis for Word documents.

This module provides functionality to detect Word tables in XML, extract rows, analyze placeholders within table cells, and identify template rows based on whether they reference list data.

Template Row Detection

Template rows are identified by analyzing placeholders in each row:

  • If a placeholder references a list in the data structure (e.g., @claims.id@ where claims is a list), the row is considered a template row
  • Multiple consecutive rows referencing the same list are grouped as multi-row templates
  • If a row references multiple different lists, an error is returned

Word XML Structure

Tables in Word XML follow this structure:

<w:tbl>
  <w:tr>  <!-- table row -->
    <w:tc>  <!-- table cell -->
      <w:p>  <!-- paragraph -->
        <w:r><w:t>@claims.id@</w:t></w:r>
      </w:p>
    </w:tc>
  </w:tr>
</w:tbl>

Examples

# Find all tables in document
tables = Ootempl.Table.find_tables(xml_doc)

# Extract rows from a table
rows = Ootempl.Table.extract_rows(table)

# Analyze a row to determine if it's a template
data = %{"claims" => [%{"id" => 1}, %{"id" => 2}]}
result = Ootempl.Table.analyze_row(row, data)
# => {:ok, %{row: row, template?: true, list_key: "claims", placeholders: [...]}}

# Group consecutive template rows
grouped = Ootempl.Table.group_template_rows(rows, data)

Summary

Functions

Duplicates template rows for each item in a list with proper data scoping.

Extracts all row elements from a table.

Finds all table elements in the given XML document.

Groups consecutive template rows that reference the same list.

Inserts duplicated rows into a table at the specified position.

Removes template rows from a table.

Types

row_analysis()

@type row_analysis() :: %{
  row: Ootempl.Xml.xml_element(),
  template?: boolean(),
  list_key: String.t() | nil,
  placeholders: [Ootempl.Placeholder.placeholder()]
}

Functions

duplicate_rows(template_rows, list_key, data)

@spec duplicate_rows([Ootempl.Xml.xml_element()], String.t(), map()) :: [
  {Ootempl.Xml.xml_element(), map()}
]

Duplicates template rows for each item in a list with proper data scoping.

This is the core transformation for dynamic table generation. It takes a set of template rows (which may be a single row or multiple consecutive rows referencing the same list), clones them for each item in the list data, and creates scoped data contexts for each duplicated row group.

Parameters

  • template_rows - List of row XML elements that form the template (1+ consecutive rows)
  • list_key - The key in the data structure that references the list
  • data - The full data structure containing both the list and parent context

Returns

  • A list of tuples {row_element, scoped_data} where each row is paired with its corresponding data context for placeholder replacement

Examples

# Single template row
template_rows = [row_with_placeholder]
list_key = "claims"
data = %{"first_name" => "John", "claims" => [%{"id" => 1}, %{"id" => 2}]}

duplicate_rows(template_rows, list_key, data)
# => [
#   {cloned_row1, %{"first_name" => "John", "id" => 1}},
#   {cloned_row2, %{"first_name" => "John", "id" => 2}}
# ]

# Multi-row template
template_rows = [header_row, detail_row]
list_key = "orders"
data = %{"company" => "Acme", "orders" => [%{"id" => 100}, %{"id" => 200}]}

duplicate_rows(template_rows, list_key, data)
# => [
#   {cloned_header1, %{"company" => "Acme", "id" => 100}},
#   {cloned_detail1, %{"company" => "Acme", "id" => 100}},
#   {cloned_header2, %{"company" => "Acme", "id" => 200}},
#   {cloned_detail2, %{"company" => "Acme", "id" => 200}}
# ]

# Empty list
duplicate_rows(template_rows, "claims", %{"claims" => []})
# => []

extract_rows(table_element)

@spec extract_rows(Ootempl.Xml.xml_element()) :: [Ootempl.Xml.xml_element()]

Extracts all row elements from a table.

Finds all <w:tr> (Word table row) elements within a table.

Parameters

  • table_element - The table XML element

Returns

  • A list of row XML elements

Examples

rows = Ootempl.Table.extract_rows(table)
# => [row1, row2, row3, ...]

find_tables(xml_element)

@spec find_tables(Ootempl.Xml.xml_element()) :: [Ootempl.Xml.xml_element()]

Finds all table elements in the given XML document.

Recursively searches for <w:tbl> elements (Word table elements) in the XML tree.

Parameters

  • xml_element - The XML element to search (typically the document root)

Returns

  • A list of table XML elements

Examples

tables = Ootempl.Table.find_tables(doc)
# => [table1, table2, ...]

group_template_rows(rows, data)

@spec group_template_rows([Ootempl.Xml.xml_element()], map()) ::
  {:ok, [row_analysis()]}
  | {:error, {:multiple_lists, Ootempl.Xml.xml_element()}}

Groups consecutive template rows that reference the same list.

Analyzes all rows and groups consecutive template rows that reference the same list key together. Non-template rows and template rows referencing different lists break the grouping.

Parameters

  • rows - List of row XML elements
  • data - The data structure to check against

Returns

  • {:ok, grouped_analyses} - List of row analyses with grouping information
  • {:error, {:multiple_lists, row}} - A row references multiple lists

Examples

rows = [regular_row, template_row1, template_row2, regular_row2]
data = %{"claims" => [...]}
Ootempl.Table.group_template_rows(rows, data)
# => {:ok, [
#      %{template?: false, ...},
#      %{template?: true, list_key: "claims", ...},
#      %{template?: true, list_key: "claims", ...},
#      %{template?: false, ...}
#    ]}

insert_rows(table_element, duplicated_rows, position)

Inserts duplicated rows into a table at the specified position.

Replaces the template rows in the table with the duplicated rows. The position should be the index of the first template row in the table's row list.

Parameters

  • table_element - The table XML element
  • duplicated_rows - List of row XML elements to insert
  • position - Zero-based index where template rows start

Returns

  • Updated table XML element with duplicated rows inserted

Examples

table = find_tables(doc) |> hd()
duplicated = duplicate_rows(template_rows, "claims", data)
rows_only = Enum.map(duplicated, fn {row, _data} -> row end)

updated_table = insert_rows(table, rows_only, 1)

remove_template_rows(table_element, template_rows)

Removes template rows from a table.

Deletes the specified template rows from the table's content. This is typically called after duplicated rows have been inserted to clean up the original template.

Parameters

  • table_element - The table XML element
  • template_rows - List of template row XML elements to remove

Returns

  • Updated table XML element with template rows removed

Examples

table = find_tables(doc) |> hd()
template_rows = [row1, row2]

updated_table = remove_template_rows(table, template_rows)