Estoy generando un documento de Word reemplazando un texto de marcador de posición en el documento de plantilla con mis valores. Para esto, estoy usando GemBox.Document, más específicamente, este código del ejemplo Buscar y reemplazar :
var document = DocumentModel.Load("input.docx"); var firstPlaceholder = document.Content.Find("%Text1%").First(); firstPlaceholder.LoadText("Value 1"); var secondPlaceholder = document.Content.Find("%Text2%").First(); firstPlaceholder.LoadText("Value 2"); document.Save("output.docx");Eso funciona bien.
Pero ahora tengo un escenario en el que los valores que reemplazarán a los marcadores de posición dependen de su ubicación, más específicamente, si el marcador de posición aparece antes o después de algún párrafo específico en el documento.
Intenté usar algo como esto:
Paragraph separator = ... string firstPlaceholderText = "%Text1%"; string separatorText = seperator.Content.ToString(); string wholeDocumentText = document.Content.ToString(); if (wholeDocumentText.IndexOf(firstPlaceholderText) < wholeDocumentText.IndexOf(separatorText)) { // The placeholder is before the separator... } else { // The placeholder is after the separator... } Sin embargo, ese mismo valor de separatorText puede aparecer en varios lugares del documento, por lo que string.IndexOf() no es una solución viable para mí.
¿Hay alguna otra manera de hacer esta comparación u otra forma de determinar la ubicación de algún marcador de posición en comparación con algún otro elemento del documento?
Prueba esto:
static bool IsPositionBefore(ContentPosition position1, ContentPosition position2) { var parentIndexes1 = GetParentIndexes(position1.Parent); var parentIndexes2 = GetParentIndexes(position2.Parent); int count = Math.Min(parentIndexes1.Count, parentIndexes2.Count); for (int i = 0; i < count; i++) { if (parentIndexes1[i] < parentIndexes2[i]) return true; if (parentIndexes1[i] > parentIndexes2[i]) return false; } // Both positions are inside the same parent element. var parent = position1.Parent; var parentClone = parent.Clone(true); string positionMarker1 = "\u0001"; string positionMarker2 = "\u0002"; position1.LoadText(positionMarker1); position2.LoadText(positionMarker2); string parentContent = parent.Content.ToString(); int positionOffset1 = parentContent.IndexOf(positionMarker1, StringComparison.Ordinal); int positionOffset2 = parentContent.IndexOf(positionMarker2, StringComparison.Ordinal); parent.Content.Set(parentClone.Content); return positionOffset1 < positionOffset2; } static IList<int> GetParentIndexes(Element element) { var parentIndexes = new List<int>(); while (element.Parent != null) { parentIndexes.Add(element.ParentCollection.IndexOf(element)); element = element.Parent; } parentIndexes.Reverse(); return parentIndexes; } Además, así es como puede usar este método IsPositionBefore :
if (IsPositionBefore(firstPlaceholder.Start, separator.Content.Start)) { // The placeholder is before the separator... } else { // The placeholder is after the separator... }La parte complicada es cómo determinar qué posición viene primero cuando ambas posiciones están dentro del mismo elemento.
Esto se debe a que ContentPosition actualmente no tiene algún tipo de API compensada que le diga dónde se encuentra exactamente dentro del elemento.
Entonces, lo que estoy haciendo es agregar temporalmente dos caracteres de control aleatorios, verificar cuál ocurre antes que el otro y luego eliminarlos.
Creo que este enfoque es seguro porque los documentos de Word no pueden tener caracteres de control (las aplicaciones de Word los mostrarán como dañados) y si intenta guardar un modelo de DocumentModel que tiene dichos caracteres, obtendrá una excepción.