2016-09-04 10 views
0

У меня есть файлы шаблонов ввода docx с одной страницей, в которых пользовательский дизайн использует определенные переменные, такие как contact_name. Во время обработки с использованием OpenXml SDK + Open-Xml-PowerTools я создаю много экземпляров файлов docx на основе этого шаблона и подставляю их в реальные значения для переменных. В конце мне нужен один вывод docx, поэтому я использую Open-Xml-PowerTools DocumentBuilder для объединения в один docx.Объединить файлы docx в один и сохранить форматирование пронумерованных списков

Это похоже на работу, пока пользователь не поместит нумерованный список в шаблон. Моя первоначальная проблема была пронумерована, списки продолжали нумерацию по экземплярам документов после слияния, то есть числа на второй странице в списке составляли 11-20 вместо 1-10, потому что документ думал, что все они относятся к одному и тому же идентификатору списка.

Мне удалось решить эту проблему, указав, что идентификаторы num уникальны в теле документа, но теперь форматирование списка теряется за пределами первой страницы, например. на первой странице элементы с пронумерованными списками имеют отступы, но во второй раз они сложны на странице, так как они не являются нумерованным списком. Похоже, мне нужно обновить разделы стилизации и нумерации, чтобы иметь соответствующие новые идентификаторы, но я не могу заставить это работать.

