Skip to content

Word .docx templating system that is designer (no scripting tags) and server-friendly (no word installation required)

License

Notifications You must be signed in to change notification settings

antonmihaylov/OpenXmlTemplates

Repository files navigation

GitHub contributors GitHub issues License: LGPL v3


Open XML Templates

A .NET Standard Word documents templating system that doesn't need Word installed and is both designer and developer friendly

Report Bug · Request Feature

Table of Contents

About The Project

With the library you can easily:

  • Create word templates using only content controls and their tags
  • Replace the content controls in a template with actual data from any source (json and a dictionary are natively supported)
  • Repeat text based on a list (with nested variables and lists)
  • Conditionally remove text section
  • Specify a singular and a plural word that should be used conditionally, based of the length of a list

It is server-friendly, because it doesn't require Word installed. Only the Open XML Sdk is used for manipulating the document.

It is friendly to the designer of the document templates, because they don't need to have any coding skills and they won't have to write any script-like snippets in the word document. Everything is instead managed by native Word content controls.

Built With

Getting Started

To get a local copy up and running use one of the following methods:

Install via nuget:

nuget install OpenXMLTemplates

or clone the repo and reference OpenXMLTemplates.csproj in your project

git clone https://github.com/antonmihaylov/OpenXmlTemplates.git

Usage

