Skip to content

Commit

Permalink
Merge pull request #18 from Amberg/workitems/MetadataAttribute
Browse files Browse the repository at this point in the history
V2.3.0 Final
  • Loading branch information
Amberg authored Jun 19, 2024
2 parents e1cfd74 + 17c0b63 commit 14b54f1
Show file tree
Hide file tree
Showing 28 changed files with 2,322 additions and 1,895 deletions.
724 changes: 412 additions & 312 deletions DocxTemplater.Images/ImageFormatter.cs

Large diffs are not rendered by default.

15 changes: 14 additions & 1 deletion DocxTemplater.Markdown/MarkDownFormatterConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,22 @@ public List<ListLevelConfiguration> UnorderedListLevelConfiguration
get;
}

/// <summary>
/// Name of a list style in the template document applied to lists.
/// If this style is not found, a style is created based on <see cref="UnorderedListLevelConfiguration"/>
/// </summary>
public string UnorderedListStyle { get; set; } = "md_ListStyle";

/// <summary>
/// Name of a list style in the template document applied to lists.
/// If this style is not found, a style is created based on <see cref="OrderedListLevelConfiguration"/>
/// </summary>
public string OrderedListStyle { get; set; } = "md_OrderedListStyle";


/// <summary>
/// Name of a table style in the template document applied to tables.
/// </summary>
public string TableStyle { get; set; }
public string TableStyle { get; set; } = "md_TableStyle";
}
}
51 changes: 23 additions & 28 deletions DocxTemplater.Markdown/MarkdownFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Markdig;
using Markdig.Parsers;
using System;
using System.Linq;

