SimpleIDML

repository·master·Indexed 42 days ago

https://github.com/starou/simpleidml

A Python 3.9+ library for manipulating Adobe InDesign IDML files. It enables automated document generation by separating document structure from content using XML. Key features include importing/exporting XML with content control flags, inserting content via XPath, merging pages, importing PDFs, and a SOAP client for remote automation via InDesign Server to trigger file conversions (e.g., IDML to PDF).

Tokens
10.6K
Snippets
42
Records
48
Agent score
54%

What's inside SimpleIDML

  1. What is SimpleIDML?

    master

    SimpleIDML is a Python library designed to manipulate Adobe® InDesign® IDML files. Its primary purpose is to allow developers to compose complex documents from simple pieces and to separate document data from document structure.

    Core Philosophy

    The library follows a philosophy of keeping content and structure separated. It leverages the XML Structure in InDesign, using XML files to feed documents. This isolation is intended to ease debugging and improve tracking of document state.

  2. Use the InDesign SOAP client for remote automation

    master

    SimpleIDML includes a SOAP client to interact with an InDesign Server. This allows you to remotely request .INDD files and trigger exports into various formats.

    Key features:

    • Remote Export: Use indesign.save_as() to trigger exports on the server.
    • PDF Export Presets: Supports using PDF export presets in indesign.save_as().
    • Profile Listing: Use the simpleidml_indesign_profiles.py script to list available joboptions files on the InDesign Server via SOAP.
    • Remote Document Management: Use indesign.close_all_documents() to close all open documents on the server.
    # Note: indesign.save_as() parameters are provided as a list of dictionaries
    indesign.save_as([
        {'client_workdir': '/path/to/client', 'server_workdir': '/path/to/server', 'pdfExportPresetName': 'HighQualityPrint'}
    ])
  3. Use context managers for IDMLPackage side-effect methods

    master

    When using methods that return new instances or perform side effects (like .prefix(), .insert_idml(), or .add_page_from_idml()), you must use a with context manager. This ensures that associated files are properly closed and prevents resource leaks or file locking issues (especially on Windows).

    from simple_idml import idml
    my_doc = idml.IDMLPackage("/path/to/my_main_document.idml")
    
    # CORRECT: Use a context manager
    with my_doc.prefix("main") as f:
        # perform operations on f
        pass
    
    # INCORRECT: This loses the reference and may leave files unclosed
    # my_doc = my_doc.prefix("main")
  4. Coordinate system of a Page

    master

    In IDML, a Page's position within a Spread is determined by two main components:

    1. GeometricBounds: A string of four coordinates (y1 x1 y2 x2) representing the upper-left and lower-right corners.
    2. ItemTransform: A matrix (a b c d x y) representing the translation applied to the page.

    The Page class provides properties to access these as Decimal lists and calculates the actual coordinates (x1, y1, x2, y2) by combining the bounds with the translation offsets.

  5. Insert content from one IDML into another using XPath

    master

    SimpleIDML can insert content from a source IDML document into a target IDML document using XPath syntax.

    Best Practices:

    1. Always make a backup of your files before altering them.
    2. Use .prefix() on both documents before inserting to avoid ID/reference collisions.

    Use insert_idml(source_package, at="XPATH_TARGET", only="XPATH_SOURCE") within a context manager.

    from simple_idml import idml
    
    idml_main = idml.IDMLPackage("/path/to/my_main_document.idml")
    idml_module = idml.IDMLPackage("/path/to/my_small_document.idml")
    
    # Prefix both to avoid collisions
    with idml_main.prefix("main") as p_idml_main, \
         idml_module.prefix("article") as p_idml_article:
    
        # Insert article from module into main at a specific XPath
        with p_idml_main.insert_idml(p_idml_article, at="/Root/article[3]", 
                                     only="/Root/module[1]") as f:
            print(f.stories)
  6. Explore IDML file structure with IDMLPackage

    master

    You can inspect the contents of an IDML file using the IDMLPackage class. Key attributes include:

    • spreads: A list of paths to spread XML files.
    • stories: A list of paths to story XML files.
    • font_families: A list of lxml.etree Elements representing font families. You can extract names using the Name attribute.
    • xml_structure: An lxml.etree Element representing the InDesign XML-ready structure. This structure is used to map external XML data to the document.
    • xml_structure_pretty(): A shortcut to print the XML structure as a pretty-printed string.
    from simple_idml import idml
    my_idml_package = idml.IDMLPackage("/path/to/my_main_document.idml")
    
    # List spreads and stories
    print(my_idml_package.spreads)
    print(my_idml_package.stories)
    
    # Access font names
    font_names = [e.get("Name") for e in my_idml_package.font_families]
    print(font_names)
    
    # View XML structure
    print(my_idml_package.xml_structure_pretty())
  7. Convert files using InDesign Server SOAP interface

    master

    The simple_idml.indesign.indesign.save_as function allows you to trigger file conversions (e.g., IDML to PDF) via an InDesign Server instance.

    Arguments:

    • file_path: Path to the source file.
    • formats: A list of dictionaries defining the target format and parameters. Supported formats: "jpeg", "idml", "pdf", "indd", "zip".
    • server_url: The URL of the InDesign Server.
    • client_workdir: Directory accessible by the client.
    • server_workdir: Directory accessible by the server.

    Special Configurations:

    • Windows Server: Set indesign_server_path_style="windows" if the server is on Windows.
    • FTP Access: If the client accesses the workdir via FTP, provide ftp_params containing auth (tuple of (url, user, password)), passive, keepalive, etc.
    from simple_idml.indesign import indesign
    
    # Example: Convert to PDF with CMYK color space
    response = indesign.save_as(
        "/path_to_file.indd",
        [{"fmt": "pdf", "params": {"colorSpace": "CMYK"}}],
        "http://url-to-indesign-server:port",
        "/path/to/client/workdir",
        "/path/to/indesign-server/workdir"
    )[0]
    
    with open("my_file.pdf", "wb+") as f:
        f.write(response)
  8. Combine pages from multiple IDML documents

    master

    You can merge pages from several documents into a single document using add_page_from_idml (for a single page) or add_pages_from_idml (for multiple pages).

    Workflow:

    1. Prefix all packages to avoid collisions.
    2. For add_pages_from_idml, provide a list of tuples: (package, page_number, at_xpath, only_xpath).
    from simple_idml import idml
    
    edito_idml_file = idml.IDMLPackage("magazineA-edito.idml")
    courrier_idml_file = idml.IDMLPackage("magazineA-courrier-des-lecteurs.idml")
    
    with edito_idml_file.prefix("edito") as p_edito, \
         courrier_idml_file.prefix("courrier") as p_courrier:
    
        # Add a single page
        new_idml = p_edito.add_page_from_idml(p_courrier, 
                                              page_number=1, 
                                              at="/Root", 
                                              only="/Root/page[1]")
    
        # Add multiple pages at once
        packages_to_add = [
            (p_courrier, 1, "/Root", "/Root/page[1]"),
        ]
        new_idml_bulk = p_edito.add_pages_from_idml(packages_to_add)
  9. Manage layers in an IDML package

    master

    The IDMLPackage class provides methods to manipulate layers within a document:

    • merge_layers(with_name): Merges layers based on a provided name.
    • add_note(note, author, at=path): Adds a note to the package.
    package.merge_layers(with_name='MyLayerName')
    package.add_note('This is a note', author='Developer', at='/path/to/location')
  10. Exclude or force XML nodes during import

    master

    You can use specific XML attributes to fine-tune the XML import process for specific nodes and their children:

    • simpleidml-ignorecontent: Excludes the node and its children from being imported.
    • simpleidml-forcecontent: Ensures the node and its children are included during the import process.
    <node simpleidml-ignorecontent="true">This node and children will be ignored</node>
    <node simpleidml-forcecontent="true">This node and children will be forced</node>