To create a template:

  1. Open your document in Word
  2. Open the Developer tab in the ribbon (if you don't have it - open File tab, go to Options > Customize Ribbon. Under Customize the Ribbon and under Main Tabs, select the Developer check box.)
  3. Under the Controls tab - add a new Content Control of your liking (Plain text is the simplest one - just text with formatting)
  4. Select the newly added Content control and click Properties in the Developer ribbon
  5. Change the Tag in the popup window to match one of the supported tags (the tag name is case-insensitive - variable is the same as VARIABLE)

To create a document from a template, using the default content control replacers:

  1. Create a new TemplateDocument. This represents your document and it neatly handles all content controls in it, as well as the open/save/close file logic. Don't forget to call Dispose() on it after you're done, or just use an "using" statement:
         using var doc = new TemplateDocument("path/to/my/document.docx");
  2. Create a new VariableSource (currently available sources are a json string and a dictionary. You can also create your own class that implements IVariableSource). The variable source handles your data and extracts it in a way that the template engine can read it.
        var src = new VariableSource(jsonString); 
  3. Create an OpenXmlTemplateEngine. A default one is provided (DefaultOpenXmlTemplateEngine). The default one contains all control replacers listed in the readme. You can disable/enable a control replacer by modifying the IsEnabled variable in it. You can also register your own replacer by calling RegisterReplacer on the engine.
        var engine = new DefaultOpenXmlTemplateEngine();
  4. Call the ReplaceAll method on the engine using the document and the variable source
        engine.ReplaceAll(doc, src);
  5. Save the edited document
       doc.SaveAs("result.docx"); 

If you want to remove the content controls from the final document, but keep the content you have two options:

  1. Use the RemoveControlsAndKeepContent method on the TemplateDocument object or
  2. Set the KeepContentControlAfterReplacement boolean of the OpenXmlTemplateEngine

Supported Tags

Note that if your variable names contain an underscore results may be unpredictable! Note: to insert a new line, add a new line character (\r\n, \n\r, \n) in the data you provide, it will be parsed as a line break

Variable

  • Tag name: "variable_<NAME OF YOUR VARIABLE>" (the variable keyword is case-insensitive)

  • Replaces the text inside the control with the value of the variable with the provided name

  • Supports nested variable names (e.g. address.street)

  • Supports array access (e.g. names.[0])

  • Supports nested variables using rich text content controls. For example: a rich text content control with tag name address, followed by an inner content control with tag name variable_street is the same as variable.street

  • Note that if you reference a variable from a nested control, that is available in the outer scope, but not in the inner scope - the outer scope variable will be used.

  • Supports variables inside repeating items, the variable name is relative to the repeated item.

    Example:

Repeating

  • Tag name: "repeating_<NAME OF YOUR VARIABLE>" (the repeating keyword is case-insensitive)

  • Repeats the content control as many times as there are items in the variable identified by the provided variable name.

  • Complex fields with inner content controls are supported. Use the inner controls as you would normally, except that the variable names will be relative to the list item. All default content controls can be nested.

  • Note that if you reference a variable from a nested control, that is available in the outer scope, but not in the inner scope (the list item) - the outer scope variable will be used. That is useful if you want to include something in your list item's text output that is available in the global scope only.

  • Add an inner content control with tag variable_index to insert the index of the current item (1-based)

  • You can add extra arguments to the tag name (e.g. "repeating_<VARIABLE NAME>_extraparam1_extraparam2..."):

    • "separator_<INSERT SEPARATOR STRING>"- inserts a separator after each item (e.g. "repeating_<VARIABLE NAME>separator, " - this inserts a comma between each item)
    • "lastSeparator_<INSERT SEPARATOR STRING>"- inserts a special sepeartor before the last item (e.g. "repeating_<VARIABLE NAME>separator, _lastSeparator_and " - this inserts a comma between each item and an "and" before the last item)

    Example:

Conditional remove

  • Tag name: "conditionalRemove_<ENTER THE NAME OF YOUR VARIABLE>" (the conditionalRemove keyword is case-insensitive)

  • Removes content controls based on the value of the provided variable

  • If the variable value is evaluated to true (True, "true", 1, "1", non-empty list, non-empty dict) the control stays. If it doesn't - it is removed

  • You can add extra arguments to the tag name (e.g. "conditionalRemove_<VARIABLE NAME>_extraparam1_extraparam2..."):

    • "OR" - applies an OR operation to the values. The control is removed if none of the values between the operator are true. (e.g. "conditionalRemove_<VARIABLE NAME 1>or<VARIABLE NAME 2>")
    • "EQ", "GT" and "LT" - checks if the value of the first variable equals ("eq"), is greather than ("gt") or is less than ("lt") the second variable's value. (e.g. "conditionalRemove_<VARIABLE NAME 1>lt<VARIABLE NAME 2>"). You can also provide a value to the operation, instead of a variable name (e.g. "conditionalRemove_<VARIABLE NAME>_lt_2). The control is removed if the supplied condition evaluates to false.
    • "NOT" - reverses the last value. (e.g. "conditionalRemove_<VARIABLE NAME>_not)
  • You can also chain multiple arguments, e.g. "conditionalRemove_<VARIABLE NAME 1>not_or_<VARIABLE NAME 2>and<VARIABLE NAME 3>". Note that the expression is evaluated from left to right, with no recognition for the order of operations.

    Example:

Singular dropdown

  • Works only with Dropdown content control!

  • Tag name: "singular_<ENTER THE NAME OF YOUR LIST VARIABLE>" (the singular keyword is case-insensitive)

  • Replaces the text inside a content control with the appropriate value based on the length of the list variable with the provided name

  • If the list variable has a length of 1 (or 0) the first value from the dropdown is used. If it's more than one - the second value from the dropdown is used.

    Example:

Conditional dropdown

  • Works only with Dropdown content controls!
  • Tag name: "conditional_<ENTER THE NAME OF YOUR LIST VARIABLE>" (the conditional keyword is case-insensitive)
  • Replaces the text inside a content control with the appropriate value based on the length of the variable with the provided name
  • If it's evaluated to true (aka is true, "true", 1, "1", non-empty list, non-empty dict) - the first value from the dropdown is used. If it's not - the second value is used.
  • You can use the same extra arguments as in the Conditional remove replacer

Images

  • Works only with Image content controls!
  • Tag name: "image_<ENTER THE NAME OF YOUR VARIABLE>" (the image keyword is case-insensitive)
  • Put an image path as a variable in your data, the replacer will get the image and will embed it in the Image content control.

Numeric Formatting

Variables that are numeric can be formatted. Follow the variable with () and include the formmater inside the parenthesis. Example: variable_thing1(N2)

Any standard .net numeric formatter may be used. Numeric Formatters

Roadmap

See the open issues for a list of proposed features (and known issues).

Contributing

Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

License

Distributed under the LGPLv3 License. See LICENSE for more information.

Contact

Anton Mihaylov - antonmmihaylov@gmail.com

Project Link: https://github.com/antonmihaylov/OpenXmlTemplates