namespace DocxTemplater.Markdown
{
Expand All @@ -16,6 +17,7 @@ public class MarkdownFormatter : IFormatter, IFormatterInitialization
private IVariableReplacer m_variableReplacer;
private IScriptCompiler m_scriptCompiler;
private int m_nestingDepth;
private MainDocumentPart m_mainDocumentPart;

public MarkdownFormatter(MarkDownFormatterConfiguration configuration = null)
{
Expand All @@ -34,37 +36,35 @@ public void ApplyFormat(FormatterContext context, Text target)
{
return;
}

if (m_nestingDepth > 3)
{
throw new OpenXmlTemplateException("Markdown nesting depth exceeded");
}
m_nestingDepth++;


m_nestingDepth++;
var root = target.GetRoot();
if (root is OpenXmlPartRootElement openXmlPartRootElement && openXmlPartRootElement.OpenXmlPart != null)
{
if (openXmlPartRootElement.OpenXmlPart is MainDocumentPart mainDocumentPart)
var pipeline = new MarkdownPipelineBuilder().UsePipeTables().Build();
var markdownDocument = MarkdownParser.Parse(mdText, pipeline);

var parentParagraph = target.GetFirstAncestor<Paragraph>();
// split the paragraph at the target
var paragraph = (Paragraph)parentParagraph.SplitAfterElement(target).First();

var renderer = new MarkdownToOpenXmlRenderer(paragraph, target, m_mainDocumentPart, m_configuration);
var firstParagraph = renderer.CurrentParagraph;
renderer.Render(markdownDocument);
var lastParagraph = renderer.CurrentParagraph;
try
{
var pipeline = new MarkdownPipelineBuilder().UsePipeTables().Build();
var markdownDocument = MarkdownParser.Parse(mdText, pipeline);
var renderer = new MarkdownToOpenXmlRenderer(target, mainDocumentPart, m_configuration);
var firstParagraph = renderer.CurrentParagraph;
renderer.Render(markdownDocument);
var lastParagraph = renderer.CurrentParagraph;
try
{
target.RemoveWithEmptyParent();
DoVariableReplacementInParagraphs(firstParagraph, lastParagraph);
}
catch (Exception e)
{
throw new OpenXmlTemplateException("Variable Replacement in markdown failed", e);
}
target.RemoveWithEmptyParent();
DoVariableReplacementInParagraphs(firstParagraph, lastParagraph);
}
else
catch (Exception e)
{
throw new OpenXmlTemplateException("Markdown currently only supported in MainDocument");
throw new OpenXmlTemplateException("Variable Replacement in markdown failed", e);
}
}
m_nestingDepth--;
Expand All @@ -78,7 +78,7 @@ private void DoVariableReplacementInParagraphs(Paragraph firstParagraph, Paragra
{
if (currentParagraph.InnerText.Contains('{'))
{
var processor = new XmlNodeTemplate(currentParagraph, m_processSettings, m_modelLookup, m_variableReplacer, m_scriptCompiler);
var processor = new XmlNodeTemplate(currentParagraph, m_processSettings, m_modelLookup, m_variableReplacer, m_scriptCompiler, m_mainDocumentPart);
processor.Process();
}
currentParagraph = currentParagraph.NextSibling<Paragraph>();
Expand All @@ -90,19 +90,14 @@ private void DoVariableReplacementInParagraphs(Paragraph firstParagraph, Paragra
while (currentParagraph != lastParagraph);
}

public void Initialize(IModelLookup modelLookup, ProcessSettings processSettings)
{
m_modelLookup = modelLookup;
m_processSettings = processSettings;
}

public void Initialize(IModelLookup modelLookup, IScriptCompiler scriptCompiler, IVariableReplacer variableReplacer,
ProcessSettings processSettings)
ProcessSettings processSettings, MainDocumentPart mainDocumentPart)
{
m_modelLookup = modelLookup;
m_processSettings = processSettings;
m_variableReplacer = variableReplacer;
m_scriptCompiler = scriptCompiler;
m_mainDocumentPart = mainDocumentPart;
}
}
}
31 changes: 20 additions & 11 deletions DocxTemplater.Markdown/MarkdownToOpenXmlRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@ private sealed record Format(bool Bold, bool Italic, string Style);
private readonly Stack<Format> m_formatStack = new();
private OpenXmlCompositeElement m_parentElement;
private bool m_lastElemntWasNewLine;
private readonly RunProperties m_targetRunProperties;

public MarkdownToOpenXmlRenderer(
Text previousOpenXmlNode,
Paragraph parentElement,
Text target,
MainDocumentPart mainDocumentPart,
MarkDownFormatterConfiguration configuration)
{
// extract style from target run element
m_targetRunProperties = ((Run)target.Parent).RunProperties;
m_lastElemntWasNewLine = true;
m_formatStack.Push(new Format(false, false, null));
m_parentElement = previousOpenXmlNode.GetFirstAncestor<Paragraph>();
m_parentElement = parentElement;
ObjectRenderers.Add(new LiteralInlineRenderer());
ObjectRenderers.Add(new ParagraphRenderer());
ObjectRenderers.Add(new LineBreakLineRenderer());
Expand All @@ -53,30 +57,35 @@ public void Write(ReadOnlySpan<char> content)
if (!content.IsEmpty)
{
var text = new Text(content.ToString());
if (string.IsNullOrWhiteSpace(text.Text))
if (char.IsWhiteSpace(content[^1]) || char.IsWhiteSpace(content[0]))
{
text.Space = SpaceProcessingModeValues.Preserve;
}

var newRun = new Run(text);
if (m_targetRunProperties != null)
{
// we merge existing styles with styles from markdown
newRun.RunProperties = (RunProperties)m_targetRunProperties.CloneNode(true);
}
var format = m_formatStack.Peek();
if (format.Bold || format.Italic || format.Style != null)
{
RunProperties run1Properties = new();
if (format.Bold)
newRun.RunProperties ??= new RunProperties();

if (format.Bold && newRun.RunProperties.Bold == null)
{
run1Properties.Append(new Bold());
newRun.RunProperties.AddChild(new Bold());
}
if (format.Italic)
if (format.Italic && newRun.RunProperties.Italic == null)
{
run1Properties.Append(new Italic());
newRun.RunProperties.AddChild(new Italic());
}
newRun.RunProperties = run1Properties;

//add style
if (format.Style != null)
{
var runStyle = new RunStyle { Val = format.Style };
newRun.RunProperties.Append(runStyle);
newRun.RunProperties.AddChild(runStyle);
}
}
m_parentElement.Append(newRun);
Expand Down
144 changes: 14 additions & 130 deletions DocxTemplater.Markdown/Renderer/ListRenderer.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Markdig.Syntax;
using System.Linq;

namespace DocxTemplater.Markdown.Renderer
{
Expand All @@ -10,42 +9,41 @@ internal sealed class ListRenderer : OpenXmlObjectRenderer<ListBlock>
private int m_level = -1;
private int m_levelWithSameOrdering = -1;
private bool? m_lastLevelOrdered;

private readonly MainDocumentPart m_mainDocumentPart;
private readonly MarkDownFormatterConfiguration m_configuration;
private AbstractNum m_currentAbstractNumNotOrdered;
private AbstractNum m_currentAbstractNumOrdered;
private NumberingInstance m_currentNumberingInstanceOrdered;
private NumberingInstance m_currentNumberingInstanceNotOrdered;
private NumberingDefinitionsPart m_numberingDefinitionsPart;
private string m_listParagraphStyle;
private readonly ListStyleFactory m_orderedListStyleFactory;
private readonly ListStyleFactory m_unorderedListStyleFactory;

public ListRenderer(MainDocumentPart mainDocumentPart, MarkDownFormatterConfiguration configuration)
{
m_mainDocumentPart = mainDocumentPart;
m_configuration = configuration;
m_orderedListStyleFactory = new ListStyleFactory(true, configuration, mainDocumentPart);
m_unorderedListStyleFactory = new ListStyleFactory(false, configuration, mainDocumentPart);
}

protected override void Write(MarkdownToOpenXmlRenderer renderer, ListBlock listBlock)
{

var listStyleFactory = listBlock.IsOrdered ? m_orderedListStyleFactory : m_unorderedListStyleFactory;
listStyleFactory.EnsureExists();

StartListLevel(listBlock.IsOrdered);
listStyleFactory.EnsureLevelDefinitionExists(m_levelWithSameOrdering);
try
{
var numberingInstance = listBlock.IsOrdered ? m_currentNumberingInstanceOrdered : m_currentNumberingInstanceNotOrdered;

foreach (var item in listBlock)
{
var numberingProps =
new NumberingProperties(
new NumberingLevelReference() { Val = m_levelWithSameOrdering },
new NumberingId() { Val = numberingInstance.NumberID }
new NumberingId() { Val = listStyleFactory.Numbering.NumberID }
);
var listItem = (ListItemBlock)item;
var listParagraph = new Paragraph();
var paragraphProperties = new ParagraphProperties(numberingProps)
{
ParagraphStyleId = new ParagraphStyleId() { Val = m_listParagraphStyle }
ParagraphStyleId = new ParagraphStyleId()
{
Val = listStyleFactory.ListParagraphStyle
}
};
listParagraph.ParagraphProperties = paragraphProperties;
renderer.ReplaceIfCurrentParagraphIsEmpty(listParagraph);
Expand All @@ -64,137 +62,23 @@ protected override void Write(MarkdownToOpenXmlRenderer renderer, ListBlock list
}
}

private void CrateListParagraphStyleIfNotExistent()
{
if (m_listParagraphStyle == null)
{
var part = m_mainDocumentPart.StyleDefinitionsPart;
if (part == null)
{
part = m_mainDocumentPart.AddNewPart<StyleDefinitionsPart>();
part.Styles = new Styles();
}

var style = part.Styles?.Elements<Style>().FirstOrDefault(x => x.StyleId == "ListParagraph");
if (style == null)
{
style = new Style()
{
Type = StyleValues.Paragraph,
StyleId = "ListParagraph",
CustomStyle = true,
StyleName = new StyleName() { Val = "List Paragraph" },
UIPriority = new UIPriority() { Val = 34 },
PrimaryStyle = new PrimaryStyle(),
Rsid = new Rsid() { Val = "004004FF" }
};
var styleParagraphProperties = new StyleParagraphProperties();
styleParagraphProperties.Append(new ContextualSpacing());
style.Append(styleParagraphProperties);
part.Styles?.Append(style);
part.Styles?.Save();
}
m_listParagraphStyle = "ListParagraph";
}
}

private void StartListLevel(bool ordered)
{
m_level++;
if (m_level == 0)
{
CrateListParagraphStyleIfNotExistent();
CrateNumberingInstance(ordered);
}

if (m_lastLevelOrdered != ordered)
{
m_levelWithSameOrdering = -1;
}
m_levelWithSameOrdering++;
m_lastLevelOrdered = ordered;

// try to find level
var abstractNumberDefinition = GetAbstractNumberingDefinition(ordered);
var level = abstractNumberDefinition.ChildElements.OfType<Level>().FirstOrDefault(x => x.LevelIndex == m_level);
if (level == null)
{
var levelConfig = ordered ? m_configuration.OrderedListLevelConfiguration : m_configuration.UnorderedListLevelConfiguration;
var config = levelConfig[m_levelWithSameOrdering % levelConfig.Count];
level = new Level()
{
LevelIndex = m_level,
NumberingFormat = new NumberingFormat()
{
Val = config.NumberingFormat
},
LevelText = new LevelText() { Val = config.LevelText },
StartNumberingValue = new StartNumberingValue() { Val = 1 },
};
var paraProps = new PreviousParagraphProperties(new Indentation()
{
Left = (config.IndentPerLevel * (m_level + 1)).ToString(),
Hanging = m_level % 2 == 0 ? "360" : "180"
});
level.Append(paraProps);
if (!string.IsNullOrEmpty(config.FontOverride))
{
level.Append(new NumberingSymbolRunProperties(new RunFonts() { Ascii = config.FontOverride, HighAnsi = config.FontOverride }));

}
abstractNumberDefinition.Append(level);
m_numberingDefinitionsPart.Numbering.Save();
}
}


private void EndListLevel()
{
m_level--;
m_levelWithSameOrdering--;
}

/// <summary>
/// Crates a AbstractNum format for multiple levels of lists
/// </summary>
/// <returns></returns>
private void CrateNumberingInstance(bool ordered)
{
// Ensure the main document part has a NumberingDefinitionsPart
if (m_mainDocumentPart.NumberingDefinitionsPart == null)
{
m_numberingDefinitionsPart = m_mainDocumentPart.AddNewPart<NumberingDefinitionsPart>();
m_numberingDefinitionsPart.Numbering = new Numbering();
}
else
{
m_numberingDefinitionsPart = m_mainDocumentPart.NumberingDefinitionsPart;
}

var abstractNum = GetAbstractNumberingDefinition(ordered);

if (abstractNum == null)
{
abstractNum = m_numberingDefinitionsPart.Numbering.CreateNewAbstractNumbering();
var numberIngInstance = m_numberingDefinitionsPart.Numbering.CreateNewNumberingInstance(abstractNum.AbstractNumberId);
if (!ordered)
{
m_currentAbstractNumNotOrdered = abstractNum;
m_currentNumberingInstanceNotOrdered = numberIngInstance;
}
else
{
m_currentAbstractNumOrdered = abstractNum;
m_currentNumberingInstanceOrdered = numberIngInstance;
}
m_numberingDefinitionsPart.Numbering.Save();
}

}

private AbstractNum GetAbstractNumberingDefinition(bool ordered)
{
var abstractNum = ordered ? m_currentAbstractNumOrdered : m_currentAbstractNumNotOrdered;
return abstractNum;
}
}
}
Loading

0 comments on commit 14b54f1

Please sign in to comment.