Zastąp tekst zakładek w pliku Word za pomocą Open XML SDK
Zakładam, że v2. 0 jest lepszy... mają fajne " jak:..."przykłady {[2] } ale zakładki nie wydają się działać tak wyraźnie, jak mówią tabela... zakładka jest zdefiniowana przez dwa elementy XML BookmarkStart & BookmarkEnd . Mamy kilka szablonów z tekstem w zakładkach i po prostu chcemy zastąpić zakładki innym tekstem... nie ma dziwnego formatowania, ale jak wybrać / zastąpić tekst zakładek?
11 answers
Oto moje podejście po użyciu was jako inspiracji:
IDictionary<String, BookmarkStart> bookmarkMap =
new Dictionary<String, BookmarkStart>();
foreach (BookmarkStart bookmarkStart in file.MainDocumentPart.RootElement.Descendants<BookmarkStart>())
{
bookmarkMap[bookmarkStart.Name] = bookmarkStart;
}
foreach (BookmarkStart bookmarkStart in bookmarkMap.Values)
{
Run bookmarkText = bookmarkStart.NextSibling<Run>();
if (bookmarkText != null)
{
bookmarkText.GetFirstChild<Text>().Text = "blah";
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-23 13:08:13
Zastąp zakładki jedną treścią (ewentualnie wieloma blokami tekstu).
public static void InsertIntoBookmark(BookmarkStart bookmarkStart, string text)
{
OpenXmlElement elem = bookmarkStart.NextSibling();
while (elem != null && !(elem is BookmarkEnd))
{
OpenXmlElement nextElem = elem.NextSibling();
elem.Remove();
elem = nextElem;
}
bookmarkStart.Parent.InsertAfter<Run>(new Run(new Text(text)), bookmarkStart);
}
Po pierwsze, istniejąca zawartość między początkiem i końcem jest usuwana. Następnie nowy bieg jest dodawany bezpośrednio za startem(przed końcem).
Nie wiadomo jednak, czy zakładka jest zamknięta w innej sekcji, gdy została otwarta, czy w innych komórkach tabeli, itp. ..
Dla mnie to na razie wystarczy.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-12 12:02:32
Rozgryzłem to 10 minut temu, więc wybacz hakerski charakter kodu.
Najpierw napisałem Helper recursive helper function, aby znaleźć wszystkie zakładki:
private static Dictionary<string, BookmarkEnd> FindBookmarks(OpenXmlElement documentPart, Dictionary<string, BookmarkEnd> results = null, Dictionary<string, string> unmatched = null )
{
results = results ?? new Dictionary<string, BookmarkEnd>();
unmatched = unmatched ?? new Dictionary<string,string>();
foreach (var child in documentPart.Elements())
{
if (child is BookmarkStart)
{
var bStart = child as BookmarkStart;
unmatched.Add(bStart.Id, bStart.Name);
}
if (child is BookmarkEnd)
{
var bEnd = child as BookmarkEnd;
foreach (var orphanName in unmatched)
{
if (bEnd.Id == orphanName.Key)
results.Add(orphanName.Value, bEnd);
}
}
FindBookmarks(child, results, unmatched);
}
return results;
}
To zwraca mi Słownik, którego mogę użyć, aby podzielić się przez moją listę zastępczą i dodać tekst po zakładce:
var bookMarks = FindBookmarks(doc.MainDocumentPart.Document);
foreach( var end in bookMarks )
{
var textElement = new Text("asdfasdf");
var runElement = new Run(textElement);
end.Value.InsertAfterSelf(runElement);
}
Z tego co wiem wkładanie i wymiana zakładek wygląda trudniej. Kiedy używałem InsertAt zamiast InsertIntoSelf otrzymałem: "elementy nie kompozytowe nie mieć elementy potomne."YMMV
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-24 15:00:45
Po wielu godzinach napisałem tą metodę:
Public static void ReplaceBookmarkParagraphs(WordprocessingDocument doc, string bookmark, string text)
{
//Find all Paragraph with 'BookmarkStart'
var t = (from el in doc.MainDocumentPart.RootElement.Descendants<BookmarkStart>()
where (el.Name == bookmark) &&
(el.NextSibling<Run>() != null)
select el).First();
//Take ID value
var val = t.Id.Value;
//Find the next sibling 'text'
OpenXmlElement next = t.NextSibling<Run>();
//Set text value
next.GetFirstChild<Text>().Text = text;
//Delete all bookmarkEnd node, until the same ID
deleteElement(next.GetFirstChild<Text>().Parent, next.GetFirstChild<Text>().NextSibling(), val, true);
}
Potem wołam:
Public static bool deleteElement(OpenXmlElement parentElement, OpenXmlElement elem, string id, bool seekParent)
{
bool found = false;
//Loop until I find BookmarkEnd or null element
while (!found && elem != null && (!(elem is BookmarkEnd) || (((BookmarkEnd)elem).Id.Value != id)))
{
if (elem.ChildElements != null && elem.ChildElements.Count > 0)
{
found = deleteElement(elem, elem.FirstChild, id, false);
}
if (!found)
{
OpenXmlElement nextElem = elem.NextSibling();
elem.Remove();
elem = nextElem;
}
}
if (!found)
{
if (elem == null)
{
if (!(parentElement is Body) && seekParent)
{
//Try to find bookmarkEnd in Sibling nodes
found = deleteElement(parentElement.Parent, parentElement.NextSibling(), id, true);
}
}
else
{
if (elem is BookmarkEnd && ((BookmarkEnd)elem).Id.Value == id)
{
found = true;
}
}
}
return found;
}
Ten kod działa dobrze, jeśli nie masz pustych zakładek. Mam nadzieję, że komuś pomoże.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-22 13:45:30
Większość rozwiązań zakłada regularny wzorzec zakładek rozpoczynający się przed i kończący po uruchomieniach, co nie zawsze jest prawdą, np. jeśli zakładka zaczyna się w para lub tabeli i kończy się gdzieś w innej para (jak inni zauważyli). Co powiesz na użycie kolejności dokumentów, aby poradzić sobie w przypadku, gdy zakładki nie są umieszczone w zwykłej strukturze - kolejność dokumentów nadal znajdzie wszystkie odpowiednie węzły tekstu, pomiędzy którymi można następnie zastąpić. Po prostu root.DescendantNodes ().Gdzie (xtext lub bookmarkstart lub koniec zakładki), które będą przesuwać się w kolejności dokumentu, a następnie można zastąpić węzły tekstowe, które pojawiają się po zobaczeniu węzła początkowego zakładki, ale przed zobaczeniem węzła końcowego.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-04 15:58:12
Oto Jak to zrobić i VB dodać / zastąpić tekst między bookmarkStart i BookmarkEnd.
<w:bookmarkStart w:name="forbund_kort" w:id="0" />
- <w:r>
<w:t>forbund_kort</w:t>
</w:r>
<w:bookmarkEnd w:id="0" />
Imports DocumentFormat.OpenXml.Packaging
Imports DocumentFormat.OpenXml.Wordprocessing
Public Class PPWordDocx
Public Sub ChangeBookmarks(ByVal path As String)
Try
Dim doc As WordprocessingDocument = WordprocessingDocument.Open(path, True)
'Read the entire document contents using the GetStream method:
Dim bookmarkMap As IDictionary(Of String, BookmarkStart) = New Dictionary(Of String, BookmarkStart)()
Dim bs As BookmarkStart
For Each bs In doc.MainDocumentPart.RootElement.Descendants(Of BookmarkStart)()
bookmarkMap(bs.Name) = bs
Next
For Each bs In bookmarkMap.Values
Dim bsText As DocumentFormat.OpenXml.OpenXmlElement = bs.NextSibling
If Not bsText Is Nothing Then
If TypeOf bsText Is BookmarkEnd Then
'Add Text element after start bookmark
bs.Parent.InsertAfter(New Run(New Text(bs.Name)), bs)
Else
'Change Bookmark Text
If TypeOf bsText Is Run Then
If bsText.GetFirstChild(Of Text)() Is Nothing Then
bsText.InsertAt(New Text(bs.Name), 0)
End If
bsText.GetFirstChild(Of Text)().Text = bs.Name
End If
End If
End If
Next
doc.MainDocumentPart.RootElement.Save()
doc.Close()
Catch ex As Exception
Throw ex
End Try
End Sub
End Class
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-18 15:18:35
Wziąłem kod z odpowiedzi i miałem z nim kilka problemów w wyjątkowych przypadkach:
- możesz zignorować Ukryte zakładki. Zakładki są ukryte, jeśli nazwa zaczyna się od _ (podkreślenia)
- Jeśli zakładka jest dla jeszcze jednej TableCell ' S, znajdziesz ją w BookmarkStart w pierwszej komórce wiersza z właściwością ColumnFirst odnoszącą się do indeksu kolumny opartej na 0 komórki, w której zaczyna się zakładka. ColumnLast odnosi się do komórki, w której końcówki zakładek, w moim szczególnym przypadku zawsze były to ColumnFirst = = ColumnLast (zakładki oznaczone tylko jedną kolumną). W tym przypadku również nie znajdziesz BookmarkEnd.
- Zakładki mogą być puste, więc BookmarkStart podąża bezpośrednio za BookmarkEnd, w tym przypadku możesz po prostu wywołać
bookmarkStart.Parent.InsertAfter(new Run(new Text("Hello World")), bookmarkStart)
- również zakładka może zawierać wiele elementów tekstowych, więc możesz chcieć usunąć wszystkie pozostałe elementy, w przeciwnym razie Części zakładki mogą zostać zastąpione, podczas gdy inne następujące części pozostaną.
- oraz Nie jestem pewien, czy mój ostatni hack jest konieczny, ponieważ nie znam wszystkich ograniczeń OpenXML, ale po odkryciu poprzedniego 4, również nie ufałem już, że będzie rodzeństwo Run, z dzieckiem tekstu. Zamiast tego patrzę na wszystkie moje rodzeństwo (aż do BookmarEnd, który ma ten sam identyfikator co BookmarkStart) i sprawdzam wszystkie dzieci, dopóki nie znajdę żadnego tekstu. - Może ktoś z większym doświadczeniem z OpenXML odpowie, czy jest to konieczne?
Możesz zobaczyć mój specyficzny implementacja tutaj )
Mam nadzieję, że pomoże to niektórym z Was, którzy doświadczyli tych samych problemów.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-26 16:17:20
Musiałem zastąpić tekst zakładki (nazwa zakładek to "tabela") tabelą. To jest moje podejście:
public void ReplaceBookmark( DatasetToTable( ds ) )
{
MainDocumentPart mainPart = myDoc.MainDocumentPart;
Body body = mainPart.Document.GetFirstChild<Body>();
var bookmark = body.Descendants<BookmarkStart>()
.Where( o => o.Name == "Table" )
.FirstOrDefault();
var parent = bookmark.Parent; //bookmark's parent element
if (ds!=null)
{
parent.InsertAfterSelf( DatasetToTable( ds ) );
parent.Remove();
}
mainPart.Document.Save();
}
public Table DatasetToTable( DataSet ds )
{
Table table = new Table();
//creating table;
return table;
}
Hope this helps
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-23 13:45:26
Oto Jak to robię w VB.NET:
For Each curBookMark In contractBookMarkStarts
''# Get the "Run" immediately following the bookmark and then
''# get the Run's "Text" field
runAfterBookmark = curBookMark.NextSibling(Of Wordprocessing.Run)()
textInRun = runAfterBookmark.LastChild
''# Decode the bookmark to a contract attribute
lines = DecodeContractDataToContractDocFields(curBookMark.Name, curContract).Split(vbCrLf)
''# If there are multiple lines returned then some work needs to be done to create
''# the necessary Run/Text fields to hold lines 2 thru n. If just one line then set the
''# Text field to the attribute from the contract
For ptr = 0 To lines.Count - 1
line = lines(ptr)
If ptr = 0 Then
textInRun.Text = line.Trim()
Else
''# Add a <br> run/text component then add next line
newRunForLf = New Run(runAfterBookmark.OuterXml)
newRunForLf.LastChild.Remove()
newBreak = New Break()
newRunForLf.Append(newBreak)
newRunForText = New Run(runAfterBookmark.OuterXml)
DirectCast(newRunForText.LastChild, Text).Text = line.Trim
curBookMark.Parent.Append(newRunForLf)
curBookMark.Parent.Append(newRunForText)
End If
Next
Next
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-22 17:23:25
Przyjęta odpowiedź i niektóre z pozostałych przyjmują założenia co do tego, gdzie zakładki znajdują się w strukturze dokumentu. Oto Mój kod C#, który może poradzić sobie z wymianą zakładek, które rozciągają się na wiele akapitów i poprawnie zastępują zakładki, które nie zaczynają się i nie kończą na granicach akapitu. Nadal nie idealnie, ale bliżej... mam nadzieję, że się przyda. Edytuj, jeśli znajdziesz więcej sposobów na jego ulepszenie!
private static void ReplaceBookmarkParagraphs(MainDocumentPart doc, string bookmark, IEnumerable<OpenXmlElement> paras) {
var start = doc.Document.Descendants<BookmarkStart>().Where(x => x.Name == bookmark).First();
var end = doc.Document.Descendants<BookmarkEnd>().Where(x => x.Id.Value == start.Id.Value).First();
OpenXmlElement current = start;
var done = false;
while ( !done && current != null ) {
OpenXmlElement next;
next = current.NextSibling();
if ( next == null ) {
var parentNext = current.Parent.NextSibling();
while ( !parentNext.HasChildren ) {
var toRemove = parentNext;
parentNext = parentNext.NextSibling();
toRemove.Remove();
}
next = current.Parent.NextSibling().FirstChild;
current.Parent.Remove();
}
if ( next is BookmarkEnd ) {
BookmarkEnd maybeEnd = (BookmarkEnd)next;
if ( maybeEnd.Id.Value == start.Id.Value ) {
done = true;
}
}
if ( current != start ) {
current.Remove();
}
current = next;
}
foreach ( var p in paras ) {
end.Parent.InsertBeforeSelf(p);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-25 12:26:02
Oto, z czym skończyłem-Nie w 100% idealny, ale działa na proste zakładki i prosty tekst do wstawienia:
private void FillBookmarksUsingOpenXml(string sourceDoc, string destDoc, Dictionary<string, string> bookmarkData)
{
string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
// Make a copy of the template file.
File.Copy(sourceDoc, destDoc, true);
//Open the document as an Open XML package and extract the main document part.
using (WordprocessingDocument wordPackage = WordprocessingDocument.Open(destDoc, true))
{
MainDocumentPart part = wordPackage.MainDocumentPart;
//Setup the namespace manager so you can perform XPath queries
//to search for bookmarks in the part.
NameTable nt = new NameTable();
XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
nsManager.AddNamespace("w", wordmlNamespace);
//Load the part's XML into an XmlDocument instance.
XmlDocument xmlDoc = new XmlDocument(nt);
xmlDoc.Load(part.GetStream());
//Iterate through the bookmarks.
foreach (KeyValuePair<string, string> bookmarkDataVal in bookmarkData)
{
var bookmarks = from bm in part.Document.Body.Descendants<BookmarkStart>()
select bm;
foreach (var bookmark in bookmarks)
{
if (bookmark.Name == bookmarkDataVal.Key)
{
Run bookmarkText = bookmark.NextSibling<Run>();
if (bookmarkText != null) // if the bookmark has text replace it
{
bookmarkText.GetFirstChild<Text>().Text = bookmarkDataVal.Value;
}
else // otherwise append new text immediately after it
{
var parent = bookmark.Parent; // bookmark's parent element
Text text = new Text(bookmarkDataVal.Value);
Run run = new Run(new RunProperties());
run.Append(text);
// insert after bookmark parent
parent.Append(run);
}
//bk.Remove(); // we don't want the bookmark anymore
}
}
}
//Write the changes back to the document part.
xmlDoc.Save(wordPackage.MainDocumentPart.GetStream(FileMode.Create));
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-05 20:33:01