Я разместил об этом на форумах на ericwhite.com, но не слышал о последней проблеме (http://ericwhite.com/blog/forums/topic/list-numbering-on-merged-docs/).

Моя последняя попытка исправить это бросает исключение в OpenXml-Power-Tools, поэтому я думаю, что мне не хватает обновления некоторого раздела с новыми идентификаторами списка. Кто-нибудь знает как это сделать? Следующая попытка кода с последующим исключением.

public bool Merge(List<InterchangeableWordProcessingDocument> inputFiles, string outputFilePath) 
    { 
     if (inputFiles == null) 
     { 
      logger.LogDebug("No files to merge."); 
      return true; 
     } 
     try 
     { 

      List<OpenXmlPowerTools.Source> sources = new List<OpenXmlPowerTools.Source>(); 
      int highestListNumbering = 0; 
      int highestAbstractListNumbering = 0; 
      foreach (var inputFile in inputFiles) 
      { 
       //Sometimes merge puts start of next page onto end of previous one so prevent 
       //Seems to cause extra blank page when there are labels so don't do on labels pages 
       if (inputFile.DocType == DocType.Letter) 
       { 
        using (var wordDoc = inputFile.GetAsWordProcessingDocument()) 
        { 
         var para = wordDoc.MainDocumentPart.Document.Body.ChildElements.First<Paragraph>(); 

         if (para.ParagraphProperties == null) 
         { 
          para.ParagraphProperties = new ParagraphProperties(); 
         } 

         para.ParagraphProperties.PageBreakBefore = new PageBreakBefore(); 

         //http://ericwhite.com/blog/forums/topic/list-numbering-on-merged-docs/ 
         //Numberings should be unique to each page otherwise they continue from the previous 
         //Keep track of how many we have so we can add on to always have a unique number 
         var numIds = wordDoc.MainDocumentPart.Document.Body.Descendants<NumberingId>().ToList(); 

         logger.LogDebug("Found " + numIds.Count + " num ids."); 

         foreach (var numId in numIds) 
          numId.Val += highestListNumbering; 

         var styleNumIds = wordDoc.MainDocumentPart.StyleDefinitionsPart.RootElement.Descendants<NumberingId>().ToList(); 

         if (wordDoc.MainDocumentPart.StyleDefinitionsPart != null) 
         { 

          logger.LogDebug("Found " + styleNumIds.Count + " stlye num ids."); 
          foreach (var styleNumId in styleNumIds) 
           styleNumId.Val += highestListNumbering; 
         } 

         if (wordDoc.MainDocumentPart.NumberingDefinitionsPart != null) 
         { 

          var numberingNumIds = wordDoc.MainDocumentPart.NumberingDefinitionsPart.RootElement.Descendants<NumberingInstance>().ToList(); 

          logger.LogDebug("Found " + numberingNumIds.Count + " numbering num ids."); 
          foreach (var numberingNumId in numberingNumIds) 
          { 
           numberingNumId.NumberID += highestListNumbering; 
           numberingNumId.AbstractNumId.Val += highestAbstractListNumbering; 
          } 

          var abstractNumberingNumIds = wordDoc.MainDocumentPart.NumberingDefinitionsPart.RootElement.Descendants<AbstractNumId>().ToList(); 

          logger.LogDebug("Found " + abstractNumberingNumIds.Count + " abstract num ids." + wordDoc.MainDocumentPart.NumberingDefinitionsPart.RootElement.XName.LocalName); 
          foreach (var abstractNumberingNumId in abstractNumberingNumIds) 
           abstractNumberingNumId.Val += highestAbstractListNumbering; 

          //Keep the max nums up to date 
          if (abstractNumberingNumIds.Count > 0) 
           highestAbstractListNumbering = Math.Max(highestAbstractListNumbering, abstractNumberingNumIds.Max(ln => (ln.Val.HasValue ? ln.Val.Value : 0))); 

         } 


         if (numIds.Count > 0) 
          highestListNumbering = Math.Max(highestListNumbering, numIds.Max(ln => (ln.Val.HasValue ? ln.Val.Value : 0))); 



         wordDoc.MainDocumentPart.Document.Save(); 
        } 
       } 
       sources.Add(new OpenXmlPowerTools.Source(inputFile.GetAsWmlDocument(), true)); 

      } 
      DocumentBuilder.BuildDocument(sources, outputFilePath); 
      return true; 

     } 
     catch (SystemException ex) 
     { 
      logger.LogError("Error occured while generating bereavement letters. ", ex); 

      return false; 
     } 
     finally 
     { 
      foreach (var inputFile in inputFiles) 
      { 
       inputFile.Dispose(); 
      } 
     } 
    } 

Исключение:

System.InvalidOperationException: Sequence contains no elements 
at System.Linq.Enumerable.First[TSource](IEnumerable`1 source) 
at OpenXmlPowerTools.DocumentBuilder.CopyNumbering(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument, IEnumerable1 newContent, List1 images) 
at OpenXmlPowerTools.DocumentBuilder.AppendDocument(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument, List1 newContent, Boolean keepSection, String insertId, List1 images) 
at OpenXmlPowerTools.DocumentBuilder.BuildDocument(List`1 sources, WordprocessingDocument output) 
at OpenXmlPowerTools.DocumentBuilder.BuildDocument(List`1 sources, String fileName) 
at BereavementMailing.TemplateEngine.Merge(List`1 inputFiles, String outputFilePath) in C:\caw\Underdog\Apps\Services\BereavementMailingEngine\BM_RequestProcessor\TemplateEngine.cs:line 508 

ответ

1

Похоже, вы обновляете те же AbstractNumId эталонных значений в два раза в течение каждого прохода. Вместо этого вам нужно обновить значение идентификатора определения AbstractNum.

Эталонные значения в вашем NumberingPart XML выглядеть следующим образом:

<w:num w:numId="58"> 
     <w:abstractNumId w:val="2"/> 
    </w:num> 

и обновление тех дважды.

Определения abstractNumber выглядеть следующим образом:

<w:abstractNum w:abstractNumId="0" 
       w15:restartNumberingAfterBreak="0"> 
    <w:nsid w:val="FFFFFF88"/> 
    <w:multiLevelType w:val="singleLevel"/> 
    <w:tmpl w:val="8EE6963C"/> 
    <w:lvl w:ilvl="0"> 
     <w:start w:val="1"/> 
     <w:numFmt w:val="decimal"/> 
     <w:pStyle w:val="ListNumber"/> 
     <w:lvlText w:val="%1."/> 
     <w:lvlJc w:val="left"/> 
     <w:pPr> 
      <w:tabs> 
       <w:tab w:val="num" 
         w:pos="360"/> 
      </w:tabs> 
      <w:ind w:left="360" 
        w:hanging="360"/> 
     </w:pPr> 
    </w:lvl> 
</w:abstractNum> 

Попробуйте изменить этот раздел:

Оригинал

var abstractNumberingNumIds = wordDoc.MainDocumentPart.NumberingDefinitionsPart.RootElement.Descendants<AbstractNumId>().ToList(); 

logger.LogDebug("Found " + abstractNumberingNumIds.Count + " abstract num ids." + wordDoc.MainDocumentPart.NumberingDefinitionsPart.RootElement.XName.LocalName); 
foreach (var abstractNumberingNumId in abstractNumberingNumIds) 
abstractNumberingNumId.Val += highestAbstractListNumbering; 

//Keep the max nums up to date 
if (abstractNumberingNumIds.Count > 0) 
    highestAbstractListNumbering = Math.Max(highestAbstractListNumbering, abstractNumberingNumIds.Max(ln => (ln.Val.HasValue ? ln.Val.Value : 0))); 

Новый

var abstractNums = wordDoc.MainDocumentPart.NumberingDefinitionsPart.RootElement.Descendants<AbstractNum>().ToList(); 

logger.LogDebug("Found " + abstractNums.Count + " abstract nums." + wordDoc.MainDocumentPart.NumberingDefinitionsPart.RootElement.XName.LocalName); 
foreach (var abstractNum in abstractNums) 
    abstractNum.AbstractNumberId += highestAbstractListNumbering; 

//Keep the max nums up to date 
if (abstractNums.Count > 0) 
    highestAbstractListNumbering = Math.Max(highestAbstractListNumbering, abstractNums.Select(a => a.AbstractNumberId).Max(n => n.HasValue ? n.Value : 0));