Domanda

I am trying to replace certain places in my word document, which acts as a template. These places are marked as words starting with @. For example @Name. I select all the texts, run through them with my function and replace. All the logic behind that is working fine. However, for a reason unknown to me, texts, which are defined simply like this:

List<Text> texts = w.MainDocumentPart.Document.Descendants<Text>().ToList();

are not how I expect them to be. For example (a short list of the texts, where one text is in each line):

Document date: 
@
date
, written by: 
@Name 
@
Surname

even though it should have been in one line as this: Document date: @date, written by: @Name @Surname. As you can see, in cases like @ and date OR @ and Surname, I am unable to use my replacement method properly, because there is no @date nor @Surname actively in place. So I improved my code, but now I see many flaws in that, so it connects left-out @'s to the next text. Unfortunately, I came to one text in a table which totally killed all my algorithm. It was like this:

(@
sum_
words)

even though I didn't specially format it with different styles. So as you can see, it had to be (@sum_words), which, in this case, my algorithm would have easily replaced. Unfortunately, I am unable to. Thus, my questions would be:

  1. Why is it separating into parts even though I didn't make the parts of the word any different?
  2. Is there a way to solve this, so my texts can be intact?

UPDATE

Best thing I was able to do so far was:

for (int i = texts.Count - 1; i > 0; i--)
{
    if (texts[i - 1].Text.EndsWith("@") || texts[i - 1].Text.EndsWith("_"))
    {
        texts[i - 1].Text = texts[i - 1].Text + texts[i].Text;
        texts[i].Text = "";
    }
}

which connects the texts if they get split halfway (from what I've seen they only get split on the characters which are specific to my needs which are @ and _. The loop is reversed because it makes more sense to save the properties of the first text part in the group rather than the last (for example preserve spacing before elements).

UPDATE 2

Here is the requested table cell:

<w:tc>
    <w:tcPr>
        <w:tcW w:w="2410" w:type="dxa"/>
        <w:tcBorders>
            <w:top w:val="single" w:sz="4" w:space="0" w:color="auto"/>
            <w:left w:val="single" w:sz="4" w:space="0" w:color="auto"/>
            <w:bottom w:val="single" w:sz="4" w:space="0" w:color="auto"/>
            <w:right w:val="single" w:sz="4" w:space="0" w:color="auto"/>
        </w:tcBorders>
        <w:shd w:val="clear" w:color="auto" w:fill="auto"/>
    </w:tcPr>
    <w:p w:rsidR="009D473B" w:rsidRDefault="00385754" w:rsidP="00385754">
        <w:pPr>
            <w:jc w:val="center"/>
            <w:rPr>
                <w:lang w:val="lt-LT"/>
            </w:rPr>
        </w:pPr>
        <w:r>
            <w:rPr>
                <w:lang w:val="lt-LT"/>
            </w:rPr>
            <w:t>@sum</w:t>
        </w:r>
        <w:r w:rsidR="009D473B">
            <w:rPr>
                <w:lang w:val="lt-LT"/>
            </w:rPr>
            <w:t xml:space="preserve">(@</w:t>
        </w:r>
        <w:r>
            <w:rPr>
                <w:lang w:val="lt-LT"/>
            </w:rPr>
            <w:t>sum_</w:t>
        </w:r>
        <w:r w:rsidR="009D473B">
            <w:rPr>
                <w:lang w:val="lt-LT"/>
            </w:rPr>
            <w:t>words)</w:t>
        </w:r>
    </w:p>
</w:tc>
È stato utile?

Soluzione

The only problems I had doing replacements of variables in WordML documents where caused by the spelling and grammar module which tend to insert special tags which breaks your variable names which is solved by deactivating completely checking and marking of grammar errors in Word during template editing.

My method of replacing is something like this (my convention for naming variables is a little different, this one is addapted to yours):

public static void Assign(XElement xe, string name, string value)
{
    XElement el = xe.Descendants()
        .FirstOrDefault(e => e.Name.LocalName == "t" && e.Value.Contains("(@" + name + ")"));
    if (el != null)
    {
        el.Value = el.Value.Replace("(@" + name + ")", value);
    }
    else
    {
        AssignFallback(xe, name, value);
    }   
}

EDIT 2

I wrote a fallback method of replacing the variable in case the above don't work (case el == null) above. A paragraph w:p can contain multiple runs and our variable name could be breaked across a continuous serie of runs. So we want to identify those and replace the value only in first, removing the rest. We have to be carefull to preserve the text wich could appear before de variable name and after (prefix will be in the first run and sufix in the last one).

public static void AssignFallback(XElement xe, string name, string value)
{
    string varName = "(@" + name + ")";
    XElement xep = xe.Descendants()
            .FirstOrDefault(x => x.Name.LocalName == "p" && x.Value.Contains(varName));
    if (xep == null)
    {
        return;
    }
    string prefix = "", sufix = "";
    List<XElement> truns = new List<XElement>();
    List<XElement> allruns = xep.Descendants().Where(x => x.Name.LocalName == "r").ToList();
    for (int i = 0; i < allruns.Count; i++)
    {
        if (!allruns[i].Value.Contains("(@"))
        {
            continue;
        }
        int index = allruns[i].Value.IndexOf("(@");
        prefix = allruns[i].Value.Substring(0, index);
        truns.Clear();
        truns.Add(allruns[i]);
        string nameTemp = allruns[i].Value.Substring(index, allruns[i].Value.Length - index);
        if (!varName.StartsWith(nameTemp))
        {
            continue;
        }
        for (int j = i + 1; j < allruns.Count; j++)
        {
            nameTemp += allruns[j].Value;
            truns.Add(allruns[j]);
            if (nameTemp.StartsWith(varName))
            {
                sufix = nameTemp.Substring(varName.Length);
                break;
            }
            else if (nameTemp.Length > varName.Length)
            {
                break;
            }
        }
        if (nameTemp.StartsWith(varName))
        {
            XElement xet = truns[0].Descendants().FirstOrDefault(x => x.Name.LocalName == "t");
            xet.Value = prefix + value + sufix;
            for (int j = 1; j < truns.Count; j++)
            {
                truns[j].Remove();
            }
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top